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
= g_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
);
5280 gchar
*body
= (gchar
*) g_base64_decode(tmp
, &len
);
5282 GSList
*parsed_body
= sipe_ft_parse_msg_body(body
);
5284 sipe_process_incoming_x_msmsgsinvite(sip
, msg
, parsed_body
);
5285 sipe_utils_nameval_free(parsed_body
);
5286 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5290 else if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html"))
5292 /* please do not optimize logic inside as this code may be re-enabled for other cases */
5293 gchar
*html
= get_html_message(ms_text_format
, NULL
);
5295 if (is_multiparty
) {
5296 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5297 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5299 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5302 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5310 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
5311 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5312 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5314 body
= g_strdup_printf(
5316 "o=- 0 0 IN IP4 %s\r\n"
5320 "m=%s %d sip sip:%s\r\n"
5321 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5322 purple_network_get_my_ip(-1),
5323 purple_network_get_my_ip(-1),
5324 sip
->ocs2007
? "message" : "x-ms-message",
5327 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5331 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5335 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
5336 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5337 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5339 body
= g_strdup_printf(
5341 "o=- 0 0 IN IP4 0.0.0.0\r\n"
5343 "c=IN IP4 0.0.0.0\r\n"
5345 "m=%s %d sip sip:%s\r\n"
5346 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5347 sip
->ocs2007
? "message" : "x-ms-message",
5350 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5355 sipe_get_auth_scheme_name(struct sipe_account_data
*sip
)
5357 const char *res
= "NTLM";
5358 #ifdef HAVE_KERBEROS
5359 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5363 (void) sip
; /* make compiler happy */
5368 static void sipe_connection_cleanup(struct sipe_account_data
*);
5369 static void create_connection(struct sipe_account_data
*, gchar
*, int);
5371 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5372 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5375 const gchar
*expires_header
;
5377 GSList
*hdr
= msg
->headers
;
5378 struct sipnameval
*elem
;
5380 expires_header
= sipmsg_find_header(msg
, "Expires");
5381 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
5382 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
5384 switch (msg
->response
) {
5387 sip
->registerstatus
= 0;
5389 const gchar
*contact_hdr
;
5394 const gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
5395 const char *auth_scheme
;
5397 if (!sip
->reregister_set
) {
5398 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
5399 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
5400 g_free(action_name
);
5401 sip
->reregister_set
= TRUE
;
5404 sip
->registerstatus
= 3;
5406 if (server_hdr
&& !sip
->server_version
) {
5407 sip
->server_version
= g_strdup(server_hdr
);
5412 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5413 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5416 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5417 fill_auth(tmp
, &sip
->registrar
);
5420 if (!sip
->reauthenticate_set
) {
5421 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
5422 guint reauth_timeout
;
5423 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
5424 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
5425 reauth_timeout
= sip
->registrar
.expires
- 300;
5427 /* NTLM: we have to reauthenticate as our security token expires
5428 after eight hours (be five minutes early) */
5429 reauth_timeout
= (8 * 3600) - 300;
5431 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
5432 g_free(action_name
);
5433 sip
->reauthenticate_set
= TRUE
;
5436 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
5438 epid
= get_epid(sip
);
5439 uuid
= generateUUIDfromEPID(epid
);
5442 // There can be multiple Contact headers (one per location where the user is logged in) so
5443 // make sure to only get the one for this uuid
5444 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
5445 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
5446 if (valid_contact
) {
5447 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
5448 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
5449 g_free(valid_contact
);
5452 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
5457 g_free(sip
->contact
);
5459 sip
->contact
= g_strdup_printf("<%s>", gruu
);
5462 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
5463 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
);
5465 sip
->ocs2007
= FALSE
;
5466 sip
->batched_support
= FALSE
;
5471 if (sipe_strcase_equal(elem
->name
, "Supported")) {
5472 if (sipe_strcase_equal(elem
->value
, "msrtc-event-categories")) {
5473 /* We interpret this as OCS2007+ indicator */
5474 sip
->ocs2007
= TRUE
;
5475 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
5477 if (sipe_strcase_equal(elem
->value
, "adhoclist")) {
5478 sip
->batched_support
= TRUE
;
5479 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
5482 if (sipe_strcase_equal(elem
->name
, "Allow-Events")){
5483 gchar
**caps
= g_strsplit(elem
->value
,",",0);
5486 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
5487 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
5492 hdr
= g_slist_next(hdr
);
5495 /* rejoin open chats to be able to use them by continue to send messages */
5496 purple_conversation_foreach(sipe_rejoin_chat
);
5499 if (!sip
->subscribed
) { //do it just once, not every re-register
5501 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
5502 (GCompareFunc
)g_ascii_strcasecmp
)) {
5503 sipe_subscribe_roaming_contacts(sip
);
5506 /* For 2007+ it does not make sence to subscribe to:
5507 * vnd-microsoft-roaming-ACL
5508 * vnd-microsoft-provisioning (not v2)
5510 * These are for backward compatibility.
5514 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
5515 (GCompareFunc
)g_ascii_strcasecmp
)) {
5516 sipe_subscribe_roaming_self(sip
);
5518 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
5519 (GCompareFunc
)g_ascii_strcasecmp
)) {
5520 sipe_subscribe_roaming_provisioning_v2(sip
);
5523 /* For 2005- servers */
5526 //sipe_options_request(sip, sip->sipdomain);
5528 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
5529 (GCompareFunc
)g_ascii_strcasecmp
)) {
5530 sipe_subscribe_roaming_acl(sip
);
5532 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
5533 (GCompareFunc
)g_ascii_strcasecmp
)) {
5534 sipe_subscribe_roaming_provisioning(sip
);
5536 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
5537 (GCompareFunc
)g_ascii_strcasecmp
)) {
5538 sipe_subscribe_presence_wpending(sip
, msg
);
5541 /* For 2007+ we publish our initial statuses and calendar data only after
5542 * received our existing publications in sipe_process_roaming_self()
5543 * Only in this case we know versions of current publications made
5546 /* For 2005- we publish our initial statuses only after
5547 * received our existing UserInfo data in response to
5548 * self subscription.
5549 * Only in this case we won't override existing UserInfo data
5550 * set earlier or by other client on our behalf.
5554 sip
->subscribed
= TRUE
;
5557 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
5558 "timeout=", ";", NULL
);
5559 if (timeout
!= NULL
) {
5560 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
5561 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
5562 sip
->keepalive_timeout
);
5566 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
5571 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
5573 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
5574 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
5578 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
5581 tmp
= g_strsplit(parts
[0], ":", 0);
5582 hostname
= g_strdup(tmp
[0]);
5583 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
5587 tmp
= g_strsplit(parts
[i
], "=", 0);
5589 if (g_strcasecmp("transport", tmp
[0]) == 0) {
5590 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
5591 transport
= SIPE_TRANSPORT_TCP
;
5592 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
5593 transport
= SIPE_TRANSPORT_UDP
;
5602 /* Close old connection */
5603 sipe_connection_cleanup(sip
);
5605 /* Create new connection */
5606 sip
->transport
= transport
;
5607 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
5608 hostname
, port
, TRANSPORT_DESCRIPTOR
);
5609 create_connection(sip
, hostname
, port
);
5615 if (sip
->registerstatus
!= 2) {
5616 const char *auth_scheme
;
5617 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
5618 if (sip
->registrar
.retries
> 3) {
5619 sip
->gc
->wants_to_die
= TRUE
;
5620 purple_connection_error(sip
->gc
, _("Authentication failed"));
5624 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5625 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5627 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
? tmp
: "");
5629 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
5630 sip
->gc
->wants_to_die
= TRUE
;
5631 purple_connection_error(sip
->gc
, tmp2
);
5635 fill_auth(tmp
, &sip
->registrar
);
5636 sip
->registerstatus
= 2;
5637 if (sip
->account
->disconnecting
) {
5638 do_register_exp(sip
, 0);
5646 const gchar
*diagnostics
= sipmsg_find_header(msg
, "Warning");
5647 gchar
**reason
= NULL
;
5649 if (diagnostics
!= NULL
) {
5651 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
5653 reason
= g_strsplit(diagnostics
, "\"", 0);
5655 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
5656 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
5659 sip
->gc
->wants_to_die
= TRUE
;
5660 purple_connection_error(sip
->gc
, warning
);
5667 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5668 gchar
*reason
= NULL
;
5670 if (diagnostics
!= NULL
) {
5671 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5673 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
5674 diagnostics
? (reason
? reason
: _("no reason given")) :
5675 _("SIP is either not enabled for the destination URI or it does not exist"));
5678 sip
->gc
->wants_to_die
= TRUE
;
5679 purple_connection_error(sip
->gc
, warning
);
5685 case 504: /* Server time-out */
5687 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5688 gchar
*reason
= NULL
;
5690 if (diagnostics
!= NULL
) {
5691 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5693 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
5696 sip
->gc
->wants_to_die
= TRUE
;
5697 purple_connection_error(sip
->gc
, warning
);
5707 * Returns 2005-style activity and Availability.
5709 * @param status Sipe statis id.
5712 sipe_get_act_avail_by_status_2005(const char *status
,
5716 int avail
= 300; /* online */
5717 int act
= 400; /* Available */
5719 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
5721 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
5723 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
5725 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
5727 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
5729 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
5730 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
5732 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
5733 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
5734 avail
= 0; /* offline */
5737 act
= 400; /* Available */
5740 if (activity
) *activity
= act
;
5741 if (availability
) *availability
= avail
;
5747 * @param activity 2005 aggregated activity. Ex.: 600
5748 * @param availablity 2005 aggregated availablity. Ex.: 300
5751 sipe_get_status_by_act_avail_2005(const int activity
,
5752 const int availablity
,
5753 char **activity_desc
)
5755 const char *status_id
= NULL
;
5756 const char *act
= NULL
;
5758 if (activity
< 150) {
5759 status_id
= SIPE_STATUS_ID_AWAY
;
5760 } else if (activity
< 200) {
5761 //status_id = SIPE_STATUS_ID_LUNCH;
5762 status_id
= SIPE_STATUS_ID_AWAY
;
5763 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
5764 } else if (activity
< 300) {
5765 //status_id = SIPE_STATUS_ID_IDLE;
5766 status_id
= SIPE_STATUS_ID_AWAY
;
5767 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5768 } else if (activity
< 400) {
5769 status_id
= SIPE_STATUS_ID_BRB
;
5770 } else if (activity
< 500) {
5771 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5772 } else if (activity
< 600) {
5773 //status_id = SIPE_STATUS_ID_ON_PHONE;
5774 status_id
= SIPE_STATUS_ID_BUSY
;
5775 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
5776 } else if (activity
< 700) {
5777 status_id
= SIPE_STATUS_ID_BUSY
;
5778 } else if (activity
< 800) {
5779 status_id
= SIPE_STATUS_ID_AWAY
;
5781 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5784 if (availablity
< 100)
5785 status_id
= SIPE_STATUS_ID_OFFLINE
;
5787 if (activity_desc
&& act
) {
5788 g_free(*activity_desc
);
5789 *activity_desc
= g_strdup(act
);
5796 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
5799 sipe_get_status_by_availability(int avail
,
5800 char** activity_desc
)
5803 const char *act
= NULL
;
5806 status
= SIPE_STATUS_ID_OFFLINE
;
5807 } else if (avail
< 4500) {
5808 status
= SIPE_STATUS_ID_AVAILABLE
;
5809 } else if (avail
< 6000) {
5810 //status = SIPE_STATUS_ID_IDLE;
5811 status
= SIPE_STATUS_ID_AVAILABLE
;
5812 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5813 } else if (avail
< 7500) {
5814 status
= SIPE_STATUS_ID_BUSY
;
5815 } else if (avail
< 9000) {
5816 //status = SIPE_STATUS_ID_BUSYIDLE;
5817 status
= SIPE_STATUS_ID_BUSY
;
5818 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
5819 } else if (avail
< 12000) {
5820 status
= SIPE_STATUS_ID_DND
;
5821 } else if (avail
< 15000) {
5822 status
= SIPE_STATUS_ID_BRB
;
5823 } else if (avail
< 18000) {
5824 status
= SIPE_STATUS_ID_AWAY
;
5826 status
= SIPE_STATUS_ID_OFFLINE
;
5829 if (activity_desc
&& act
) {
5830 g_free(*activity_desc
);
5831 *activity_desc
= g_strdup(act
);
5838 * Returns 2007-style availability value
5840 * @param sipe_status_id (in)
5841 * @param activity_token (out) Must be g_free()'d after use if consumed.
5844 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
5847 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
5849 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
5850 availability
= 15500;
5851 if (!activity_token
|| !(*activity_token
)) {
5852 activity
= SIPE_ACTIVITY_AWAY
;
5854 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
5855 availability
= 12500;
5856 activity
= SIPE_ACTIVITY_BRB
;
5857 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
5858 availability
= 9500;
5859 activity
= SIPE_ACTIVITY_DND
;
5860 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
5861 availability
= 6500;
5862 if (!activity_token
|| !(*activity_token
)) {
5863 activity
= SIPE_ACTIVITY_BUSY
;
5865 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
5866 availability
= 3500;
5867 activity
= SIPE_ACTIVITY_ONLINE
;
5868 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
5871 // Offline or invisible
5872 availability
= 18500;
5873 activity
= SIPE_ACTIVITY_OFFLINE
;
5876 if (activity_token
) {
5877 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
5879 return availability
;
5882 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5885 sipe_xml
*xn_categories
;
5886 const sipe_xml
*xn_category
;
5887 const char *status
= NULL
;
5888 gboolean do_update_status
= FALSE
;
5889 gboolean has_note_cleaned
= FALSE
;
5890 gboolean has_free_busy_cleaned
= FALSE
;
5892 xn_categories
= sipe_xml_parse(data
, len
);
5893 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
5895 for (xn_category
= sipe_xml_child(xn_categories
, "category");
5897 xn_category
= sipe_xml_twin(xn_category
) )
5899 const sipe_xml
*xn_node
;
5901 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
5902 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
5903 sipe_utils_str_to_time(tmp
) : 0;
5906 if (sipe_strequal(attrVar
, "contactCard"))
5908 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
5911 const sipe_xml
*node
;
5912 /* identity - Display Name and email */
5913 node
= sipe_xml_child(card
, "identity");
5915 char* display_name
= sipe_xml_data(
5916 sipe_xml_child(node
, "name/displayName"));
5917 char* email
= sipe_xml_data(
5918 sipe_xml_child(node
, "email"));
5920 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5921 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5923 g_free(display_name
);
5927 node
= sipe_xml_child(card
, "company");
5929 char* company
= sipe_xml_data(node
);
5930 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
5934 node
= sipe_xml_child(card
, "department");
5936 char* department
= sipe_xml_data(node
);
5937 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
5941 node
= sipe_xml_child(card
, "title");
5943 char* title
= sipe_xml_data(node
);
5944 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
5948 node
= sipe_xml_child(card
, "office");
5950 char* office
= sipe_xml_data(node
);
5951 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
5955 node
= sipe_xml_child(card
, "url");
5957 char* site
= sipe_xml_data(node
);
5958 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
5962 for (node
= sipe_xml_child(card
, "phone");
5964 node
= sipe_xml_twin(node
))
5966 const char *phone_type
= sipe_xml_attribute(node
, "type");
5967 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
5968 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
5970 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
5973 g_free(phone_display_string
);
5976 for (node
= sipe_xml_child(card
, "address");
5978 node
= sipe_xml_twin(node
))
5980 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
5981 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
5982 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
5983 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
5984 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
5985 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
5987 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
5988 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
5989 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
5990 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
5991 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
5997 g_free(country_code
);
6005 else if (sipe_strequal(attrVar
, "note"))
6008 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6010 if (!has_note_cleaned
) {
6011 has_note_cleaned
= TRUE
;
6013 g_free(sbuddy
->note
);
6014 sbuddy
->note
= NULL
;
6015 sbuddy
->is_oof_note
= FALSE
;
6016 sbuddy
->note_since
= publish_time
;
6018 do_update_status
= TRUE
;
6020 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
6021 /* clean up in case no 'note' element is supplied
6022 * which indicate note removal in client
6024 g_free(sbuddy
->note
);
6025 sbuddy
->note
= NULL
;
6026 sbuddy
->is_oof_note
= FALSE
;
6027 sbuddy
->note_since
= publish_time
;
6029 xn_node
= sipe_xml_child(xn_category
, "note/body");
6032 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
6034 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
6035 sbuddy
->note_since
= publish_time
;
6037 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s), note(%s)\n",
6038 uri
, sbuddy
->note
? sbuddy
->note
: "");
6040 /* to trigger UI refresh in case no status info is supplied in this update */
6041 do_update_status
= TRUE
;
6046 else if(sipe_strequal(attrVar
, "state"))
6050 const sipe_xml
*xn_availability
;
6051 const sipe_xml
*xn_activity
;
6052 const sipe_xml
*xn_meeting_subject
;
6053 const sipe_xml
*xn_meeting_location
;
6054 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6056 xn_node
= sipe_xml_child(xn_category
, "state");
6057 if (!xn_node
) continue;
6058 xn_availability
= sipe_xml_child(xn_node
, "availability");
6059 if (!xn_availability
) continue;
6060 xn_activity
= sipe_xml_child(xn_node
, "activity");
6061 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
6062 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
6064 tmp
= sipe_xml_data(xn_availability
);
6065 availability
= atoi(tmp
);
6068 /* activity, meeting_subject, meeting_location */
6073 g_free(sbuddy
->activity
);
6074 sbuddy
->activity
= NULL
;
6076 const char *token
= sipe_xml_attribute(xn_activity
, "token");
6077 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
6080 if (!is_empty(token
)) {
6081 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
6083 /* from custom element */
6085 char *custom
= sipe_xml_data(xn_custom
);
6087 if (!is_empty(custom
)) {
6088 sbuddy
->activity
= custom
;
6094 /* meeting_subject */
6095 g_free(sbuddy
->meeting_subject
);
6096 sbuddy
->meeting_subject
= NULL
;
6097 if (xn_meeting_subject
) {
6098 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
6100 if (!is_empty(meeting_subject
)) {
6101 sbuddy
->meeting_subject
= meeting_subject
;
6102 meeting_subject
= NULL
;
6104 g_free(meeting_subject
);
6106 /* meeting_location */
6107 g_free(sbuddy
->meeting_location
);
6108 sbuddy
->meeting_location
= NULL
;
6109 if (xn_meeting_location
) {
6110 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
6112 if (!is_empty(meeting_location
)) {
6113 sbuddy
->meeting_location
= meeting_location
;
6114 meeting_location
= NULL
;
6116 g_free(meeting_location
);
6119 status
= sipe_get_status_by_availability(availability
, &tmp
);
6120 if (sbuddy
->activity
&& tmp
) {
6121 char *tmp2
= sbuddy
->activity
;
6123 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
6127 sbuddy
->activity
= tmp
;
6131 do_update_status
= TRUE
;
6134 else if(sipe_strequal(attrVar
, "calendarData"))
6136 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6137 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
6138 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
6140 if (sbuddy
&& xn_free_busy
) {
6141 if (!has_free_busy_cleaned
) {
6142 has_free_busy_cleaned
= TRUE
;
6144 g_free(sbuddy
->cal_start_time
);
6145 sbuddy
->cal_start_time
= NULL
;
6147 g_free(sbuddy
->cal_free_busy_base64
);
6148 sbuddy
->cal_free_busy_base64
= NULL
;
6150 g_free(sbuddy
->cal_free_busy
);
6151 sbuddy
->cal_free_busy
= NULL
;
6153 sbuddy
->cal_free_busy_published
= publish_time
;
6156 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
6157 g_free(sbuddy
->cal_start_time
);
6158 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
6160 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
6163 g_free(sbuddy
->cal_free_busy_base64
);
6164 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
6166 g_free(sbuddy
->cal_free_busy
);
6167 sbuddy
->cal_free_busy
= NULL
;
6169 sbuddy
->cal_free_busy_published
= publish_time
;
6171 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
);
6175 if (sbuddy
&& xn_working_hours
) {
6176 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
6181 if (do_update_status
) {
6182 if (!status
) { /* no status category in this update, using contact's current status */
6183 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6184 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
6185 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
6186 status
= purple_status_get_id(pstatus
);
6189 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
6190 sipe_got_user_status(sip
, uri
, status
);
6193 sipe_xml_free(xn_categories
);
6196 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
6198 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6199 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
6200 payload
->host
= g_strdup(host
);
6201 payload
->buddies
= server
;
6202 sipe_subscribe_presence_batched_routed(sip
, payload
);
6203 sipe_subscribe_presence_batched_routed_free(payload
);
6206 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6209 xmlnode
*xn_resource
;
6210 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
6215 xn_list
= xmlnode_from_str(data
, len
);
6217 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
6219 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
6221 const char *uri
, *state
;
6222 xmlnode
*xn_instance
;
6224 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
6225 if (!xn_instance
) continue;
6227 uri
= xmlnode_get_attrib(xn_resource
, "uri");
6228 state
= xmlnode_get_attrib(xn_instance
, "state");
6229 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
6231 if (strstr(state
, "resubscribe")) {
6232 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
6234 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
6235 gchar
*user
= g_strdup(uri
);
6236 host
= g_strdup(poolFqdn
);
6237 server
= g_hash_table_lookup(servers
, host
);
6238 server
= g_slist_append(server
, user
);
6239 g_hash_table_insert(servers
, host
, server
);
6241 sipe_subscribe_presence_single(sip
, (void *) uri
);
6246 /* Send out any deferred poolFqdn subscriptions */
6247 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
6248 g_hash_table_destroy(servers
);
6250 xmlnode_free(xn_list
);
6253 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6257 gchar
*activity
= NULL
;
6259 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
6260 gboolean isonline
= FALSE
;
6261 xmlnode
*display_name_node
;
6263 pidf
= xmlnode_from_str(data
, len
);
6265 purple_debug_info("sipe", "process_incoming_notify_pidf: no parseable pidf:%s\n",data
);
6269 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
6271 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6272 basicstatus
= xmlnode_get_child(status
, "basic");
6277 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic found\n");
6282 getbasic
= xmlnode_get_data(basicstatus
);
6284 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic data found\n");
6289 purple_debug_info("sipe", "process_incoming_notify_pidf: basic-status(%s)\n", getbasic
);
6290 if (strstr(getbasic
, "open")) {
6295 uri
= sip_uri(xmlnode_get_attrib(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
6297 display_name_node
= xmlnode_get_child(pidf
, "display-name");
6298 if (display_name_node
) {
6299 char * display_name
= xmlnode_get_data(display_name_node
);
6301 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6302 g_free(display_name
);
6305 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
6306 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6307 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
6308 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
6309 activity
= xmlnode_get_data(basicstatus
);
6310 purple_debug_info("sipe", "process_incoming_notify_pidf: activity(%s)\n", activity
);
6317 const gchar
* status_id
= NULL
;
6319 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
6320 status_id
= SIPE_STATUS_ID_BUSY
;
6321 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
6322 status_id
= SIPE_STATUS_ID_AWAY
;
6327 status_id
= SIPE_STATUS_ID_AVAILABLE
;
6330 purple_debug_info("sipe", "process_incoming_notify_pidf: status_id(%s)\n", status_id
);
6331 sipe_got_user_status(sip
, uri
, status_id
);
6333 sipe_got_user_status(sip
, uri
, SIPE_STATUS_ID_OFFLINE
);
6343 sipe_user_info_has_updated(struct sipe_account_data
*sip
,
6344 xmlnode
*xn_userinfo
)
6348 g_free(sip
->user_states
);
6349 sip
->user_states
= NULL
;
6350 if ((xn_states
= xmlnode_get_child(xn_userinfo
, "states")) != NULL
) {
6351 sip
->user_states
= xmlnode_to_str(xn_states
, NULL
);
6352 /* this is a hack-around to remove added newline after inner element,
6353 * state in this case, where it shouldn't be.
6354 * After several use of xmlnode_to_str, amount of added newlines
6355 * grows significantly.
6357 purple_str_strip_char(sip
->user_states
, '\n');
6358 //purple_str_strip_char(sip->user_states, '\r');
6361 /* Publish initial state if not yet.
6362 * Assuming this happens on initial responce to self subscription
6363 * so we've already updated our UserInfo.
6365 if (!sip
->initial_state_published
) {
6366 send_presence_soap(sip
, FALSE
);
6368 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
6372 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6374 char *activity
= NULL
;
6376 const char *status_id
= NULL
;
6379 char *self_uri
= sip_uri_self(sip
);
6382 const char *device_name
= NULL
;
6383 const char *cal_start_time
= NULL
;
6384 const char *cal_granularity
= NULL
;
6385 char *cal_free_busy_base64
= NULL
;
6386 struct sipe_buddy
*sbuddy
;
6388 xmlnode
*xn_presentity
;
6389 xmlnode
*xn_availability
;
6390 xmlnode
*xn_activity
;
6391 xmlnode
*xn_display_name
;
6393 xmlnode
*xn_phone_number
;
6394 xmlnode
*xn_userinfo
;
6398 xmlnode
*xn_contact
;
6400 char *free_activity
;
6402 const char *user_avail_nil
;
6404 time_t user_avail_since
= 0;
6405 time_t activity_since
= 0;
6407 /* fix for Reuters environment on Linux */
6408 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
6410 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
6411 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
6414 xn_presentity
= xmlnode_from_str(data
, len
);
6417 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
6418 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
6419 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
6420 xn_email
= xmlnode_get_child(xn_presentity
, "email");
6421 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
6422 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
6423 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
6424 xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
): NULL
;
6425 user_avail
= xn_state
? xmlnode_get_int_attrib(xn_state
, "avail", 0) : 0;
6426 user_avail_since
= xn_state
? sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since")) : 0;
6427 user_avail_nil
= xn_state
? xmlnode_get_attrib(xn_state
, "nil") : NULL
;
6428 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
6429 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
6430 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
6432 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
6434 user_avail_since
= 0;
6437 free_activity
= NULL
;
6439 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
6440 uri
= sip_uri_from_name(name
);
6441 avl
= xmlnode_get_int_attrib(xn_availability
, "aggregate", 0);
6442 epid
= xmlnode_get_attrib(xn_availability
, "epid");
6443 act
= xmlnode_get_int_attrib(xn_activity
, "aggregate", 0);
6445 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
6446 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
6447 if (user_avail
> res_avail
) {
6448 res_avail
= user_avail
;
6449 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
6452 if (xn_display_name
) {
6453 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
6454 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
6455 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
6456 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
6457 char *tel_uri
= sip_to_tel_uri(phone_number
);
6459 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6460 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
6461 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
6462 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
6465 g_free(phone_label
);
6466 g_free(phone_number
);
6468 g_free(display_name
);
6473 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
6475 /* Ex.: <tel type="work">tel:+3222220000</tel> */
6476 const char *phone_type
= xmlnode_get_attrib(node
, "type");
6477 char* phone
= xmlnode_get_data(node
);
6479 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
6485 /* devicePresence */
6486 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
6487 xmlnode
*xn_device_name
;
6488 xmlnode
*xn_calendar_info
;
6493 if (sipe_strequal(xmlnode_get_attrib(node
, "epid"), epid
)) {
6494 xn_device_name
= xmlnode_get_child(node
, "deviceName");
6495 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
6499 xn_calendar_info
= xmlnode_get_child(node
, "calendarInfo");
6500 if (xn_calendar_info
) {
6501 const char *cal_start_time_tmp
= xmlnode_get_attrib(xn_calendar_info
, "startTime");
6503 if (cal_start_time
) {
6504 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
6505 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
6507 if (cal_start_time_t_tmp
> cal_start_time_t
) {
6508 cal_start_time
= cal_start_time_tmp
;
6509 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6510 g_free(cal_free_busy_base64
);
6511 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6513 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
);
6516 cal_start_time
= cal_start_time_tmp
;
6517 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6518 g_free(cal_free_busy_base64
);
6519 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6521 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
);
6526 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
6528 int dev_avail
= xmlnode_get_int_attrib(xn_state
, "avail", 0);
6529 time_t dev_avail_since
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since"));
6531 state
= xmlnode_get_data(xn_state
);
6532 if (dev_avail_since
> user_avail_since
&&
6533 dev_avail
>= res_avail
)
6535 res_avail
= dev_avail
;
6536 if (!is_empty(state
))
6538 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
6540 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
6541 } else if (sipe_strequal(state
, "presenting")) {
6543 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
6548 activity_since
= dev_avail_since
;
6550 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
6557 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
6559 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
6563 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6566 g_free(sbuddy
->activity
);
6567 sbuddy
->activity
= activity
;
6570 sbuddy
->activity_since
= activity_since
;
6572 sbuddy
->user_avail
= user_avail
;
6573 sbuddy
->user_avail_since
= user_avail_since
;
6575 g_free(sbuddy
->note
);
6576 sbuddy
->note
= NULL
;
6577 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
6579 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
6581 g_free(sbuddy
->device_name
);
6582 sbuddy
->device_name
= NULL
;
6583 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
6585 if (!is_empty(cal_free_busy_base64
)) {
6586 g_free(sbuddy
->cal_start_time
);
6587 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
6589 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
6591 g_free(sbuddy
->cal_free_busy_base64
);
6592 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
6593 cal_free_busy_base64
= NULL
;
6595 g_free(sbuddy
->cal_free_busy
);
6596 sbuddy
->cal_free_busy
= NULL
;
6599 sbuddy
->last_non_cal_status_id
= status_id
;
6600 g_free(sbuddy
->last_non_cal_activity
);
6601 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
6603 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
6604 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
6606 sip
->is_oof_note
= sbuddy
->is_oof_note
;
6609 sip
->note
= g_strdup(sbuddy
->note
);
6611 sip
->note_since
= time(NULL
);
6614 g_free(sip
->status
);
6615 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
6618 g_free(cal_free_busy_base64
);
6621 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
6622 sipe_got_user_status(sip
, uri
, status_id
);
6624 if (!sip
->ocs2007
&& sipe_strcase_equal(self_uri
, uri
)) {
6625 sipe_user_info_has_updated(sip
, xn_userinfo
);
6629 xmlnode_free(xn_presentity
);
6634 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
6636 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6638 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
6640 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
6641 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
6643 const char *content
= msg
->body
;
6644 unsigned length
= msg
->bodylen
;
6645 PurpleMimeDocument
*mime
= NULL
;
6647 if (strstr(ctype
, "multipart"))
6649 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6650 const char *content_type
;
6652 mime
= purple_mime_document_parse(doc
);
6653 parts
= purple_mime_document_get_parts(mime
);
6655 content
= purple_mime_part_get_data(parts
->data
);
6656 length
= purple_mime_part_get_length(parts
->data
);
6657 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
6658 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
6660 process_incoming_notify_rlmi_resub(sip
, content
, length
);
6662 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
6664 process_incoming_notify_msrtc(sip
, content
, length
);
6668 process_incoming_notify_rlmi(sip
, content
, length
);
6670 parts
= parts
->next
;
6676 purple_mime_document_free(mime
);
6679 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
6681 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
6683 else if(strstr(ctype
, "application/rlmi+xml"))
6685 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
6688 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
6690 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
6694 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
6698 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
6700 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6701 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6703 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
6706 strstr(ctype
, "multipart") &&
6707 (strstr(ctype
, "application/rlmi+xml") ||
6708 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
6709 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6710 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
6711 GList
*parts
= purple_mime_document_get_parts(mime
);
6712 GSList
*buddies
= NULL
;
6713 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6716 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
6717 purple_mime_part_get_length(parts
->data
));
6719 if (xml
&& !sipe_strequal(xml
->name
, "list")) {
6720 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
6722 buddies
= g_slist_append(buddies
, uri
);
6726 parts
= parts
->next
;
6729 if (mime
) purple_mime_document_free(mime
);
6731 payload
->host
= g_strdup(who
);
6732 payload
->buddies
= buddies
;
6733 sipe_schedule_action(action_name
, timeout
,
6734 sipe_subscribe_presence_batched_routed
,
6735 sipe_subscribe_presence_batched_routed_free
,
6737 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
6740 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6741 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
6743 g_free(action_name
);
6747 * Dispatcher for all incoming subscription information
6748 * whether it comes from NOTIFY, BENOTIFY requests or
6749 * piggy-backed to subscription's OK responce.
6751 * @param request whether initiated from BE/NOTIFY request or OK-response message.
6752 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
6754 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
6756 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
6757 const gchar
*event
= sipmsg_find_header(msg
, "Event");
6758 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
6761 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
6763 tmp
= fix_newlines(msg
->body
));
6765 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
6767 /* implicit subscriptions */
6768 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
6769 sipe_process_imdn(sip
, msg
);
6773 /* for one off subscriptions (send with Expire: 0) */
6774 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
6776 sipe_process_provisioning_v2(sip
, msg
);
6778 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
6780 sipe_process_provisioning(sip
, msg
);
6782 else if (sipe_strcase_equal(event
, "presence"))
6784 sipe_process_presence(sip
, msg
);
6786 else if (sipe_strcase_equal(event
, "registration-notify"))
6788 sipe_process_registration_notify(sip
, msg
);
6791 if (!subscription_state
|| strstr(subscription_state
, "active"))
6793 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
6795 sipe_process_roaming_contacts(sip
, msg
);
6797 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
6799 sipe_process_roaming_self(sip
, msg
);
6801 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
6803 sipe_process_roaming_acl(sip
, msg
);
6805 else if (sipe_strcase_equal(event
, "presence.wpending"))
6807 sipe_process_presence_wpending(sip
, msg
);
6809 else if (sipe_strcase_equal(event
, "conference"))
6811 sipe_process_conference(sip
, msg
);
6816 /* The server sends status 'terminated' */
6817 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
6818 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6819 gchar
*key
= sipe_get_subscription_key(event
, who
);
6821 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
6824 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
6825 g_hash_table_remove(sip
->subscriptions
, key
);
6826 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
6832 if (!request
&& event
) {
6833 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
6834 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
6835 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
6838 /* 2 min ahead of expiration */
6839 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
6841 if (sipe_strcase_equal(event
, "presence.wpending") &&
6842 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
6844 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
6845 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
6846 g_free(action_name
);
6848 else if (sipe_strcase_equal(event
, "presence") &&
6849 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
6851 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
6852 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6854 if (sip
->batched_support
) {
6855 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
6858 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6859 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
6861 g_free(action_name
);
6867 /* The client responses on received a NOTIFY message */
6868 if (request
&& !benotify
)
6870 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
6875 * Whether user manually changed status or
6876 * it was changed automatically due to user
6877 * became inactive/active again
6880 sipe_is_user_state(struct sipe_account_data
*sip
)
6883 time_t now
= time(NULL
);
6885 purple_debug_info("sipe", "sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
6886 purple_debug_info("sipe", "sipe_is_user_state: now : %s", asctime(localtime(&now
)));
6888 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
6890 purple_debug_info("sipe", "sipe_is_user_state: res = %s\n", res
? "USER" : "MACHINE");
6895 send_presence_soap0(struct sipe_account_data
*sip
,
6896 gboolean do_publish_calendar
,
6897 gboolean do_reset_status
)
6899 struct sipe_ews
* ews
= sip
->ews
;
6900 int availability
= 0;
6905 gchar
*res_note
= NULL
;
6906 gchar
*res_oof
= NULL
;
6907 const gchar
*note_pub
= NULL
;
6908 gchar
*states
= NULL
;
6909 gchar
*calendar_data
= NULL
;
6910 gchar
*epid
= get_epid(sip
);
6911 time_t now
= time(NULL
);
6912 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
6913 const gchar
*oof_note
= ews
? sipe_ews_get_oof_note(ews
) : NULL
;
6914 const char *user_input
;
6915 gboolean pub_oof
= ews
&& oof_note
&& (!sip
->note
|| ews
->updated
> sip
->note_since
);
6917 if (oof_note
&& sip
->note
) {
6918 purple_debug_info("sipe", "ews->oof_start : %s", asctime(localtime(&(ews
->oof_start
))));
6919 purple_debug_info("sipe", "sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
6922 purple_debug_info("sipe", "sip->note : %s", sip
->note
? sip
->note
: "");
6924 if (!sip
->initial_state_published
||
6927 g_free(sip
->status
);
6928 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
6931 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
6935 note_pub
= oof_note
;
6936 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
6937 ews
->published
= TRUE
;
6938 } else if (sip
->note
) {
6939 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in ews already */
6942 sip
->is_oof_note
= FALSE
;
6943 sip
->note_since
= 0;
6945 note_pub
= sip
->note
;
6946 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
6952 /* to protocol internal plain text format */
6953 tmp
= purple_markup_strip_html(note_pub
);
6954 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
6959 if (!do_reset_status
) {
6960 if (sipe_is_user_state(sip
) && !do_publish_calendar
&& sip
->initial_state_published
)
6962 gchar
*activity_token
= NULL
;
6963 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
6965 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
6970 g_free(activity_token
);
6972 else /* preserve existing publication */
6974 if (sip
->user_states
) {
6975 states
= g_strdup(sip
->user_states
);
6979 /* do nothing - then User state will be erased */
6981 sip
->initial_state_published
= TRUE
;
6984 if (ews
&& (!is_empty(ews
->legacy_dn
) || !is_empty(ews
->email
)) && ews
->fb_start
&& !is_empty(ews
->free_busy
))
6986 char *fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
6987 char *free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
6988 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
6989 !is_empty(ews
->legacy_dn
) ? ews
->legacy_dn
: ews
->email
,
6992 g_free(fb_start_str
);
6993 g_free(free_busy_base64
);
6996 user_input
= !sipe_is_user_state(sip
) && sip
->status
!= SIPE_STATUS_ID_AVAILABLE
? "idle" : "active";
6998 /* forming resulting XML */
6999 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
7003 (tmp
= g_ascii_strup(g_get_host_name(), -1)),
7004 res_note
? res_note
: "",
7005 res_oof
? res_oof
: "",
7006 states
? states
: "",
7007 calendar_data
? calendar_data
: "",
7016 g_free(calendar_data
);
7018 send_soap_request(sip
, body
);
7021 g_free(since_time_str
);
7026 send_presence_soap(struct sipe_account_data
*sip
,
7027 gboolean do_publish_calendar
)
7029 return send_presence_soap0(sip
, do_publish_calendar
, FALSE
);
7034 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
7036 struct transaction
*trans
)
7038 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
7040 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
7046 gboolean has_device_publication
= FALSE
;
7048 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
7050 /* test if version mismatch fault */
7051 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
7052 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
7053 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
7060 /* accumulating information about faulty versions */
7061 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
7062 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
7064 node
= xmlnode_get_next_twin(node
))
7066 const gchar
*index
= xmlnode_get_attrib(node
, "index");
7067 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
7069 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
7070 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
7074 /* here we are parsing own request to figure out what publication
7075 * referensed here only by index went wrong
7077 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
7080 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
7081 index_our
= 1; /* starts with 1 - our first publication */
7083 node
= xmlnode_get_next_twin(node
), index_our
++)
7085 gchar
*idx
= g_strdup_printf("%d", index_our
);
7086 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
7087 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
7090 if (sipe_strequal("device", categoryName
)) {
7091 has_device_publication
= TRUE
;
7094 if (curVersion
) { /* fault exist on this index */
7095 const gchar
*container
= xmlnode_get_attrib(node
, "container");
7096 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
7097 /* key is <category><instance><container> */
7098 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
7099 struct sipe_publication
*publication
=
7100 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
7102 purple_debug_info("sipe", "key is %s\n", key
);
7105 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
7106 key
, curVersion
, publication
->version
);
7107 /* updating publication's version to the correct one */
7108 publication
->version
= atoi(curVersion
);
7114 g_hash_table_destroy(faults
);
7116 /* rebublishing with right versions */
7117 if (has_device_publication
) {
7118 send_publish_category_initial(sip
);
7120 send_presence_status(sip
);
7127 * Returns 'device' XML part for publication.
7128 * Must be g_free'd after use.
7131 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
7135 gchar
*epid
= get_epid(sip
);
7136 gchar
*uuid
= generateUUIDfromEPID(epid
);
7137 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
7138 /* key is <category><instance><container> */
7139 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
7140 struct sipe_publication
*publication
=
7141 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
7146 uri
= sip_uri_self(sip
);
7147 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
7149 publication
? publication
->version
: 0,
7152 "00:00:00+01:00", /* @TODO make timezone real*/
7163 * A service method - use
7164 * - send_publish_get_category_state_machine and
7165 * - send_publish_get_category_state_user instead.
7166 * Must be g_free'd after use.
7169 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
7170 gboolean is_user_state
)
7172 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
7173 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
7174 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
7175 /* key is <category><instance><container> */
7176 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7177 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7178 struct sipe_publication
*publication_2
=
7179 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7180 struct sipe_publication
*publication_3
=
7181 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7186 if (publication_2
&& (publication_2
->availability
== availability
))
7188 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
7189 return NULL
; /* nothing to update */
7192 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
7194 publication_2
? publication_2
->version
: 0,
7197 publication_3
? publication_3
->version
: 0,
7202 * Only Busy and OOF calendar event are published.
7203 * Different instances are used for that.
7205 * Must be g_free'd after use.
7208 sipe_publish_get_category_state_calendar(struct sipe_account_data
*sip
,
7209 struct sipe_cal_event
*event
,
7213 gchar
*start_time_str
;
7214 int availability
= 0;
7217 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
7218 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
) :
7219 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
7221 /* key is <category><instance><container> */
7222 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7223 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7224 struct sipe_publication
*publication_2
=
7225 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7226 struct sipe_publication
*publication_3
=
7227 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7232 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
7233 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7234 "Exiting as no publication and no event for cal_satus:%d\n", cal_satus
);
7240 (publication_3
->availability
== availability
) &&
7241 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
7244 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7245 "cal state has NOT changed for cal_satus:%d. Exiting.\n", cal_satus
);
7246 return NULL
; /* nothing to update */
7251 (event
->cal_status
== SIPE_CAL_BUSY
||
7252 event
->cal_status
== SIPE_CAL_OOF
))
7254 gchar
*availability_xml_str
= NULL
;
7255 gchar
*activity_xml_str
= NULL
;
7257 if (event
->cal_status
== SIPE_CAL_BUSY
) {
7258 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
7261 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
7262 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7263 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
7264 "minAvailability=\"6500\"",
7265 "maxAvailability=\"8999\"");
7266 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
7267 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7268 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
7269 "minAvailability=\"12000\"",
7272 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
7274 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
7276 publication_2
? publication_2
->version
: 0,
7279 availability_xml_str
? availability_xml_str
: "",
7280 activity_xml_str
? activity_xml_str
: "",
7281 event
->subject
? event
->subject
: "",
7282 event
->location
? event
->location
: "",
7285 publication_3
? publication_3
->version
: 0,
7288 availability_xml_str
? availability_xml_str
: "",
7289 activity_xml_str
? activity_xml_str
: "",
7290 event
->subject
? event
->subject
: "",
7291 event
->location
? event
->location
: ""
7293 g_free(start_time_str
);
7294 g_free(availability_xml_str
);
7295 g_free(activity_xml_str
);
7298 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
7300 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
7302 publication_2
? publication_2
->version
: 0,
7305 publication_3
? publication_3
->version
: 0
7313 * Returns 'machineState' XML part for publication.
7314 * Must be g_free'd after use.
7317 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
7319 return sipe_publish_get_category_state(sip
, FALSE
);
7323 * Returns 'userState' XML part for publication.
7324 * Must be g_free'd after use.
7327 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
7329 return sipe_publish_get_category_state(sip
, TRUE
);
7333 * Returns 'note' XML part for publication.
7334 * Must be g_free'd after use.
7336 * Protocol format for Note is plain text.
7338 * @param note a note in Sipe internal HTML format
7339 * @param note_type either personal or OOF
7342 sipe_publish_get_category_note(struct sipe_account_data
*sip
,
7343 const char *note
, /* html */
7344 const char *note_type
,
7348 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
) : 0;
7349 /* key is <category><instance><container> */
7350 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
7351 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
7352 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
7354 struct sipe_publication
*publication_note_200
=
7355 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
7356 struct sipe_publication
*publication_note_300
=
7357 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
7358 struct sipe_publication
*publication_note_400
=
7359 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
7361 char *tmp
= note
? purple_markup_strip_html(note
) : NULL
;
7362 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
7363 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
7364 char *res
, *tmp1
, *tmp2
, *tmp3
;
7365 char *start_time_attr
;
7366 char *end_time_attr
;
7370 g_free(key_note_200
);
7371 g_free(key_note_300
);
7372 g_free(key_note_400
);
7374 /* we even need to republish empty note */
7375 if (sipe_strequal(n1
, n2
))
7377 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
7379 return NULL
; /* nothing to update */
7382 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
7385 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
7389 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7392 publication_note_200
? publication_note_200
->version
: 0,
7394 start_time_attr
? start_time_attr
: "",
7395 end_time_attr
? end_time_attr
: "",
7398 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7401 publication_note_300
? publication_note_300
->version
: 0,
7403 start_time_attr
? start_time_attr
: "",
7404 end_time_attr
? end_time_attr
: "",
7407 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7410 publication_note_400
? publication_note_400
->version
: 0,
7412 start_time_attr
? start_time_attr
: "",
7413 end_time_attr
? end_time_attr
: "",
7416 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7420 publication_note_200
? publication_note_200
->version
: 0,
7422 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7426 publication_note_200
? publication_note_200
->version
: 0,
7428 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7432 publication_note_200
? publication_note_200
->version
: 0,
7435 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
7437 g_free(start_time_attr
);
7438 g_free(end_time_attr
);
7448 * Returns 'calendarData' XML part with WorkingHours for publication.
7449 * Must be g_free'd after use.
7452 sipe_publish_get_category_cal_working_hours(struct sipe_account_data
*sip
)
7454 struct sipe_ews
* ews
= sip
->ews
;
7456 /* key is <category><instance><container> */
7457 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
7458 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
7459 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
7460 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
7461 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
7462 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
7464 struct sipe_publication
*publication_cal_1
=
7465 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7466 struct sipe_publication
*publication_cal_100
=
7467 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7468 struct sipe_publication
*publication_cal_200
=
7469 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7470 struct sipe_publication
*publication_cal_300
=
7471 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7472 struct sipe_publication
*publication_cal_400
=
7473 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7474 struct sipe_publication
*publication_cal_32000
=
7475 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7477 const char *n1
= ews
? ews
->working_hours_xml_str
: NULL
;
7478 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
7481 g_free(key_cal_100
);
7482 g_free(key_cal_200
);
7483 g_free(key_cal_300
);
7484 g_free(key_cal_400
);
7485 g_free(key_cal_32000
);
7487 if (!ews
|| is_empty(ews
->email
) || is_empty(ews
->working_hours_xml_str
)) {
7488 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: no data to publish, exiting\n");
7492 if (sipe_strequal(n1
, n2
))
7494 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.\n");
7495 return NULL
; /* nothing to update */
7498 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
7500 publication_cal_1
? publication_cal_1
->version
: 0,
7502 ews
->working_hours_xml_str
,
7504 publication_cal_100
? publication_cal_100
->version
: 0,
7506 publication_cal_200
? publication_cal_200
->version
: 0,
7508 ews
->working_hours_xml_str
,
7510 publication_cal_300
? publication_cal_300
->version
: 0,
7512 ews
->working_hours_xml_str
,
7513 /* 400 - Personal */
7514 publication_cal_400
? publication_cal_400
->version
: 0,
7516 ews
->working_hours_xml_str
,
7517 /* 32000 - Blocked */
7518 publication_cal_32000
? publication_cal_32000
->version
: 0
7523 * Returns 'calendarData' XML part with FreeBusy for publication.
7524 * Must be g_free'd after use.
7527 sipe_publish_get_category_cal_free_busy(struct sipe_account_data
*sip
)
7529 struct sipe_ews
* ews
= sip
->ews
;
7530 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
7532 char *free_busy_base64
;
7537 /* key is <category><instance><container> */
7538 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
7539 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
7540 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
7541 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
7542 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
7543 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
7545 struct sipe_publication
*publication_cal_1
=
7546 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7547 struct sipe_publication
*publication_cal_100
=
7548 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7549 struct sipe_publication
*publication_cal_200
=
7550 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7551 struct sipe_publication
*publication_cal_300
=
7552 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7553 struct sipe_publication
*publication_cal_400
=
7554 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7555 struct sipe_publication
*publication_cal_32000
=
7556 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7559 g_free(key_cal_100
);
7560 g_free(key_cal_200
);
7561 g_free(key_cal_300
);
7562 g_free(key_cal_400
);
7563 g_free(key_cal_32000
);
7565 if (!ews
|| is_empty(ews
->email
) || !ews
->fb_start
|| is_empty(ews
->free_busy
)) {
7566 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: no data to publish, exiting\n");
7570 fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
7571 free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
7573 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
7574 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
7576 /* we will rebuplish the same data to refresh publication time,
7577 * so if data from multiple sources, most recent will be choosen
7579 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
7581 // purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.\n");
7582 // g_free(fb_start_str);
7583 // g_free(free_busy_base64);
7584 // return NULL; /* nothing to update */
7587 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
7590 publication_cal_1
? publication_cal_1
->version
: 0,
7593 publication_cal_100
? publication_cal_100
->version
: 0,
7596 publication_cal_200
? publication_cal_200
->version
: 0,
7602 publication_cal_300
? publication_cal_300
->version
: 0,
7606 /* 400 - Personal */
7608 publication_cal_400
? publication_cal_400
->version
: 0,
7612 /* 32000 - Blocked */
7614 publication_cal_32000
? publication_cal_32000
->version
: 0
7617 g_free(fb_start_str
);
7618 g_free(free_busy_base64
);
7622 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
7629 uri
= sip_uri_self(sip
);
7630 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
7634 tmp
= get_contact(sip
);
7635 hdr
= g_strdup_printf("Contact: %s\r\n"
7636 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
7638 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
7647 send_publish_category_initial(struct sipe_account_data
*sip
)
7649 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
7651 gchar
*publications
;
7653 g_free(sip
->status
);
7654 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
7656 pub_machine
= sipe_publish_get_category_state_machine(sip
);
7657 publications
= g_strdup_printf("%s%s",
7659 pub_machine
? pub_machine
: "");
7661 g_free(pub_machine
);
7663 send_presence_publish(sip
, publications
);
7664 g_free(publications
);
7668 send_presence_category_publish(struct sipe_account_data
*sip
)
7670 gchar
*pub_state
= sipe_is_user_state(sip
) ?
7671 sipe_publish_get_category_state_user(sip
) :
7672 sipe_publish_get_category_state_machine(sip
);
7673 gchar
*pub_note
= sipe_publish_get_category_note(sip
,
7675 sip
->is_oof_note
? "OOF" : "personal",
7678 gchar
*publications
;
7680 if (!pub_state
&& !pub_note
) {
7681 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
7685 publications
= g_strdup_printf("%s%s",
7686 pub_state
? pub_state
: "",
7687 pub_note
? pub_note
: "");
7692 send_presence_publish(sip
, publications
);
7693 g_free(publications
);
7697 * Publishes self status
7698 * based on own calendar information.
7703 publish_calendar_status_self(struct sipe_account_data
*sip
)
7705 struct sipe_cal_event
* event
= NULL
;
7706 gchar
*pub_cal_working_hours
= NULL
;
7707 gchar
*pub_cal_free_busy
= NULL
;
7708 gchar
*pub_calendar
= NULL
;
7709 gchar
*pub_calendar2
= NULL
;
7710 gchar
*pub_oof_note
= NULL
;
7711 const gchar
*oof_note
;
7712 time_t oof_start
= 0;
7716 purple_debug_info("sipe", "publish_calendar_status_self() no calendar data.\n");
7720 purple_debug_info("sipe", "publish_calendar_status_self() started.\n");
7721 if (sip
->ews
->cal_events
) {
7722 event
= sipe_cal_get_event(sip
->ews
->cal_events
, time(NULL
));
7726 purple_debug_info("sipe", "publish_calendar_status_self: current event is NULL\n");
7728 char *desc
= sipe_cal_event_describe(event
);
7729 purple_debug_info("sipe", "publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
7735 OOF publish, Busy clean
7737 OOF clean, Busy publish
7739 OOF clean, Busy clean
7741 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
7742 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_OOF
);
7743 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7744 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
7745 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7746 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7748 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7749 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7752 oof_note
= sipe_ews_get_oof_note(sip
->ews
);
7753 if (sipe_strequal("Scheduled", sip
->ews
->oof_state
)) {
7754 oof_start
= sip
->ews
->oof_start
;
7755 oof_end
= sip
->ews
->oof_end
;
7757 pub_oof_note
= sipe_publish_get_category_note(sip
, oof_note
, "OOF", oof_start
, oof_end
);
7759 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sip
);
7760 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sip
);
7762 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
7763 purple_debug_info("sipe", "publish_calendar_status_self: nothing has changed.\n");
7765 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
7766 pub_cal_working_hours
? pub_cal_working_hours
: "",
7767 pub_cal_free_busy
? pub_cal_free_busy
: "",
7768 pub_calendar
? pub_calendar
: "",
7769 pub_calendar2
? pub_calendar2
: "",
7770 pub_oof_note
? pub_oof_note
: "");
7772 send_presence_publish(sip
, publications
);
7773 g_free(publications
);
7776 g_free(pub_cal_working_hours
);
7777 g_free(pub_cal_free_busy
);
7778 g_free(pub_calendar
);
7779 g_free(pub_calendar2
);
7780 g_free(pub_oof_note
);
7782 /* repeat scheduling */
7783 sipe_sched_calendar_status_self_publish(sip
, time(NULL
));
7786 static void send_presence_status(struct sipe_account_data
*sip
)
7788 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
7790 if (!status
) return;
7792 purple_debug_info("sipe", "send_presence_status: status: %s (%s)\n",
7793 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
7794 sipe_is_user_state(sip
) ? "USER" : "MACHINE");
7797 send_presence_category_publish(sip
);
7799 send_presence_soap(sip
, FALSE
);
7803 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
7805 gboolean found
= FALSE
;
7806 const char *method
= msg
->method
? msg
->method
: "NOT FOUND";
7807 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,method
);
7808 if (msg
->response
== 0) { /* request */
7809 if (sipe_strequal(method
, "MESSAGE")) {
7810 process_incoming_message(sip
, msg
);
7812 } else if (sipe_strequal(method
, "NOTIFY")) {
7813 purple_debug_info("sipe","send->process_incoming_notify\n");
7814 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
7816 } else if (sipe_strequal(method
, "BENOTIFY")) {
7817 purple_debug_info("sipe","send->process_incoming_benotify\n");
7818 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
7820 } else if (sipe_strequal(method
, "INVITE")) {
7821 process_incoming_invite(sip
, msg
);
7823 } else if (sipe_strequal(method
, "REFER")) {
7824 process_incoming_refer(sip
, msg
);
7826 } else if (sipe_strequal(method
, "OPTIONS")) {
7827 process_incoming_options(sip
, msg
);
7829 } else if (sipe_strequal(method
, "INFO")) {
7830 process_incoming_info(sip
, msg
);
7832 } else if (sipe_strequal(method
, "ACK")) {
7833 // ACK's don't need any response
7835 } else if (sipe_strequal(method
, "SUBSCRIBE")) {
7836 // LCS 2005 sends us these - just respond 200 OK
7838 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
7839 } else if (sipe_strequal(method
, "BYE")) {
7840 process_incoming_bye(sip
, msg
);
7843 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
7845 } else { /* response */
7846 struct transaction
*trans
= transactions_find(sip
, msg
);
7848 if (msg
->response
== 407) {
7849 gchar
*resend
, *auth
;
7852 if (sip
->proxy
.retries
> 30) return;
7853 sip
->proxy
.retries
++;
7854 /* do proxy authentication */
7856 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
7858 fill_auth(ptmp
, &sip
->proxy
);
7859 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
7860 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7861 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7863 resend
= sipmsg_to_string(trans
->msg
);
7864 /* resend request */
7865 sendout_pkt(sip
->gc
, resend
);
7868 if (msg
->response
< 200) {
7869 /* ignore provisional response */
7870 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
7872 sip
->proxy
.retries
= 0;
7873 if (sipe_strequal(trans
->msg
->method
, "REGISTER")) {
7874 if (msg
->response
== 401)
7876 sip
->registrar
.retries
++;
7880 sip
->registrar
.retries
= 0;
7882 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
7884 if (msg
->response
== 401) {
7885 gchar
*resend
, *auth
, *ptmp
;
7886 const char* auth_scheme
;
7888 if (sip
->registrar
.retries
> 4) return;
7889 sip
->registrar
.retries
++;
7891 auth_scheme
= sipe_get_auth_scheme_name(sip
);
7892 ptmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
7894 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
? ptmp
: "");
7896 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
7897 sip
->gc
->wants_to_die
= TRUE
;
7898 purple_connection_error(sip
->gc
, tmp2
);
7903 fill_auth(ptmp
, &sip
->registrar
);
7904 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
7905 sipmsg_remove_header_now(trans
->msg
, "Authorization");
7906 sipmsg_add_header_now_pos(trans
->msg
, "Authorization", auth
, 5);
7908 resend
= sipmsg_to_string(trans
->msg
);
7909 /* resend request */
7910 sendout_pkt(sip
->gc
, resend
);
7915 if (trans
->callback
) {
7916 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
7917 /* call the callback to process response*/
7918 (trans
->callback
)(sip
, msg
, trans
);
7921 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
7922 transactions_remove(sip
, trans
);
7928 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
7932 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", method
, msg
->response
);
7936 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
7945 /* according to the RFC remove CRLF at the beginning */
7946 while (*cur
== '\r' || *cur
== '\n') {
7949 if (cur
!= conn
->inbuf
) {
7950 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
7951 conn
->inbufused
= strlen(conn
->inbuf
);
7954 /* Received a full Header? */
7955 sip
->processing_input
= TRUE
;
7956 while (sip
->processing_input
&&
7957 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
7958 time_t currtime
= time(NULL
);
7961 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
7963 msg
= sipmsg_parse_header(conn
->inbuf
);
7966 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
7967 if (msg
&& restlen
>= msg
->bodylen
) {
7968 dummy
= g_malloc(msg
->bodylen
+ 1);
7969 memcpy(dummy
, cur
, msg
->bodylen
);
7970 dummy
[msg
->bodylen
] = '\0';
7972 cur
+= msg
->bodylen
;
7973 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
7974 conn
->inbufused
= strlen(conn
->inbuf
);
7977 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
7984 purple_debug_info("sipe", "body:\n%s", msg->body);
7987 // Verify the signature before processing it
7988 if (sip
->registrar
.gssapi_context
) {
7989 struct sipmsg_breakdown msgbd
;
7990 gchar
*signature_input_str
;
7993 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
7994 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
7996 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
7998 if (rspauth
!= NULL
) {
7999 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
8000 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
8001 process_input_message(sip
, msg
);
8003 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
8004 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
8005 sip
->gc
->wants_to_die
= TRUE
;
8007 } else if (msg
->response
== 401) {
8008 purple_connection_error(sip
->gc
, _("Authentication failed"));
8009 sip
->gc
->wants_to_die
= TRUE
;
8011 g_free(signature_input_str
);
8014 sipmsg_breakdown_free(&msgbd
);
8016 process_input_message(sip
, msg
);
8023 static void sipe_udp_process(gpointer data
, gint source
,
8024 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
8026 PurpleConnection
*gc
= data
;
8027 struct sipe_account_data
*sip
= gc
->proto_data
;
8030 static char buffer
[65536];
8031 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
8032 time_t currtime
= time(NULL
);
8035 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
8036 msg
= sipmsg_parse_msg(buffer
);
8037 if (msg
) process_input_message(sip
, msg
);
8041 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
8043 struct sipe_account_data
*sip
= gc
->proto_data
;
8044 PurpleSslConnection
*gsc
= sip
->gsc
;
8046 purple_debug_error("sipe", "%s",debug
);
8047 purple_connection_error(gc
, msg
);
8049 /* Invalidate this connection. Next send will open a new one */
8051 connection_remove(sip
, gsc
->fd
);
8052 purple_ssl_close(gsc
);
8058 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8059 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8061 PurpleConnection
*gc
= data
;
8062 struct sipe_account_data
*sip
;
8063 struct sip_connection
*conn
;
8065 gboolean firstread
= TRUE
;
8067 /* NOTE: This check *IS* necessary */
8068 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
8069 purple_ssl_close(gsc
);
8073 sip
= gc
->proto_data
;
8074 conn
= connection_find(sip
, gsc
->fd
);
8076 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
8077 gc
->wants_to_die
= TRUE
;
8078 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
8082 /* Read all available data from the SSL connection */
8084 /* Increase input buffer size as needed */
8085 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8086 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8087 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8088 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
8091 /* Try to read as much as there is space left in the buffer */
8092 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
8093 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
8095 if (len
< 0 && errno
== EAGAIN
) {
8096 /* Try again later */
8098 } else if (len
< 0) {
8099 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
8101 } else if (firstread
&& (len
== 0)) {
8102 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
8106 conn
->inbufused
+= len
;
8109 /* Equivalence indicates that there is possibly more data to read */
8110 } while (len
== readlen
);
8112 conn
->inbuf
[conn
->inbufused
] = '\0';
8113 process_input(sip
, conn
);
8117 static void sipe_input_cb(gpointer data
, gint source
,
8118 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8120 PurpleConnection
*gc
= data
;
8121 struct sipe_account_data
*sip
= gc
->proto_data
;
8123 struct sip_connection
*conn
= connection_find(sip
, source
);
8125 purple_debug_error("sipe", "Connection not found!\n");
8129 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8130 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8131 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8134 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
8136 if (len
< 0 && errno
== EAGAIN
)
8138 else if (len
<= 0) {
8139 purple_debug_info("sipe", "sipe_input_cb: read error\n");
8140 connection_remove(sip
, source
);
8141 if (sip
->fd
== source
) sip
->fd
= -1;
8145 conn
->inbufused
+= len
;
8146 conn
->inbuf
[conn
->inbufused
] = '\0';
8148 process_input(sip
, conn
);
8151 /* Callback for new connections on incoming TCP port */
8152 static void sipe_newconn_cb(gpointer data
, gint source
,
8153 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8155 PurpleConnection
*gc
= data
;
8156 struct sipe_account_data
*sip
= gc
->proto_data
;
8157 struct sip_connection
*conn
;
8159 int newfd
= accept(source
, NULL
, NULL
);
8161 conn
= connection_create(sip
, newfd
);
8163 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8166 static void login_cb(gpointer data
, gint source
,
8167 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
8169 PurpleConnection
*gc
= data
;
8170 struct sipe_account_data
*sip
;
8171 struct sip_connection
*conn
;
8173 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8181 purple_connection_error(gc
, _("Could not connect"));
8185 sip
= gc
->proto_data
;
8187 sip
->last_keepalive
= time(NULL
);
8189 conn
= connection_create(sip
, source
);
8193 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8196 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8197 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8199 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
8200 if (sip
== NULL
) return;
8205 static guint
sipe_ht_hash_nick(const char *nick
)
8207 char *lc
= g_utf8_strdown(nick
, -1);
8208 guint bucket
= g_str_hash(lc
);
8214 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
8216 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
8219 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
8221 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8223 sip
->listen_data
= NULL
;
8225 if (listenfd
== -1) {
8226 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8232 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
8233 sip
->listenfd
= sip
->fd
;
8235 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
8237 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
8241 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
8242 SIPE_UNUSED_PARAMETER
const char *error_message
)
8244 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8246 sip
->query_data
= NULL
;
8248 if (!hosts
|| !hosts
->data
) {
8249 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
8253 hosts
= g_slist_remove(hosts
, hosts
->data
);
8254 g_free(sip
->serveraddr
);
8255 sip
->serveraddr
= hosts
->data
;
8256 hosts
= g_slist_remove(hosts
, hosts
->data
);
8258 void *tmp
= hosts
->data
;
8259 hosts
= g_slist_remove(hosts
, tmp
);
8260 hosts
= g_slist_remove(hosts
, tmp
);
8264 /* create socket for incoming connections */
8265 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
8266 sipe_udp_host_resolved_listen_cb
, sip
);
8267 if (sip
->listen_data
== NULL
) {
8268 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8273 static const struct sipe_service_data
*current_service
= NULL
;
8275 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
8276 PurpleSslErrorType error
,
8279 PurpleConnection
*gc
= data
;
8280 struct sipe_account_data
*sip
;
8282 /* If the connection is already disconnected, we don't need to do anything else */
8283 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8286 sip
= gc
->proto_data
;
8287 current_service
= sip
->service_data
;
8288 if (current_service
) {
8289 purple_debug_info("sipe", "current_service: transport '%s' service '%s'\n",
8290 current_service
->transport
? current_service
->transport
: "NULL",
8291 current_service
->service
? current_service
->service
: "NULL");
8298 case PURPLE_SSL_CONNECT_FAILED
:
8299 purple_connection_error(gc
, _("Connection failed"));
8301 case PURPLE_SSL_HANDSHAKE_FAILED
:
8302 purple_connection_error(gc
, _("SSL handshake failed"));
8304 case PURPLE_SSL_CERTIFICATE_INVALID
:
8305 purple_connection_error(gc
, _("SSL certificate invalid"));
8311 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
8313 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8314 PurpleProxyConnectData
*connect_data
;
8316 sip
->listen_data
= NULL
;
8318 sip
->listenfd
= listenfd
;
8319 if (sip
->listenfd
== -1) {
8320 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8324 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
8325 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
8326 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
8327 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
8328 sipe_newconn_cb
, sip
->gc
);
8329 purple_debug_info("sipe", "connecting to %s port %d\n",
8330 sip
->realhostname
, sip
->realport
);
8331 /* open tcp connection to the server */
8332 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
8333 sip
->realport
, login_cb
, sip
->gc
);
8335 if (connect_data
== NULL
) {
8336 purple_connection_error(sip
->gc
, _("Could not create socket"));
8340 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
8342 PurpleAccount
*account
= sip
->account
;
8343 PurpleConnection
*gc
= sip
->gc
;
8346 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
8349 sip
->realhostname
= hostname
;
8350 sip
->realport
= port
;
8352 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
8355 /* TODO: is there a good default grow size? */
8356 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
8357 sip
->txbuf
= purple_circ_buffer_new(0);
8359 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
8361 if (!purple_ssl_is_supported()) {
8362 gc
->wants_to_die
= TRUE
;
8363 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
8367 purple_debug_info("sipe", "using SSL\n");
8369 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
8370 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
8371 if (sip
->gsc
== NULL
) {
8372 purple_connection_error(gc
, _("Could not create SSL context"));
8375 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
8377 purple_debug_info("sipe", "using UDP\n");
8379 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
8380 if (sip
->query_data
== NULL
) {
8381 purple_connection_error(gc
, _("Could not resolve hostname"));
8385 purple_debug_info("sipe", "using TCP\n");
8386 /* create socket for incoming connections */
8387 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
8388 sipe_tcp_connect_listen_cb
, sip
);
8389 if (sip
->listen_data
== NULL
) {
8390 purple_connection_error(gc
, _("Could not create listen socket"));
8396 /* Service list for autodection */
8397 static const struct sipe_service_data service_autodetect
[] = {
8398 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8399 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8400 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8401 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8405 /* Service list for SSL/TLS */
8406 static const struct sipe_service_data service_tls
[] = {
8407 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8408 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8412 /* Service list for TCP */
8413 static const struct sipe_service_data service_tcp
[] = {
8414 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8415 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8419 /* Service list for UDP */
8420 static const struct sipe_service_data service_udp
[] = {
8421 { "sip", "udp", SIPE_TRANSPORT_UDP
},
8425 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
8426 static void resolve_next_service(struct sipe_account_data
*sip
,
8427 const struct sipe_service_data
*start
)
8430 sip
->service_data
= start
;
8432 sip
->service_data
++;
8433 if (sip
->service_data
->service
== NULL
) {
8435 /* Try connecting to the SIP hostname directly */
8436 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
8437 if (sip
->auto_transport
) {
8438 // If SSL is supported, default to using it; OCS servers aren't configured
8439 // by default to accept TCP
8440 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
8441 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8442 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
8445 hostname
= g_strdup(sip
->sipdomain
);
8446 create_connection(sip
, hostname
, 0);
8451 /* Try to resolve next service */
8452 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
8453 sip
->service_data
->transport
,
8458 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
8460 struct sipe_account_data
*sip
= data
;
8462 sip
->srv_query_data
= NULL
;
8464 /* find the host to connect to */
8466 gchar
*hostname
= g_strdup(resp
->hostname
);
8467 int port
= resp
->port
;
8468 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
8472 sip
->transport
= sip
->service_data
->type
;
8474 create_connection(sip
, hostname
, port
);
8476 resolve_next_service(sip
, NULL
);
8480 static void sipe_login(PurpleAccount
*account
)
8482 PurpleConnection
*gc
;
8483 struct sipe_account_data
*sip
;
8484 gchar
**signinname_login
, **userserver
;
8485 const char *transport
;
8488 const char *username
= purple_account_get_username(account
);
8489 gc
= purple_account_get_connection(account
);
8491 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
8493 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
8494 gc
->wants_to_die
= TRUE
;
8495 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
8499 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
8500 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
8501 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
8503 sip
->account
= account
;
8504 sip
->reregister_set
= FALSE
;
8505 sip
->reauthenticate_set
= FALSE
;
8506 sip
->subscribed
= FALSE
;
8507 sip
->subscribed_buddies
= FALSE
;
8508 sip
->initial_state_published
= FALSE
;
8510 /* username format: <username>,[<optional login>] */
8511 signinname_login
= g_strsplit(username
, ",", 2);
8512 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
8514 /* ensure that username format is name@domain */
8515 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
8516 g_strfreev(signinname_login
);
8517 gc
->wants_to_die
= TRUE
;
8518 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
8521 sip
->username
= g_strdup(signinname_login
[0]);
8523 /* ensure that email format is name@domain if provided */
8524 email
= purple_account_get_string(sip
->account
, "email", NULL
);
8525 if (!is_empty(email
) &&
8526 (!strchr(email
, '@') || g_str_has_prefix(email
, "@") || g_str_has_suffix(email
, "@")))
8528 gc
->wants_to_die
= TRUE
;
8529 purple_connection_error(gc
, _("Email address should be valid if provided\nExample: user@company.com"));
8532 sip
->email
= !is_empty(email
) ? g_strdup(email
) : g_strdup(sip
->username
);
8534 /* login name specified? */
8535 if (signinname_login
[1] && strlen(signinname_login
[1])) {
8536 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
8537 gboolean has_domain
= domain_user
[1] != NULL
;
8538 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
8539 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
8540 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
8541 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
8542 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
8543 g_strfreev(domain_user
);
8546 userserver
= g_strsplit(signinname_login
[0], "@", 2);
8547 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
8548 purple_connection_set_display_name(gc
, userserver
[0]);
8549 sip
->sipdomain
= g_strdup(userserver
[1]);
8550 g_strfreev(userserver
);
8551 g_strfreev(signinname_login
);
8553 if (strchr(sip
->username
, ' ') != NULL
) {
8554 gc
->wants_to_die
= TRUE
;
8555 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
8559 sip
->password
= g_strdup(purple_connection_get_password(gc
));
8561 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
8562 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8563 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
8564 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8565 g_free
, (GDestroyNotify
)sipe_subscription_free
);
8567 sip
->filetransfers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,g_free
,NULL
);
8569 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
8571 g_free(sip
->status
);
8572 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
8574 sip
->auto_transport
= FALSE
;
8575 transport
= purple_account_get_string(account
, "transport", "auto");
8576 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
8577 if (userserver
[0]) {
8578 /* Use user specified server[:port] */
8582 port
= atoi(userserver
[1]);
8584 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
8585 userserver
[0], port
);
8587 if (sipe_strequal(transport
, "auto")) {
8588 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8589 } else if (sipe_strequal(transport
, "tls")) {
8590 sip
->transport
= SIPE_TRANSPORT_TLS
;
8591 } else if (sipe_strequal(transport
, "tcp")) {
8592 sip
->transport
= SIPE_TRANSPORT_TCP
;
8594 sip
->transport
= SIPE_TRANSPORT_UDP
;
8597 create_connection(sip
, g_strdup(userserver
[0]), port
);
8599 /* Server auto-discovery */
8600 if (sipe_strequal(transport
, "auto")) {
8601 sip
->auto_transport
= TRUE
;
8602 if (current_service
&& current_service
->transport
!= NULL
&& current_service
->service
!= NULL
){
8604 resolve_next_service(sip
, current_service
);
8606 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
8608 } else if (sipe_strequal(transport
, "tls")) {
8609 resolve_next_service(sip
, service_tls
);
8610 } else if (sipe_strequal(transport
, "tcp")) {
8611 resolve_next_service(sip
, service_tcp
);
8613 resolve_next_service(sip
, service_udp
);
8616 g_strfreev(userserver
);
8619 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
8621 connection_free_all(sip
);
8626 if (sip
->query_data
!= NULL
)
8627 purple_dnsquery_destroy(sip
->query_data
);
8628 sip
->query_data
= NULL
;
8630 if (sip
->srv_query_data
!= NULL
)
8631 purple_srv_cancel(sip
->srv_query_data
);
8632 sip
->srv_query_data
= NULL
;
8634 if (sip
->listen_data
!= NULL
)
8635 purple_network_listen_cancel(sip
->listen_data
);
8636 sip
->listen_data
= NULL
;
8638 if (sip
->gsc
!= NULL
)
8639 purple_ssl_close(sip
->gsc
);
8642 sipe_auth_free(&sip
->registrar
);
8643 sipe_auth_free(&sip
->proxy
);
8646 purple_circ_buffer_destroy(sip
->txbuf
);
8649 g_free(sip
->realhostname
);
8650 sip
->realhostname
= NULL
;
8652 g_free(sip
->server_version
);
8653 sip
->server_version
= NULL
;
8656 purple_input_remove(sip
->listenpa
);
8658 if (sip
->tx_handler
)
8659 purple_input_remove(sip
->tx_handler
);
8660 sip
->tx_handler
= 0;
8661 if (sip
->resendtimeout
)
8662 purple_timeout_remove(sip
->resendtimeout
);
8663 sip
->resendtimeout
= 0;
8664 if (sip
->timeouts
) {
8665 GSList
*entry
= sip
->timeouts
;
8667 struct scheduled_action
*sched_action
= entry
->data
;
8668 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
8669 purple_timeout_remove(sched_action
->timeout_handler
);
8670 if (sched_action
->destroy
) {
8671 (*sched_action
->destroy
)(sched_action
->payload
);
8673 g_free(sched_action
->name
);
8674 g_free(sched_action
);
8675 entry
= entry
->next
;
8678 g_slist_free(sip
->timeouts
);
8680 if (sip
->allow_events
) {
8681 GSList
*entry
= sip
->allow_events
;
8683 g_free(entry
->data
);
8684 entry
= entry
->next
;
8687 g_slist_free(sip
->allow_events
);
8689 if (sip
->containers
) {
8690 GSList
*entry
= sip
->containers
;
8692 free_container((struct sipe_container
*)entry
->data
);
8693 entry
= entry
->next
;
8696 g_slist_free(sip
->containers
);
8699 g_free(sip
->contact
);
8700 sip
->contact
= NULL
;
8702 g_free(sip
->regcallid
);
8703 sip
->regcallid
= NULL
;
8705 if (sip
->serveraddr
)
8706 g_free(sip
->serveraddr
);
8707 sip
->serveraddr
= NULL
;
8709 if (sip
->focus_factory_uri
)
8710 g_free(sip
->focus_factory_uri
);
8711 sip
->focus_factory_uri
= NULL
;
8714 sip
->processing_input
= FALSE
;
8717 sipe_ews_free(sip
->ews
);
8723 * A callback for g_hash_table_foreach_remove
8725 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
8726 SIPE_UNUSED_PARAMETER gpointer user_data
)
8728 sipe_free_buddy((struct sipe_buddy
*) buddy
);
8730 /* We must return TRUE as the key/value have already been deleted */
8734 static void sipe_close(PurpleConnection
*gc
)
8736 struct sipe_account_data
*sip
= gc
->proto_data
;
8739 /* leave all conversations */
8740 sipe_session_close_all(sip
);
8741 sipe_session_remove_all(sip
);
8744 sip_csta_close(sip
);
8747 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
8748 /* unsubscribe all */
8749 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
8752 do_register_exp(sip
, 0);
8755 sipe_connection_cleanup(sip
);
8756 g_free(sip
->sipdomain
);
8757 g_free(sip
->username
);
8759 g_free(sip
->password
);
8760 g_free(sip
->authdomain
);
8761 g_free(sip
->authuser
);
8762 g_free(sip
->status
);
8764 g_free(sip
->user_states
);
8766 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
8767 g_hash_table_destroy(sip
->buddies
);
8768 g_hash_table_destroy(sip
->our_publications
);
8769 g_hash_table_destroy(sip
->user_state_publications
);
8770 g_hash_table_destroy(sip
->subscriptions
);
8771 g_hash_table_destroy(sip
->filetransfers
);
8774 GSList
*entry
= sip
->groups
;
8776 struct sipe_group
*group
= entry
->data
;
8777 g_free(group
->name
);
8779 entry
= entry
->next
;
8782 g_slist_free(sip
->groups
);
8784 if (sip
->our_publication_keys
) {
8785 GSList
*entry
= sip
->our_publication_keys
;
8787 g_free(entry
->data
);
8788 entry
= entry
->next
;
8791 g_slist_free(sip
->our_publication_keys
);
8793 while (sip
->transactions
)
8794 transactions_remove(sip
, sip
->transactions
->data
);
8796 g_free(gc
->proto_data
);
8797 gc
->proto_data
= NULL
;
8800 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
8801 SIPE_UNUSED_PARAMETER
void *user_data
)
8803 PurpleAccount
*acct
= purple_connection_get_account(gc
);
8804 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
8805 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
8807 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
8808 purple_conversation_present(conv
);
8812 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
8813 SIPE_UNUSED_PARAMETER
void *user_data
)
8816 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
8817 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
8820 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
8821 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
8823 PurpleNotifySearchResults
*results
;
8824 PurpleNotifySearchColumn
*column
;
8825 xmlnode
*searchResults
;
8827 int match_count
= 0;
8828 gboolean more
= FALSE
;
8831 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
8833 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
8834 if (!searchResults
) {
8835 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
8839 results
= purple_notify_searchresults_new();
8841 if (results
== NULL
) {
8842 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
8843 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
8845 xmlnode_free(searchResults
);
8849 column
= purple_notify_searchresults_column_new(_("User name"));
8850 purple_notify_searchresults_column_add(results
, column
);
8852 column
= purple_notify_searchresults_column_new(_("Name"));
8853 purple_notify_searchresults_column_add(results
, column
);
8855 column
= purple_notify_searchresults_column_new(_("Company"));
8856 purple_notify_searchresults_column_add(results
, column
);
8858 column
= purple_notify_searchresults_column_new(_("Country"));
8859 purple_notify_searchresults_column_add(results
, column
);
8861 column
= purple_notify_searchresults_column_new(_("Email"));
8862 purple_notify_searchresults_column_add(results
, column
);
8864 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
8867 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
8868 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
8869 g_strfreev(uri_parts
);
8871 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
8872 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
8873 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
8874 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
8876 purple_notify_searchresults_row_add(results
, row
);
8880 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
8881 char *data
= xmlnode_get_data(mrow
);
8882 more
= (g_strcasecmp(data
, "true") == 0);
8886 secondary
= g_strdup_printf(
8887 dngettext(PACKAGE_NAME
,
8888 "Found %d contact%s:",
8889 "Found %d contacts%s:", match_count
),
8890 match_count
, more
? _(" (more matched your query)") : "");
8892 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
8893 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
8894 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
8897 xmlnode_free(searchResults
);
8901 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
8903 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
8904 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
8910 PurpleRequestField
*field
= entries
->data
;
8911 const char *id
= purple_request_field_get_id(field
);
8912 const char *value
= purple_request_field_string_get_value(field
);
8914 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
8916 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
8917 } while ((entries
= g_list_next(entries
)) != NULL
);
8921 struct sipe_account_data
*sip
= gc
->proto_data
;
8922 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
8923 gchar
*query
= g_strjoinv(NULL
, attrs
);
8924 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
8925 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
8926 send_soap_request_with_cb(sip
, domain_uri
, body
,
8927 (TransCallback
) process_search_contact_response
, NULL
);
8936 static void sipe_show_find_contact(PurplePluginAction
*action
)
8938 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8939 PurpleRequestFields
*fields
;
8940 PurpleRequestFieldGroup
*group
;
8941 PurpleRequestField
*field
;
8943 fields
= purple_request_fields_new();
8944 group
= purple_request_field_group_new(NULL
);
8945 purple_request_fields_add_group(fields
, group
);
8947 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
8948 purple_request_field_group_add_field(group
, field
);
8949 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
8950 purple_request_field_group_add_field(group
, field
);
8951 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
8952 purple_request_field_group_add_field(group
, field
);
8953 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
8954 purple_request_field_group_add_field(group
, field
);
8956 purple_request_fields(gc
,
8958 _("Search for a contact"),
8959 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
8961 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
8963 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
8966 static void sipe_show_about_plugin(PurplePluginAction
*action
)
8968 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8969 char *tmp
= g_strdup_printf(
8971 * Non-translatable parts, like markup, are hard-coded
8972 * into the format string. This requires more translatable
8973 * texts but it makes the translations less error prone.
8975 "<b><font size=\"+1\">SIPE " PACKAGE_VERSION
" </font></b><br/>"
8978 "<li> - MS Office Communications Server 2007 R2</li><br/>"
8979 "<li> - MS Office Communications Server 2007</li><br/>"
8980 "<li> - MS Live Communications Server 2005</li><br/>"
8981 "<li> - MS Live Communications Server 2003</li><br/>"
8982 "<li> - Reuters Messaging</li><br/>"
8984 /* 2 */ "%s: <a href=\"" PACKAGE_URL
"\">" PACKAGE_URL
"</a><br/>"
8985 /* 3,4 */ "%s: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">%s</a><br/>"
8986 /* 5,6 */ "%s: <a href=\"" PACKAGE_BUGREPORT
"\">%s</a><br/>"
8987 /* 7 */ "%s: <a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a><br/>"
8988 /* 8 */ "%s: GPLv2+<br/>"
8992 " - Reuters Messaging network<br/>"
8993 " - Deutsche Bank<br/>"
8994 " - Merrill Lynch<br/>"
9003 " - Alcatel-Lucent<br/>"
9006 /* 10,11 */ "%s<a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a>%s.<br/>"
9008 /* 12 */ "<b>%s:</b><br/>"
9009 " - Anibal Avelar<br/>"
9010 " - Gabriel Burt<br/>"
9011 " - Stefan Becker<br/>"
9013 " - Jakub Adam<br/>"
9014 " - Tomáš Hrabčík<br/>"
9018 /* The next 13 texts make up the SIPE about note text */
9019 /* About note, part 1/13: introduction */
9020 _("A third-party plugin implementing extended version of SIP/SIMPLE used by various products"),
9021 /* About note, part 2/13: home page URL (label) */
9023 /* About note, part 3/13: support forum URL (label) */
9025 /* About note, part 4/13: support forum name (hyperlink text) */
9027 /* About note, part 5/13: bug tracker URL (label) */
9028 _("Report Problems"),
9029 /* About note, part 6/13: bug tracker URL (hyperlink text) */
9031 /* About note, part 7/13: translation service URL (label) */
9033 /* About note, part 8/13: license type (label) */
9035 /* About note, part 9/13: known users */
9036 _("We support users in such organizations as"),
9037 /* About note, part 10/13: translation request, text before Transifex.net URL */
9038 /* append a space if text is not empty */
9039 _("Please help us to translate SIPE to your native language here at "),
9040 /* About note, part 11/13: translation request, text after Transifex.net URL */
9041 /* start with a space if text is not empty */
9042 _(" using convenient web interface"),
9043 /* About note, part 12/13: author list (header) */
9045 /* About note, part 13/13: Localization credit */
9046 /* PLEASE NOTE: do *NOT* simply translate the english original */
9047 /* but write something similar to the following sentence: */
9048 /* "Localization for <language name> (<language code>): <name>" */
9049 _("Original texts in English (en): SIPE developers")
9051 purple_notify_formatted(gc
, NULL
, " ", NULL
, tmp
, NULL
, NULL
);
9055 static void sipe_republish_calendar(PurplePluginAction
*action
)
9057 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9058 struct sipe_account_data
*sip
= gc
->proto_data
;
9060 sipe_update_calendar(sip
);
9063 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
9067 struct sipe_publication
*publication
= value
;
9069 g_string_append_printf( str
,
9070 SIPE_PUB_XML_PUBLICATION_CLEAR
,
9071 publication
->category
,
9072 publication
->instance
,
9073 publication
->container
,
9074 publication
->version
,
9078 static void sipe_reset_status(PurplePluginAction
*action
)
9080 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9081 struct sipe_account_data
*sip
= gc
->proto_data
;
9083 if (sip
->ocs2007
) /* 2007+ */
9085 GString
* str
= g_string_new(NULL
);
9086 gchar
*publications
;
9088 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
9089 purple_debug_info("sipe", "sipe_reset_status: no userState publications, exiting.\n");
9093 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
9094 publications
= g_string_free(str
, FALSE
);
9096 send_presence_publish(sip
, publications
);
9097 g_free(publications
);
9101 send_presence_soap0(sip
, FALSE
, TRUE
);
9105 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
9108 PurpleConnection
*gc
= (PurpleConnection
*)context
;
9109 struct sipe_account_data
*sip
= gc
->proto_data
;
9111 PurplePluginAction
*act
;
9112 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
9114 act
= purple_plugin_action_new(_("About SIPE plugin..."), sipe_show_about_plugin
);
9115 menu
= g_list_prepend(menu
, act
);
9117 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
9118 menu
= g_list_prepend(menu
, act
);
9120 if (sipe_strequal(calendar
, "EXCH")) {
9121 act
= purple_plugin_action_new(_("Republish Calendar"), sipe_republish_calendar
);
9122 menu
= g_list_prepend(menu
, act
);
9125 act
= purple_plugin_action_new(_("Reset status"), sipe_reset_status
);
9126 menu
= g_list_prepend(menu
, act
);
9128 menu
= g_list_reverse(menu
);
9133 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
9137 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9143 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9149 static char *sipe_status_text(PurpleBuddy
*buddy
)
9151 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9152 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9153 const char *status_id
= purple_status_get_id(status
);
9154 struct sipe_account_data
*sip
= (struct sipe_account_data
*)buddy
->account
->gc
->proto_data
;
9155 struct sipe_buddy
*sbuddy
;
9158 if (!sip
) return NULL
; /* happens on pidgin exit */
9160 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9162 const char *activity_str
= sbuddy
->activity
?
9164 sipe_strequal(status_id
, SIPE_STATUS_ID_BUSY
) || sipe_strequal(status_id
, SIPE_STATUS_ID_BRB
) ?
9165 purple_status_get_name(status
) : NULL
;
9167 if (activity_str
&& sbuddy
->note
)
9169 text
= g_strdup_printf("%s - <i>%s</i>", activity_str
, sbuddy
->note
);
9171 else if (activity_str
)
9173 text
= g_strdup(activity_str
);
9175 else if (sbuddy
->note
)
9177 text
= g_strdup_printf("<i>%s</i>", sbuddy
->note
);
9184 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
9186 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9187 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9188 struct sipe_account_data
*sip
;
9189 struct sipe_buddy
*sbuddy
;
9191 gboolean is_oof_note
= FALSE
;
9192 char *activity
= NULL
;
9193 char *calendar
= NULL
;
9194 char *meeting_subject
= NULL
;
9195 char *meeting_location
= NULL
;
9197 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
9198 if (sip
) //happens on pidgin exit
9200 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9203 note
= sbuddy
->note
;
9204 is_oof_note
= sbuddy
->is_oof_note
;
9205 activity
= sbuddy
->activity
;
9206 calendar
= sipe_cal_get_description(sbuddy
);
9207 meeting_subject
= sbuddy
->meeting_subject
;
9208 meeting_location
= sbuddy
->meeting_location
;
9213 if (purple_presence_is_online(presence
))
9215 const char *status_str
= activity
? activity
: purple_status_get_name(status
);
9217 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
9219 if (purple_presence_is_online(presence
) &&
9220 !is_empty(calendar
))
9222 purple_notify_user_info_add_pair(user_info
, _("Calendar"), calendar
);
9225 if (!is_empty(meeting_location
))
9227 purple_notify_user_info_add_pair(user_info
, _("Meeting in"), meeting_location
);
9229 if (!is_empty(meeting_subject
))
9231 purple_notify_user_info_add_pair(user_info
, _("Meeting about"), meeting_subject
);
9236 char *tmp
= g_strdup_printf("<i>%s</i>", note
);
9237 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, note
);
9239 purple_notify_user_info_add_pair(user_info
, is_oof_note
? _("Out of office note") : _("Note"), tmp
);
9245 #if PURPLE_VERSION_CHECK(2,5,0)
9247 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
9250 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
9251 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
9256 static PurpleBuddy
*
9257 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
9260 const gchar
*server_alias
, *email
;
9261 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
9263 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
9265 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
9267 server_alias
= purple_buddy_get_server_alias(buddy
);
9269 purple_blist_server_alias_buddy(clone
, server_alias
);
9272 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9274 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
9277 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
9279 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
9284 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
9286 PurpleBuddy
*buddy
, *b
;
9287 PurpleConnection
*gc
;
9288 PurpleGroup
* group
= purple_find_group(group_name
);
9290 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
9292 buddy
= (PurpleBuddy
*)node
;
9294 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
9295 gc
= purple_account_get_connection(buddy
->account
);
9297 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
9299 purple_blist_add_buddy_clone(group
, buddy
);
9302 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
9306 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
9308 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9310 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
9312 /* 2007+ conference */
9315 sipe_conf_add(sip
, buddy
->name
);
9317 else /* 2005- multiparty chat */
9319 gchar
*self
= sip_uri_self(sip
);
9320 struct sip_session
*session
;
9322 session
= sipe_session_add_chat(sip
);
9323 session
->chat_title
= sipe_chat_get_name(session
->callid
);
9324 session
->roster_manager
= g_strdup(self
);
9326 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
9327 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
9328 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
9329 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
9336 sipe_is_election_finished(struct sip_session
*session
)
9338 gboolean res
= TRUE
;
9340 SIPE_DIALOG_FOREACH
{
9341 if (dialog
->election_vote
== 0) {
9345 } SIPE_DIALOG_FOREACH_END
;
9348 session
->is_voting_in_progress
= FALSE
;
9354 sipe_election_start(struct sipe_account_data
*sip
,
9355 struct sip_session
*session
)
9357 int election_timeout
;
9359 if (session
->is_voting_in_progress
) {
9360 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
9363 session
->is_voting_in_progress
= TRUE
;
9365 session
->bid
= rand();
9367 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
9369 SIPE_DIALOG_FOREACH
{
9370 /* reset election_vote for each chat participant */
9371 dialog
->election_vote
= 0;
9373 /* send RequestRM to each chat participant*/
9374 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
9375 } SIPE_DIALOG_FOREACH_END
;
9377 election_timeout
= 15; /* sec */
9378 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
9382 * @param who a URI to whom to invite to chat
9385 sipe_invite_to_chat(struct sipe_account_data
*sip
,
9386 struct sip_session
*session
,
9390 if (session
->focus_uri
)
9392 sipe_invite_conf(sip
, session
, who
);
9394 else /* a multi-party chat */
9396 gchar
*self
= sip_uri_self(sip
);
9397 if (session
->roster_manager
) {
9398 if (sipe_strcase_equal(session
->roster_manager
, self
)) {
9399 sipe_invite(sip
, session
, who
, NULL
, NULL
, NULL
, FALSE
);
9401 sipe_refer(sip
, session
, who
);
9404 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
9406 session
->pending_invite_queue
= slist_insert_unique_sorted(
9407 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
9409 sipe_election_start(sip
, session
);
9416 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
9417 struct sip_session
*session
)
9420 GSList
*entry
= session
->pending_invite_queue
;
9423 invitee
= entry
->data
;
9424 sipe_invite_to_chat(sip
, session
, invitee
);
9425 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
9431 sipe_election_result(struct sipe_account_data
*sip
,
9434 struct sip_session
*session
= (struct sip_session
*)sess
;
9436 gboolean has_won
= TRUE
;
9438 if (session
->roster_manager
) {
9439 purple_debug_info("sipe",
9440 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
9444 session
->is_voting_in_progress
= FALSE
;
9446 SIPE_DIALOG_FOREACH
{
9447 if (dialog
->election_vote
< 0) {
9449 rival
= dialog
->with
;
9452 } SIPE_DIALOG_FOREACH_END
;
9455 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
9457 session
->roster_manager
= sip_uri_self(sip
);
9459 SIPE_DIALOG_FOREACH
{
9460 /* send SetRM to each chat participant*/
9461 sipe_send_election_set_rm(sip
, dialog
);
9462 } SIPE_DIALOG_FOREACH_END
;
9464 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
9468 sipe_process_pending_invite_queue(sip
, session
);
9472 * For 2007+ conference only.
9475 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9477 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9478 struct sip_session
*session
;
9480 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
9481 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
9483 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9485 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
9489 * For 2007+ conference only.
9492 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9494 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9495 struct sip_session
*session
;
9497 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
9498 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
9500 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9502 sipe_conf_delete_user(sip
, session
, buddy
->name
);
9506 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
9508 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9509 struct sip_session
*session
;
9511 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
9512 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
9514 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9516 sipe_invite_to_chat(sip
, session
, buddy
->name
);
9520 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
9522 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9524 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
9526 char *tel_uri
= sip_to_tel_uri(phone
);
9528 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
9529 sip_csta_make_call(sip
, tel_uri
);
9536 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
9539 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
9541 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9544 char *mailto
= g_strdup_printf("mailto:%s", email
);
9545 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
9549 char *const parmList
[] = {"xdg-email", mailto
, NULL
};
9550 if ((pid
= fork()) == -1)
9552 purple_debug_info("sipe", "fork() error\n");
9556 execvp(parmList
[0], parmList
);
9557 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
9565 //@TODO resolve env variable %WINDIR% first
9566 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
9569 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
9578 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
9583 * A menu which appear when right-clicking on buddy in contact list.
9586 sipe_buddy_menu(PurpleBuddy
*buddy
)
9588 PurpleBlistNode
*g_node
;
9589 PurpleGroup
*group
, *gr_parent
;
9590 PurpleMenuAction
*act
;
9592 GList
*menu_groups
= NULL
;
9593 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9596 const char *phone_disp_str
;
9597 gchar
*self
= sip_uri_self(sip
);
9599 SIPE_SESSION_FOREACH
{
9600 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
9602 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
9604 PurpleConvChatBuddyFlags flags
;
9605 PurpleConvChatBuddyFlags flags_us
;
9607 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
9608 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9609 if (session
->focus_uri
9610 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
9611 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9613 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
9614 act
= purple_menu_action_new(label
,
9615 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
9616 session
->chat_title
, NULL
);
9618 menu
= g_list_prepend(menu
, act
);
9621 if (session
->focus_uri
9622 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9624 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
9625 act
= purple_menu_action_new(label
,
9626 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
9627 session
->chat_title
, NULL
);
9629 menu
= g_list_prepend(menu
, act
);
9634 if (!session
->focus_uri
9635 || (session
->focus_uri
&& !session
->locked
))
9637 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
9638 act
= purple_menu_action_new(label
,
9639 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
9640 session
->chat_title
, NULL
);
9642 menu
= g_list_prepend(menu
, act
);
9646 } SIPE_SESSION_FOREACH_END
;
9648 act
= purple_menu_action_new(_("New chat"),
9649 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
9651 menu
= g_list_prepend(menu
, act
);
9653 if (sip
->csta
&& !sip
->csta
->line_status
) {
9656 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
9657 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
9659 gchar
*label
= g_strdup_printf(_("Work %s"),
9660 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9661 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9665 menu
= g_list_prepend(menu
, act
);
9669 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
9670 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
9672 gchar
*label
= g_strdup_printf(_("Mobile %s"),
9673 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9674 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9678 menu
= g_list_prepend(menu
, act
);
9682 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
9683 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
9685 gchar
*label
= g_strdup_printf(_("Home %s"),
9686 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9687 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9691 menu
= g_list_prepend(menu
, act
);
9695 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
9696 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
9698 gchar
*label
= g_strdup_printf(_("Other %s"),
9699 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9700 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9704 menu
= g_list_prepend(menu
, act
);
9708 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
9709 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
9711 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
9712 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9713 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9717 menu
= g_list_prepend(menu
, act
);
9721 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9723 act
= purple_menu_action_new(_("Send email..."),
9724 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
9726 menu
= g_list_prepend(menu
, act
);
9729 gr_parent
= purple_buddy_get_group(buddy
);
9730 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
9731 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
9734 group
= (PurpleGroup
*)g_node
;
9735 if (group
== gr_parent
)
9738 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
9741 act
= purple_menu_action_new(purple_group_get_name(group
),
9742 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
9744 menu_groups
= g_list_prepend(menu_groups
, act
);
9746 menu_groups
= g_list_reverse(menu_groups
);
9748 act
= purple_menu_action_new(_("Copy to"),
9751 menu
= g_list_prepend(menu
, act
);
9752 menu
= g_list_reverse(menu
);
9759 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
9761 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9762 struct sip_session
*session
;
9764 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9765 sipe_conf_modify_conference_lock(sip
, session
, locked
);
9769 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
9771 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
9772 sipe_conf_modify_lock(chat
, FALSE
);
9776 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
9778 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
9779 sipe_conf_modify_lock(chat
, TRUE
);
9783 sipe_chat_menu(PurpleChat
*chat
)
9785 PurpleMenuAction
*act
;
9786 PurpleConvChatBuddyFlags flags_us
;
9788 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9789 struct sip_session
*session
;
9792 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9793 if (!session
) return NULL
;
9795 self
= sip_uri_self(sip
);
9796 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9798 if (session
->focus_uri
9799 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9801 if (session
->locked
) {
9802 act
= purple_menu_action_new(_("Unlock"),
9803 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
9805 menu
= g_list_prepend(menu
, act
);
9807 act
= purple_menu_action_new(_("Lock"),
9808 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
9810 menu
= g_list_prepend(menu
, act
);
9814 menu
= g_list_reverse(menu
);
9821 sipe_blist_node_menu(PurpleBlistNode
*node
)
9823 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
9824 return sipe_buddy_menu((PurpleBuddy
*) node
);
9825 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
9826 return sipe_chat_menu((PurpleChat
*)node
);
9833 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
9835 char *uri
= trans
->payload
->data
;
9837 PurpleNotifyUserInfo
*info
;
9838 PurpleBuddy
*pbuddy
= NULL
;
9839 struct sipe_buddy
*sbuddy
;
9840 const char *alias
= NULL
;
9841 char *device_name
= NULL
;
9842 char *server_alias
= NULL
;
9843 char *phone_number
= NULL
;
9846 char *first_name
= NULL
;
9847 char *last_name
= NULL
;
9849 if (!sip
) return FALSE
;
9851 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
9853 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
9854 alias
= purple_buddy_get_local_alias(pbuddy
);
9856 //will query buddy UA's capabilities and send answer to log
9857 sipe_options_request(sip
, uri
);
9859 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
9861 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
9864 info
= purple_notify_user_info_new();
9866 if (msg
->response
!= 200) {
9867 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
9869 xmlnode
*searchResults
;
9872 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
9873 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
9874 if (!searchResults
) {
9875 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
9876 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
9878 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
9879 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
9880 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
9882 /* For 2007 system we will take this from ContactCard -
9883 * it has cleaner tel: URIs at least
9885 if (!sip
->ocs2007
) {
9886 char *tel_uri
= sip_to_tel_uri(phone_number
);
9887 /* trims its parameters, so call first */
9888 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
9889 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
9890 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
9891 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
9895 if (server_alias
&& strlen(server_alias
) > 0) {
9896 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9898 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
9899 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
9901 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
9902 purple_notify_user_info_add_pair(info
, _("Office"), value
);
9904 if (phone_number
&& strlen(phone_number
) > 0) {
9905 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
9907 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
9908 purple_notify_user_info_add_pair(info
, _("Company"), value
);
9910 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
9911 purple_notify_user_info_add_pair(info
, _("City"), value
);
9913 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
9914 purple_notify_user_info_add_pair(info
, _("State"), value
);
9916 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
9917 purple_notify_user_info_add_pair(info
, _("Country"), value
);
9919 if (email
&& strlen(email
) > 0) {
9920 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9924 xmlnode_free(searchResults
);
9927 purple_notify_user_info_add_section_break(info
);
9929 if (is_empty(server_alias
)) {
9930 g_free(server_alias
);
9931 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
9933 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9937 /* present alias if it differs from server alias */
9938 if (alias
&& !sipe_strequal(alias
, server_alias
))
9940 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
9943 if (is_empty(email
)) {
9945 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
9947 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9951 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
9953 purple_notify_user_info_add_pair(info
, _("Site"), site
);
9956 sipe_get_first_last_names(sip
, uri
, &first_name
, &last_name
);
9957 if (first_name
&& last_name
) {
9958 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
9960 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
9967 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
9970 /* show a buddy's user info in a nice dialog box */
9971 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
9972 uri
, /* buddy's URI */
9974 NULL
, /* callback called when dialog closed */
9975 NULL
); /* userdata for callback */
9977 g_free(phone_number
);
9978 g_free(server_alias
);
9980 g_free(device_name
);
9986 * AD search first, LDAP based
9988 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
9990 struct sipe_account_data
*sip
= gc
->proto_data
;
9991 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
9992 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
9993 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
9994 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
9996 payload
->destroy
= g_free
;
9997 payload
->data
= g_strdup(username
);
9999 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
10000 send_soap_request_with_cb(sip
, domain_uri
, body
,
10001 (TransCallback
) process_get_info_response
, payload
);
10002 g_free(domain_uri
);
10007 PurplePluginProtocolInfo prpl_info
=
10009 OPT_PROTO_CHAT_TOPIC
,
10010 NULL
, /* user_splits */
10011 NULL
, /* protocol_options */
10012 NO_BUDDY_ICONS
, /* icon_spec */
10013 sipe_list_icon
, /* list_icon */
10014 NULL
, /* list_emblems */
10015 sipe_status_text
, /* status_text */
10016 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
10017 sipe_status_types
, /* away_states */
10018 sipe_blist_node_menu
, /* blist_node_menu */
10019 NULL
, /* chat_info */
10020 NULL
, /* chat_info_defaults */
10021 sipe_login
, /* login */
10022 sipe_close
, /* close */
10023 sipe_im_send
, /* send_im */
10024 NULL
, /* set_info */ // TODO maybe
10025 sipe_send_typing
, /* send_typing */
10026 sipe_get_info
, /* get_info */
10027 sipe_set_status
, /* set_status */
10028 sipe_set_idle
, /* set_idle */
10029 NULL
, /* change_passwd */
10030 sipe_add_buddy
, /* add_buddy */
10031 NULL
, /* add_buddies */
10032 sipe_remove_buddy
, /* remove_buddy */
10033 NULL
, /* remove_buddies */
10034 sipe_add_permit
, /* add_permit */
10035 sipe_add_deny
, /* add_deny */
10036 sipe_add_deny
, /* rem_permit */
10037 sipe_add_permit
, /* rem_deny */
10038 dummy_permit_deny
, /* set_permit_deny */
10039 NULL
, /* join_chat */
10040 NULL
, /* reject_chat */
10041 NULL
, /* get_chat_name */
10042 sipe_chat_invite
, /* chat_invite */
10043 sipe_chat_leave
, /* chat_leave */
10044 NULL
, /* chat_whisper */
10045 sipe_chat_send
, /* chat_send */
10046 sipe_keep_alive
, /* keepalive */
10047 NULL
, /* register_user */
10048 NULL
, /* get_cb_info */ // deprecated
10049 NULL
, /* get_cb_away */ // deprecated
10050 sipe_alias_buddy
, /* alias_buddy */
10051 sipe_group_buddy
, /* group_buddy */
10052 sipe_rename_group
, /* rename_group */
10053 NULL
, /* buddy_free */
10054 sipe_convo_closed
, /* convo_closed */
10055 purple_normalize_nocase
, /* normalize */
10056 NULL
, /* set_buddy_icon */
10057 sipe_remove_group
, /* remove_group */
10058 NULL
, /* get_cb_real_name */ // TODO?
10059 NULL
, /* set_chat_topic */
10060 NULL
, /* find_blist_chat */
10061 NULL
, /* roomlist_get_list */
10062 NULL
, /* roomlist_cancel */
10063 NULL
, /* roomlist_expand_category */
10064 NULL
, /* can_receive_file */
10065 sipe_ft_send_file
, /* send_file */
10066 sipe_ft_new_xfer
, /* new_xfer */
10067 NULL
, /* offline_message */
10068 NULL
, /* whiteboard_prpl_ops */
10069 sipe_send_raw
, /* send_raw */
10070 NULL
, /* roomlist_room_serialize */
10071 NULL
, /* unregister_user */
10072 NULL
, /* send_attention */
10073 NULL
, /* get_attention_types */
10074 #if !PURPLE_VERSION_CHECK(2,5,0)
10075 /* Backward compatibility when compiling against 2.4.x API */
10076 (void (*)(void)) /* _purple_reserved4 */
10078 sizeof(PurplePluginProtocolInfo
), /* struct_size */
10079 #if PURPLE_VERSION_CHECK(2,5,0)
10080 sipe_get_account_text_table
, /* get_account_text_table */
10081 #if PURPLE_VERSION_CHECK(2,6,0)
10082 NULL
, /* initiate_media */
10083 NULL
, /* get_media_caps */
10089 PurplePluginInfo info
= {
10090 PURPLE_PLUGIN_MAGIC
,
10091 PURPLE_MAJOR_VERSION
,
10092 PURPLE_MINOR_VERSION
,
10093 PURPLE_PLUGIN_PROTOCOL
, /**< type */
10094 NULL
, /**< ui_requirement */
10096 NULL
, /**< dependencies */
10097 PURPLE_PRIORITY_DEFAULT
, /**< priority */
10098 "prpl-sipe", /**< id */
10099 "Office Communicator", /**< name */
10100 PACKAGE_VERSION
, /**< version */
10101 "Microsoft Office Communicator Protocol Plugin", /**< summary */
10102 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
10103 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
10104 "Anibal Avelar <avelar@gmail.com>, " /**< author */
10105 "Gabriel Burt <gburt@novell.com>, " /**< author */
10106 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
10107 "pier11 <pier11@operamail.com>", /**< author */
10108 PACKAGE_URL
, /**< homepage */
10109 sipe_plugin_load
, /**< load */
10110 sipe_plugin_unload
, /**< unload */
10111 sipe_plugin_destroy
, /**< destroy */
10112 NULL
, /**< ui_info */
10113 &prpl_info
, /**< extra_info */
10125 c-file-style: "bsd"
10126 indent-tabs-mode: t