6 * Copyright (C) 2010 pier11 <pier11@operamail.com>
7 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
8 * Copyright (C) 2009 pier11 <pier11@operamail.com>
9 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
10 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
11 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
14 * Thanks to Google's Summer of Code Program and the helpful mentors
17 * Session-based SIP MESSAGE documentation:
18 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
43 #define _LIBC_INTERNAL_
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
60 #include "accountopt.h"
62 #include "conversation.h"
66 #include "savedstatuses.h"
77 #include "core-depurple.h" /* Temporary for the core de-purple transition */
82 #include "sipe-chat.h"
83 #include "sipe-conf.h"
85 #include "sipe-dialog.h"
87 #include "sipe-session.h"
88 #include "sipe-utils.h"
91 #include "sipe-sign.h"
95 /* Backward compatibility when compiling against 2.4.x API */
96 #if !PURPLE_VERSION_CHECK(2,5,0)
97 #define PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY 0x0100
100 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
102 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
103 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
105 /* Keep in sync with sipe_transport_type! */
106 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
107 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
109 /* Status identifiers (see also: sipe_status_types()) */
110 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
111 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
112 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
113 /* PURPLE_STATUS_UNAVAILABLE: */
114 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
115 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
116 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
117 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
118 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
119 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
120 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
121 /* PURPLE_STATUS_AWAY: */
122 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
123 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
124 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
125 /** Reuters status (user settable) */
126 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
127 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
128 /* ??? PURPLE_STATUS_MOBILE */
129 /* ??? PURPLE_STATUS_TUNE */
131 /* Status attributes (see also sipe_status_types() */
132 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
134 #define SDP_ACCEPT_TYPES "text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml text/x-msmsgsinvite"
136 static struct sipe_activity_map_struct
141 const char *status_id
;
143 } const sipe_activity_map
[] =
145 /* This has nothing to do with Availability numbers, like 3500 (online).
146 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
148 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
149 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
150 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
151 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
152 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
153 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
154 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
155 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
156 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
157 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
158 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
159 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
160 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
161 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
162 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
164 /** @param x is sipe_activity */
165 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
168 /* Action name templates */
169 #define ACTION_NAME_PRESENCE "<presence><%s>"
172 sipe_get_activity_by_token(const char *token
)
176 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
178 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
179 return sipe_activity_map
[i
].type
;
182 return sipe_activity_map
[0].type
;
186 sipe_get_activity_desc_by_token(const char *token
)
188 if (!token
) return NULL
;
190 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
193 /** Allows to send typed messages from chat window again after account reinstantiation. */
195 sipe_rejoin_chat(PurpleConversation
*conv
)
197 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
&&
198 PURPLE_CONV_CHAT(conv
)->left
)
200 PURPLE_CONV_CHAT(conv
)->left
= FALSE
;
201 purple_conversation_update(conv
, PURPLE_CONV_UPDATE_CHATLEFT
);
205 static char *genbranch()
207 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
208 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
209 rand() & 0xFFFF, rand() & 0xFFFF);
213 static char *default_ua
= NULL
;
215 sipe_get_useragent(struct sipe_account_data
*sip
)
217 const char *useragent
= purple_account_get_string(sip
->account
, "useragent", "");
218 if (is_empty(useragent
)) {
220 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
222 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
223 #define SIPE_TARGET_PLATFORM "linux"
224 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
225 #define SIPE_TARGET_PLATFORM "bsd"
226 #elif defined(__APPLE__) || defined(__MACOS__)
227 #define SIPE_TARGET_PLATFORM "macosx"
228 #elif defined(_AIX) || defined(__AIX__) || defined(__aix__)
229 #define SIPE_TARGET_PLATFORM "aix"
230 #elif defined(__solaris__) || defined(__sun)
231 #define SIPE_TARGET_PLATFORM "sun"
232 #elif defined(_WIN32)
233 #define SIPE_TARGET_PLATFORM "win"
234 #elif defined(__CYGWIN__)
235 #define SIPE_TARGET_PLATFORM "cygwin"
236 #elif defined(__hpux__)
237 #define SIPE_TARGET_PLATFORM "hpux"
238 #elif defined(__sgi__)
239 #define SIPE_TARGET_PLATFORM "irix"
241 #define SIPE_TARGET_PLATFORM "unknown"
244 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
245 #define SIPE_TARGET_ARCH "x86_64"
246 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
247 #define SIPE_TARGET_ARCH "i386"
248 #elif defined(__ppc64__)
249 #define SIPE_TARGET_ARCH "ppc64"
250 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
251 #define SIPE_TARGET_ARCH "ppc"
252 #elif defined(__hppa__) || defined(__hppa)
253 #define SIPE_TARGET_ARCH "hppa"
254 #elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000)
255 #define SIPE_TARGET_ARCH "mips"
256 #elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x)
257 #define SIPE_TARGET_ARCH "s390"
258 #elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8)
259 #define SIPE_TARGET_ARCH "sparc"
260 #elif defined(__arm__)
261 #define SIPE_TARGET_ARCH "arm"
263 #define SIPE_TARGET_ARCH "other"
266 default_ua
= g_strdup_printf("Purple/%s Sipe/" SIPE_VERSION
" (" SIPE_TARGET_PLATFORM
"-" SIPE_TARGET_ARCH
"; %s)",
267 purple_core_get_version(),
268 sip
->server_version
? sip
->server_version
: "");
270 useragent
= default_ua
;
275 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
276 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
281 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
283 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
);
285 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
286 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
289 static void sipe_close(PurpleConnection
*gc
);
291 static void send_presence_status(struct sipe_account_data
*sip
);
293 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
295 static void sipe_keep_alive(PurpleConnection
*gc
)
297 struct sipe_account_data
*sip
= gc
->proto_data
;
298 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
299 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
300 gchar buf
[2] = {0, 0};
301 purple_debug_info("sipe", "sending keep alive\n");
302 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
304 time_t now
= time(NULL
);
305 if ((sip
->keepalive_timeout
> 0) &&
306 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
) &&
307 ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
309 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
310 sendout_pkt(gc
, "\r\n\r\n");
311 sip
->last_keepalive
= now
;
316 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
318 struct sip_connection
*ret
= NULL
;
319 GSList
*entry
= sip
->openconns
;
322 if (ret
->fd
== fd
) return ret
;
328 static void sipe_auth_free(struct sip_auth
*auth
)
330 g_free(auth
->opaque
);
334 g_free(auth
->target
);
337 auth
->type
= AUTH_TYPE_UNSET
;
340 g_free(auth
->gssapi_data
);
341 auth
->gssapi_data
= NULL
;
342 sip_sec_destroy_context(auth
->gssapi_context
);
343 auth
->gssapi_context
= NULL
;
346 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
348 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
350 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
354 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
356 struct sip_connection
*conn
= connection_find(sip
, fd
);
358 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
359 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
365 static void connection_free_all(struct sipe_account_data
*sip
)
367 struct sip_connection
*ret
= NULL
;
368 GSList
*entry
= sip
->openconns
;
371 connection_remove(sip
, ret
->fd
);
372 entry
= sip
->openconns
;
377 sipe_make_signature(struct sipe_account_data
*sip
,
380 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
383 const char *authuser
= sip
->authuser
;
387 if (!authuser
|| strlen(authuser
) < 1) {
388 authuser
= sip
->username
;
391 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
392 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
395 // If we have a signature for the message, include that
396 if (msg
->signature
) {
397 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
);
400 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
401 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
404 gchar
*sign_str
= NULL
;
406 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
409 purple_account_get_bool(sip
->account
, "sso", TRUE
),
410 sip
->authdomain
? sip
->authdomain
: "",
415 if (!gssapi_data
|| !auth
->gssapi_context
) {
416 sip
->gc
->wants_to_die
= TRUE
;
417 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
421 if (auth
->version
> 3) {
422 sipe_make_signature(sip
, msg
);
423 sign_str
= g_strdup_printf(", crand=\"%s\", cnum=\"%s\", response=\"%s\"",
424 msg
->rand
, msg
->num
, msg
->signature
);
426 sign_str
= g_strdup("");
429 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
430 version_str
= auth
->version
> 2 ? g_strdup_printf(", version=%d", auth
->version
) : g_strdup("");
431 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
);
439 version_str
= auth
->version
> 2 ? g_strdup_printf(", version=%d", auth
->version
) : g_strdup("");
440 ret
= g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"%s", auth_protocol
, auth
->realm
, auth
->target
, version_str
);
444 } else { /* Digest */
446 /* Calculate new session key */
448 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
449 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
450 authuser
, auth
->realm
, sip
->password
,
451 auth
->gssapi_data
, NULL
);
454 sprintf(noncecount
, "%08d", auth
->nc
++);
455 response
= purple_cipher_http_digest_calculate_response("md5",
456 msg
->method
, msg
->target
, NULL
, NULL
,
457 auth
->gssapi_data
, noncecount
, NULL
,
459 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
461 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
);
467 static char *parse_attribute(const char *attrname
, const char *source
)
469 const char *tmp
, *tmp2
;
471 int len
= strlen(attrname
);
473 if (g_str_has_prefix(source
, attrname
)) {
475 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
477 retval
= g_strndup(tmp
, tmp2
- tmp
);
479 retval
= g_strdup(tmp
);
485 static void fill_auth(const gchar
*hdr
, struct sip_auth
*auth
)
491 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
495 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
496 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
497 auth
->type
= AUTH_TYPE_NTLM
;
500 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
501 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
502 auth
->type
= AUTH_TYPE_KERBEROS
;
506 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
507 auth
->type
= AUTH_TYPE_DIGEST
;
511 parts
= g_strsplit(hdr
, "\", ", 0);
512 for (i
= 0; parts
[i
]; i
++) {
515 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
517 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
518 g_free(auth
->gssapi_data
);
519 auth
->gssapi_data
= tmp
;
521 if (auth
->type
== AUTH_TYPE_NTLM
) {
522 /* NTLM module extracts nonce from gssapi-data */
526 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
527 /* Only used with AUTH_TYPE_DIGEST */
528 g_free(auth
->gssapi_data
);
529 auth
->gssapi_data
= tmp
;
530 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
531 g_free(auth
->opaque
);
533 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
537 if (auth
->type
== AUTH_TYPE_DIGEST
) {
538 /* Throw away old session key */
539 g_free(auth
->opaque
);
543 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
544 g_free(auth
->target
);
546 } else if ((tmp
= parse_attribute("version=", parts
[i
]))) {
547 auth
->version
= atoi(tmp
);
550 // uncomment to revert to previous functionality if version 3+ does not work.
551 // auth->version = 2;
558 static void sipe_canwrite_cb(gpointer data
,
559 SIPE_UNUSED_PARAMETER gint source
,
560 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
562 PurpleConnection
*gc
= data
;
563 struct sipe_account_data
*sip
= gc
->proto_data
;
567 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
569 if (max_write
== 0) {
570 if (sip
->tx_handler
!= 0){
571 purple_input_remove(sip
->tx_handler
);
577 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
579 if (written
< 0 && errno
== EAGAIN
)
581 else if (written
<= 0) {
582 /*TODO: do we really want to disconnect on a failure to write?*/
583 purple_connection_error(gc
, _("Could not write"));
587 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
590 static void sipe_canwrite_cb_ssl(gpointer data
,
591 SIPE_UNUSED_PARAMETER gint src
,
592 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
594 PurpleConnection
*gc
= data
;
595 struct sipe_account_data
*sip
= gc
->proto_data
;
599 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
601 if (max_write
== 0) {
602 if (sip
->tx_handler
!= 0) {
603 purple_input_remove(sip
->tx_handler
);
609 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
611 if (written
< 0 && errno
== EAGAIN
)
613 else if (written
<= 0) {
614 /*TODO: do we really want to disconnect on a failure to write?*/
615 purple_connection_error(gc
, _("Could not write"));
619 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
622 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
624 static void send_later_cb(gpointer data
, gint source
,
625 SIPE_UNUSED_PARAMETER
const gchar
*error
)
627 PurpleConnection
*gc
= data
;
628 struct sipe_account_data
*sip
;
629 struct sip_connection
*conn
;
631 if (!PURPLE_CONNECTION_IS_VALID(gc
))
639 purple_connection_error(gc
, _("Could not connect"));
643 sip
= gc
->proto_data
;
645 sip
->connecting
= FALSE
;
646 sip
->last_keepalive
= time(NULL
);
648 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
650 /* If there is more to write now, we need to register a handler */
651 if (sip
->txbuf
->bufused
> 0)
652 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
654 conn
= connection_create(sip
, source
);
655 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
658 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
660 struct sipe_account_data
*sip
;
662 if (!PURPLE_CONNECTION_IS_VALID(gc
))
664 if (gsc
) purple_ssl_close(gsc
);
668 sip
= gc
->proto_data
;
671 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
672 sip
->connecting
= FALSE
;
673 sip
->last_keepalive
= time(NULL
);
675 connection_create(sip
, gsc
->fd
);
677 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
682 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
683 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
685 PurpleConnection
*gc
= data
;
686 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
687 if (sip
== NULL
) return;
689 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
691 /* If there is more to write now */
692 if (sip
->txbuf
->bufused
> 0) {
693 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
698 static void sendlater(PurpleConnection
*gc
, const char *buf
)
700 struct sipe_account_data
*sip
= gc
->proto_data
;
702 if (!sip
->connecting
) {
703 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
704 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
705 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
707 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
708 purple_connection_error(gc
, _("Could not create socket"));
711 sip
->connecting
= TRUE
;
714 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
715 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
717 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
720 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
722 struct sipe_account_data
*sip
= gc
->proto_data
;
723 time_t currtime
= time(NULL
);
724 int writelen
= strlen(buf
);
727 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sending - %s######\n%s######\n", ctime(&currtime
), tmp
= fix_newlines(buf
));
729 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
730 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
731 purple_debug_info("sipe", "could not send packet\n");
740 if (sip
->tx_handler
) {
745 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
747 ret
= write(sip
->fd
, buf
, writelen
);
751 if (ret
< 0 && errno
== EAGAIN
)
753 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
758 if (ret
< writelen
) {
759 if (!sip
->tx_handler
){
761 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
764 sip
->tx_handler
= purple_input_add(sip
->fd
,
765 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
770 /* XXX: is it OK to do this? You might get part of a request sent
771 with part of another. */
772 if (sip
->txbuf
->bufused
> 0)
773 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
775 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
781 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
783 sendout_pkt(gc
, buf
);
787 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
789 GSList
*tmp
= msg
->headers
;
792 GString
*outstr
= g_string_new("");
793 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
795 name
= ((struct sipnameval
*) (tmp
->data
))->name
;
796 value
= ((struct sipnameval
*) (tmp
->data
))->value
;
797 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
798 tmp
= g_slist_next(tmp
);
800 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
801 sendout_pkt(sip
->gc
, outstr
->str
);
802 g_string_free(outstr
, TRUE
);
806 sipe_make_signature(struct sipe_account_data
*sip
,
809 if (sip
->registrar
.gssapi_context
) {
810 struct sipmsg_breakdown msgbd
;
811 gchar
*signature_input_str
;
813 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
814 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
815 sip
->registrar
.ntlm_num
++;
816 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
817 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
818 if (signature_input_str
!= NULL
) {
819 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
820 msg
->signature
= signature_hex
;
821 msg
->rand
= g_strdup(msgbd
.rand
);
822 msg
->num
= g_strdup(msgbd
.num
);
823 g_free(signature_input_str
);
825 sipmsg_breakdown_free(&msgbd
);
829 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
833 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
837 sipe_make_signature(sip
, msg
);
839 if (sip
->registrar
.type
&& sipe_strequal(method
, "REGISTER")) {
840 buf
= auth_header(sip
, &sip
->registrar
, msg
);
842 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
845 } 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")) {
846 sip
->registrar
.nc
= 3;
847 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
849 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
850 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
855 buf
= auth_header(sip
, &sip
->registrar
, msg
);
856 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
859 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
863 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
864 const char *text
, const char *body
)
868 GString
*outstr
= g_string_new("");
869 struct sipe_account_data
*sip
= gc
->proto_data
;
872 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
874 /* Can return NULL! */
875 contact
= get_contact(sip
);
877 sipmsg_add_header(msg
, "Contact", contact
);
882 gchar
*len
= g_strdup_printf("%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
883 sipmsg_add_header(msg
, "Content-Length", len
);
886 sipmsg_add_header(msg
, "Content-Length", "0");
889 msg
->response
= code
;
891 sipmsg_strip_headers(msg
, keepers
);
892 sipmsg_merge_new_headers(msg
);
893 sign_outgoing_message(msg
, sip
, msg
->method
);
895 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
898 name
= ((struct sipnameval
*) (tmp
->data
))->name
;
899 value
= ((struct sipnameval
*) (tmp
->data
))->value
;
901 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
902 tmp
= g_slist_next(tmp
);
904 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
905 sendout_pkt(gc
, outstr
->str
);
906 g_string_free(outstr
, TRUE
);
909 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
911 if (sip
->transactions
) {
912 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
913 purple_debug_info("sipe", "sip->transactions count:%d after removal\n", g_slist_length(sip
->transactions
));
915 if (trans
->msg
) sipmsg_free(trans
->msg
);
916 if (trans
->payload
) {
917 (*trans
->payload
->destroy
)(trans
->payload
->data
);
918 g_free(trans
->payload
);
925 static struct transaction
*
926 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
928 const gchar
*call_id
;
930 struct transaction
*trans
= g_new0(struct transaction
, 1);
932 trans
->time
= time(NULL
);
933 trans
->msg
= (struct sipmsg
*)msg
;
934 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
935 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
936 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
937 trans
->callback
= callback
;
938 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
939 purple_debug_info("sipe", "sip->transactions count:%d after addition\n", g_slist_length(sip
->transactions
));
943 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
945 struct transaction
*trans
;
946 GSList
*transactions
= sip
->transactions
;
947 const gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
948 const gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
951 if (!call_id
|| !cseq
) {
952 purple_debug(PURPLE_DEBUG_ERROR
, "sipe", "transaction_find: no Call-ID or CSeq!\n");
956 key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
957 while (transactions
) {
958 trans
= transactions
->data
;
959 if (!g_strcasecmp(trans
->key
, key
)) {
963 transactions
= transactions
->next
;
971 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
972 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
973 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
975 struct sipe_account_data
*sip
= gc
->proto_data
;
976 const char *addh
= "";
979 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
980 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
981 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
982 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
983 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
984 gchar
*route
= g_strdup("");
985 gchar
*epid
= get_epid(sip
);
986 int cseq
= dialog
? ++dialog
->cseq
: 1 /* as Call-Id is new in this case */;
987 struct transaction
*trans
= NULL
;
989 if (dialog
&& dialog
->routes
)
991 GSList
*iter
= dialog
->routes
;
996 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
998 iter
= g_slist_next(iter
);
1002 if (!ourtag
&& !dialog
) {
1006 if (sipe_strequal(method
, "REGISTER")) {
1007 if (sip
->regcallid
) {
1009 callid
= g_strdup(sip
->regcallid
);
1011 sip
->regcallid
= g_strdup(callid
);
1016 if (addheaders
) addh
= addheaders
;
1018 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
1019 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
1020 "From: <sip:%s>%s%s;epid=%s\r\n"
1021 "To: <%s>%s%s%s%s\r\n"
1022 "Max-Forwards: 70\r\n"
1024 "User-Agent: %s\r\n"
1027 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
1029 dialog
&& dialog
->request
? dialog
->request
: url
,
1030 TRANSPORT_DESCRIPTOR
,
1031 purple_network_get_my_ip(-1),
1033 branch
? ";branch=" : "",
1034 branch
? branch
: "",
1036 ourtag
? ";tag=" : "",
1037 ourtag
? ourtag
: "",
1040 theirtag
? ";tag=" : "",
1041 theirtag
? theirtag
: "",
1042 theirepid
? ";epid=" : "",
1043 theirepid
? theirepid
: "",
1046 sipe_get_useragent(sip
),
1050 body
? (gsize
) strlen(body
) : 0,
1054 //printf ("parsing msg buf:\n%s\n\n", buf);
1055 msg
= sipmsg_parse_msg(buf
);
1066 sign_outgoing_message (msg
, sip
, method
);
1068 buf
= sipmsg_to_string (msg
);
1070 /* add to ongoing transactions */
1071 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
1072 if (!sipe_strequal(method
, "ACK")) {
1073 trans
= transactions_add_buf(sip
, msg
, tc
);
1077 sendout_pkt(gc
, buf
);
1084 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
1087 send_soap_request_with_cb(struct sipe_account_data
*sip
,
1090 TransCallback callback
,
1091 struct transaction_payload
*payload
)
1093 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sip
);
1094 gchar
*contact
= get_contact(sip
);
1095 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1096 "Content-Type: application/SOAP+xml\r\n",contact
);
1098 struct transaction
*trans
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1099 trans
->payload
= payload
;
1106 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1108 send_soap_request_with_cb(sip
, NULL
, body
, NULL
, NULL
);
1111 static char *get_contact_register(struct sipe_account_data
*sip
)
1113 char *epid
= get_epid(sip
);
1114 char *uuid
= generateUUIDfromEPID(epid
);
1115 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
);
1121 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1129 if (!sip
->sipdomain
) return;
1131 uri
= sip_uri_from_name(sip
->sipdomain
);
1132 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1133 to
= sip_uri_self(sip
);
1134 contact
= get_contact_register(sip
);
1135 hdr
= g_strdup_printf("Contact: %s\r\n"
1136 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1137 "Event: registration\r\n"
1138 "Allow-Events: presence\r\n"
1139 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1140 "%s", contact
, expires
);
1144 sip
->registerstatus
= 1;
1146 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1147 process_register_response
);
1154 static void do_register_cb(struct sipe_account_data
*sip
,
1155 SIPE_UNUSED_PARAMETER
void *unused
)
1157 do_register_exp(sip
, -1);
1158 sip
->reregister_set
= FALSE
;
1161 static void do_register(struct sipe_account_data
*sip
)
1163 do_register_exp(sip
, -1);
1167 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1169 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1170 send_soap_request(sip
, body
);
1175 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1178 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1180 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1183 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1187 void sipe_auth_user_cb(void * data
)
1189 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1192 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1197 void sipe_deny_user_cb(void * data
)
1199 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1202 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1207 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1209 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1210 sipe_contact_allow_deny(sip
, name
, TRUE
);
1214 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1216 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1217 sipe_contact_allow_deny(sip
, name
, FALSE
);
1221 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1223 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1224 sipe_contact_set_acl(sip, name, "");
1228 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1232 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1233 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1235 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1237 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1238 if (!watchers
) return;
1240 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1241 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1242 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1243 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1245 // TODO pull out optional displayName to pass as alias
1247 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1248 job
->who
= remote_user
;
1250 purple_account_request_authorization(
1264 xmlnode_free(watchers
);
1269 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1271 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1272 if (!purple_group
) {
1273 purple_group
= purple_group_new(group
->name
);
1274 purple_blist_add_group(purple_group
, NULL
);
1278 group
->purple_group
= purple_group
;
1279 sip
->groups
= g_slist_append(sip
->groups
, group
);
1280 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1282 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1286 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1288 struct sipe_group
*group
;
1294 entry
= sip
->groups
;
1296 group
= entry
->data
;
1297 if (group
->id
== id
) {
1300 entry
= entry
->next
;
1305 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1307 struct sipe_group
*group
;
1309 if (!sip
|| !name
) {
1313 entry
= sip
->groups
;
1315 group
= entry
->data
;
1316 if (sipe_strequal(group
->name
, name
)) {
1319 entry
= entry
->next
;
1325 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1328 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1329 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1330 send_soap_request(sip
, body
);
1332 g_free(group
->name
);
1333 group
->name
= g_strdup(name
);
1337 * Only appends if no such value already stored.
1340 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1341 GSList
* res
= list
;
1342 if (!g_slist_find_custom(list
, data
, func
)) {
1343 res
= g_slist_insert_sorted(list
, data
, func
);
1349 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1350 return group1
->id
- group2
->id
;
1354 * Returns string like "2 4 7 8" - group ids buddy belong to.
1357 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1360 //creating array from GList, converting int to gchar*
1361 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1362 GSList
*entry
= buddy
->groups
;
1364 if (!ids_arr
) return NULL
;
1367 struct sipe_group
* group
= entry
->data
;
1368 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1369 entry
= entry
->next
;
1373 res
= g_strjoinv(" ", ids_arr
);
1374 g_strfreev(ids_arr
);
1379 * Sends buddy update to server
1382 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1384 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1385 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1387 if (buddy
&& purple_buddy
) {
1388 const char *alias
= purple_buddy_get_alias(purple_buddy
);
1389 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1392 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1394 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1395 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1397 send_soap_request(sip
, body
);
1404 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1406 if (msg
->response
== 200) {
1407 struct sipe_group
*group
;
1408 struct group_user_context
*ctx
= trans
->payload
->data
;
1412 struct sipe_buddy
*buddy
;
1414 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1419 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1425 group_id
= xmlnode_get_data(node
);
1431 group
= g_new0(struct sipe_group
, 1);
1432 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1434 group
->name
= g_strdup(ctx
->group_name
);
1436 sipe_group_add(sip
, group
);
1438 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1440 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1443 sipe_group_set_user(sip
, ctx
->user_name
);
1451 static void sipe_group_context_destroy(gpointer data
)
1453 struct group_user_context
*ctx
= data
;
1454 g_free(ctx
->group_name
);
1455 g_free(ctx
->user_name
);
1459 static void sipe_group_create (struct sipe_account_data
*sip
, const gchar
*name
, const gchar
* who
)
1461 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
1462 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
1464 ctx
->group_name
= g_strdup(name
);
1465 ctx
->user_name
= g_strdup(who
);
1466 payload
->destroy
= sipe_group_context_destroy
;
1467 payload
->data
= ctx
;
1469 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1470 send_soap_request_with_cb(sip
, NULL
, body
, process_add_group_response
, payload
);
1475 * Data structure for scheduled actions
1478 struct scheduled_action
{
1481 * Format is <Event>[<Data>...]
1482 * Example: <presence><sip:user@domain.com> or <registration>
1485 guint timeout_handler
;
1486 gboolean repetitive
;
1488 GDestroyNotify destroy
;
1489 struct sipe_account_data
*sip
;
1495 * Should return FALSE if repetitive action is not needed
1497 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1500 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1501 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1502 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1503 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1504 ret
= sched_action
->repetitive
;
1505 if (sched_action
->destroy
) {
1506 (*sched_action
->destroy
)(sched_action
->payload
);
1508 g_free(sched_action
->name
);
1509 g_free(sched_action
);
1514 * Kills action timer effectively cancelling
1517 * @param name of action
1519 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1523 if (!sip
->timeouts
|| !name
) return;
1525 entry
= sip
->timeouts
;
1527 struct scheduled_action
*sched_action
= entry
->data
;
1528 if(sipe_strequal(sched_action
->name
, name
)) {
1529 GSList
*to_delete
= entry
;
1530 entry
= entry
->next
;
1531 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1532 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1533 purple_timeout_remove(sched_action
->timeout_handler
);
1534 if (sched_action
->destroy
) {
1535 (*sched_action
->destroy
)(sched_action
->payload
);
1537 g_free(sched_action
->name
);
1538 g_free(sched_action
);
1540 entry
= entry
->next
;
1546 sipe_schedule_action0(const gchar
*name
,
1550 GDestroyNotify destroy
,
1551 struct sipe_account_data
*sip
,
1554 struct scheduled_action
*sched_action
;
1556 /* Make sure each action only exists once */
1557 sipe_cancel_scheduled_action(sip
, name
);
1559 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1560 sched_action
= g_new0(struct scheduled_action
, 1);
1561 sched_action
->repetitive
= FALSE
;
1562 sched_action
->name
= g_strdup(name
);
1563 sched_action
->action
= action
;
1564 sched_action
->destroy
= destroy
;
1565 sched_action
->sip
= sip
;
1566 sched_action
->payload
= payload
;
1567 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1568 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1569 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1570 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1574 sipe_schedule_action(const gchar
*name
,
1577 GDestroyNotify destroy
,
1578 struct sipe_account_data
*sip
,
1581 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1585 * Same as sipe_schedule_action() but timeout is in milliseconds.
1588 sipe_schedule_action_msec(const gchar
*name
,
1591 GDestroyNotify destroy
,
1592 struct sipe_account_data
*sip
,
1595 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1599 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1600 time_t calculate_from
);
1603 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
1606 sipe_get_status_by_availability(int avail
,
1610 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
1611 const char *status_id
,
1612 const char *message
,
1613 time_t do_not_publish
[]);
1616 sipe_apply_calendar_status(struct sipe_account_data
*sip
,
1617 struct sipe_buddy
*sbuddy
,
1618 const char *status_id
)
1620 time_t cal_avail_since
;
1621 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
1625 if (!sbuddy
) return;
1627 if (cal_status
< SIPE_CAL_NO_DATA
) {
1628 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_status : %d for %s\n", cal_status
, sbuddy
->name
);
1629 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
1632 /* scheduled Cal update call */
1634 status_id
= sbuddy
->last_non_cal_status_id
;
1635 g_free(sbuddy
->activity
);
1636 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
1640 purple_debug_info("sipe", "sipe_apply_calendar_status: status_id is NULL for %s, exiting.\n",
1641 sbuddy
->name
? sbuddy
->name
: "" );
1645 /* adjust to calendar status */
1646 if (cal_status
!= SIPE_CAL_NO_DATA
) {
1647 purple_debug_info("sipe", "sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
1649 if (cal_status
== SIPE_CAL_BUSY
1650 && cal_avail_since
> sbuddy
->user_avail_since
1651 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
1653 status_id
= SIPE_STATUS_ID_BUSY
;
1654 g_free(sbuddy
->activity
);
1655 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
1657 avail
= sipe_get_availability_by_status(status_id
, NULL
);
1659 purple_debug_info("sipe", "sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
1660 if (cal_avail_since
> sbuddy
->activity_since
) {
1661 if (cal_status
== SIPE_CAL_OOF
1662 && avail
>= 15000) /* 12000 in 2007 */
1664 g_free(sbuddy
->activity
);
1665 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
1670 /* then set status_id actually */
1671 purple_debug_info("sipe", "sipe_apply_calendar_status: to %s for %s\n", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
1672 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, status_id
, NULL
);
1674 /* set our account state to the one in roaming (including calendar info) */
1675 self_uri
= sip_uri_self(sip
);
1676 if (sip
->initial_state_published
&& sipe_strequal(sbuddy
->name
, self_uri
)) {
1677 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
1678 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
1681 purple_debug_info("sipe", "sipe_apply_calendar_status: switch to '%s' for the account\n", sip
->status
);
1682 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
1688 sipe_got_user_status(struct sipe_account_data
*sip
,
1690 const char *status_id
)
1692 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
1694 if (!sbuddy
) return;
1696 /* Check if on 2005 system contact's calendar,
1697 * then set/preserve it.
1699 if (!sip
->ocs2007
) {
1700 sipe_apply_calendar_status(sip
, sbuddy
, status_id
);
1702 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
1707 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
1708 struct sipe_buddy
*sbuddy
,
1709 struct sipe_account_data
*sip
)
1711 sipe_apply_calendar_status(sip
, sbuddy
, NULL
);
1715 * Updates contact's status
1716 * based on their calendar information.
1718 * Applicability: 2005 systems
1721 update_calendar_status(struct sipe_account_data
*sip
)
1723 purple_debug_info("sipe", "update_calendar_status() started.\n");
1724 g_hash_table_foreach(sip
->buddies
, (GHFunc
)update_calendar_status_cb
, (gpointer
)sip
);
1726 /* repeat scheduling */
1727 sipe_sched_calendar_status_update(sip
, time(NULL
) + 3*60 /* 3 min */);
1731 * Schedules process of contacts' status update
1732 * based on their calendar information.
1733 * Should be scheduled to the beginning of every
1734 * 15 min interval, like:
1735 * 13:00, 13:15, 13:30, 13:45, etc.
1737 * Applicability: 2005 systems
1740 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1741 time_t calculate_from
)
1743 int interval
= 15*60;
1744 /** start of the beginning of closest 15 min interval. */
1745 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1747 purple_debug_info("sipe", "sipe_sched_calendar_status_update: calculate_from time: %s",
1748 asctime(localtime(&calculate_from
)));
1749 purple_debug_info("sipe", "sipe_sched_calendar_status_update: next start time : %s",
1750 asctime(localtime(&next_start
)));
1752 sipe_schedule_action("<+2005-cal-status>",
1753 (int)(next_start
- time(NULL
)),
1754 (Action
)update_calendar_status
,
1761 * Schedules process of self status publish
1762 * based on own calendar information.
1763 * Should be scheduled to the beginning of every
1764 * 15 min interval, like:
1765 * 13:00, 13:15, 13:30, 13:45, etc.
1767 * Applicability: 2007+ systems
1770 sipe_sched_calendar_status_self_publish(struct sipe_account_data
*sip
,
1771 time_t calculate_from
)
1773 int interval
= 5*60;
1774 /** start of the beginning of closest 5 min interval. */
1775 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1777 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: calculate_from time: %s",
1778 asctime(localtime(&calculate_from
)));
1779 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: next start time : %s",
1780 asctime(localtime(&next_start
)));
1782 sipe_schedule_action("<+2007-cal-status>",
1783 (int)(next_start
- time(NULL
)),
1784 (Action
)publish_calendar_status_self
,
1790 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1792 /** Should be g_free()'d
1795 sipe_get_subscription_key(const gchar
*event
,
1800 if (is_empty(event
)) return NULL
;
1802 if (event
&& !g_ascii_strcasecmp(event
, "presence")) {
1803 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1804 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, with
);
1806 /* @TODO drop participated buddies' just_added flag */
1808 /* Subscription is identified by <event> key */
1809 key
= g_strdup_printf("<%s>", event
);
1815 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1816 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1818 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
1819 const gchar
*event
= sipmsg_find_header(msg
, "Event");
1822 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
1824 struct sipmsg
*request_msg
= trans
->msg
;
1825 event
= sipmsg_find_header(request_msg
, "Event");
1828 key
= sipe_get_subscription_key(event
, with
);
1830 /* 200 OK; 481 Call Leg Does Not Exist */
1831 if (key
&& (msg
->response
== 200 || msg
->response
== 481)) {
1832 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
1833 g_hash_table_remove(sip
->subscriptions
, key
);
1834 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
1838 /* create/store subscription dialog if not yet */
1839 if (msg
->response
== 200) {
1840 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1841 gchar
*cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
1844 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
1845 g_hash_table_insert(sip
->subscriptions
, g_strdup(key
), subscription
);
1847 subscription
->dialog
.callid
= g_strdup(callid
);
1848 subscription
->dialog
.cseq
= atoi(cseq
);
1849 subscription
->dialog
.with
= g_strdup(with
);
1850 subscription
->event
= g_strdup(event
);
1851 sipe_dialog_parse(&subscription
->dialog
, msg
, TRUE
);
1853 purple_debug_info("sipe", "process_subscribe_response: subscription dialog added for: %s\n", key
);
1862 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1864 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1869 static void sipe_subscribe_resource_uri(const char *name
,
1870 SIPE_UNUSED_PARAMETER gpointer value
,
1871 gchar
**resources_uri
)
1873 gchar
*tmp
= *resources_uri
;
1874 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1878 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1880 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1881 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1882 gchar
*tmp
= *resources_uri
;
1884 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
1886 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
1891 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1892 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1893 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1894 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1895 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1898 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1901 gchar
*contact
= get_contact(sip
);
1904 gchar
*require
= "";
1906 gchar
*autoextend
= "";
1907 gchar
*content_type
;
1908 struct sip_dialog
*dialog
;
1911 require
= ", categoryList";
1912 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1913 content_type
= "application/msrtc-adrl-categorylist+xml";
1914 content
= g_strdup_printf(
1915 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1916 "<action name=\"subscribe\" id=\"63792024\">\n"
1917 "<adhocList>\n%s</adhocList>\n"
1918 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1919 "<category name=\"calendarData\"/>\n"
1920 "<category name=\"contactCard\"/>\n"
1921 "<category name=\"note\"/>\n"
1922 "<category name=\"state\"/>\n"
1925 "</batchSub>", sip
->username
, resources_uri
);
1927 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1928 content_type
= "application/adrl+xml";
1929 content
= g_strdup_printf(
1930 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1931 "<create xmlns=\"\">\n%s</create>\n"
1932 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1934 g_free(resources_uri
);
1936 request
= g_strdup_printf(
1937 "Require: adhoclist%s\r\n"
1938 "Supported: eventlist\r\n"
1939 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1940 "Supported: ms-piggyback-first-notify\r\n"
1941 "%sSupported: ms-benotify\r\n"
1942 "Proxy-Require: ms-benotify\r\n"
1943 "Event: presence\r\n"
1944 "Content-Type: %s\r\n"
1945 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1948 /* subscribe to buddy presence */
1949 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1950 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1951 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1952 purple_debug_info("sipe", "sipe_subscribe_presence_batched_to: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1954 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1962 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1963 SIPE_UNUSED_PARAMETER
void *unused
)
1965 gchar
*to
= sip_uri_self(sip
);
1966 gchar
*resources_uri
= g_strdup("");
1968 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1970 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1973 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1976 struct presence_batched_routed
{
1981 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1983 struct presence_batched_routed
*data
= payload
;
1984 GSList
*buddies
= data
->buddies
;
1986 g_free(buddies
->data
);
1987 buddies
= buddies
->next
;
1989 g_slist_free(data
->buddies
);
1994 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1996 struct presence_batched_routed
*data
= payload
;
1997 GSList
*buddies
= data
->buddies
;
1998 gchar
*resources_uri
= g_strdup("");
2000 gchar
*tmp
= resources_uri
;
2001 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
2003 buddies
= buddies
->next
;
2005 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
2006 g_strdup(data
->host
));
2010 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
2011 * The user sends a single SUBSCRIBE request to the subscribed contact.
2012 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
2016 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
2020 gchar
*to
= sip_uri((char *)buddy_name
);
2021 gchar
*tmp
= get_contact(sip
);
2023 gchar
*content
= NULL
;
2024 gchar
*autoextend
= "";
2025 gchar
*content_type
= "";
2026 struct sip_dialog
*dialog
;
2027 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, to
);
2028 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
2030 if (sbuddy
) sbuddy
->just_added
= FALSE
;
2033 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
2035 autoextend
= "Supported: com.microsoft.autoextend\r\n";
2038 request
= g_strdup_printf(
2039 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
2040 "Supported: ms-piggyback-first-notify\r\n"
2041 "%s%sSupported: ms-benotify\r\n"
2042 "Proxy-Require: ms-benotify\r\n"
2043 "Event: presence\r\n"
2044 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
2047 content
= g_strdup_printf(
2048 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
2049 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
2050 "<resource uri=\"%s\"%s\n"
2052 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
2053 "<category name=\"calendarData\"/>\n"
2054 "<category name=\"contactCard\"/>\n"
2055 "<category name=\"note\"/>\n"
2056 "<category name=\"state\"/>\n"
2059 "</batchSub>", sip
->username
, to
, context
);
2064 /* subscribe to buddy presence */
2065 /* Subscription is identified by ACTION_NAME_PRESENCE key */
2066 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
2067 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2068 purple_debug_info("sipe", "sipe_subscribe_presence_single: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2070 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
2078 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
2080 purple_debug_info("sipe", "sipe_set_status: status=%s\n", purple_status_get_id(status
));
2082 if (!purple_status_is_active(status
))
2086 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
2091 time_t now
= time(NULL
);
2092 const char *status_id
= purple_status_get_id(status
);
2093 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
2094 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2095 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
2097 /* when other point of presence clears note, but we are keeping
2098 * state if OOF note.
2100 if (do_not_publish
&& !note
&& sip
->ews
&& sip
->ews
->oof_note
) {
2101 purple_debug_info("sipe", "sipe_set_status: enabling publication as OOF note keepers.\n");
2102 do_not_publish
= FALSE
;
2105 purple_debug_info("sipe", "sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d\n",
2106 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
2108 sip
->do_not_publish
[activity
] = 0;
2109 purple_debug_info("sipe", "sipe_set_status: set: sip->do_not_publish[%s]=%d [0]\n",
2110 status_id
, (int)sip
->do_not_publish
[activity
]);
2114 purple_debug_info("sipe", "sipe_set_status: publication was switched off, exiting.\n");
2118 g_free(sip
->status
);
2119 sip
->status
= g_strdup(status_id
);
2121 /* hack to escape apostrof before comparison */
2122 tmp
= note
? purple_strreplace(note
, "'", "'") : NULL
;
2124 /* this will preserve OOF flag as well */
2125 if (!sipe_strequal(tmp
, sip
->note
)) {
2126 sip
->is_oof_note
= FALSE
;
2128 sip
->note
= g_strdup(note
);
2129 sip
->note_since
= time(NULL
);
2133 /* schedule 2 sec to capture idle flag */
2134 action_name
= g_strdup_printf("<%s>", "+set-status");
2135 sipe_schedule_action(action_name
, SIPE_IDLE_SET_DELAY
, (Action
)send_presence_status
, NULL
, sip
, NULL
);
2136 g_free(action_name
);
2141 sipe_set_idle(PurpleConnection
* gc
,
2144 purple_debug_info("sipe", "sipe_set_idle: interval=%d\n", interval
);
2147 struct sipe_account_data
*sip
= gc
->proto_data
;
2150 sip
->idle_switch
= time(NULL
);
2151 purple_debug_info("sipe", "sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
2157 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
2158 SIPE_UNUSED_PARAMETER
const char *alias
)
2160 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2161 sipe_group_set_user(sip
, name
);
2165 sipe_group_buddy(PurpleConnection
*gc
,
2167 const char *old_group_name
,
2168 const char *new_group_name
)
2170 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2171 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
2172 struct sipe_group
* old_group
= NULL
;
2173 struct sipe_group
* new_group
;
2175 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
2176 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
2178 if(!buddy
) { // buddy not in roaming list
2182 if (old_group_name
) {
2183 old_group
= sipe_group_find_by_name(sip
, old_group_name
);
2185 new_group
= sipe_group_find_by_name(sip
, new_group_name
);
2188 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
2189 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
2193 sipe_group_create(sip
, new_group_name
, who
);
2195 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
2196 sipe_group_set_user(sip
, who
);
2200 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2202 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2204 /* libpurple can call us with undefined buddy or group */
2205 if (buddy
&& group
) {
2206 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2208 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2209 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
2210 purple_blist_rename_buddy(buddy
, buddy_name
);
2213 /* Prepend sip: if needed */
2214 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
2215 gchar
*buf
= sip_uri_from_name(buddy
->name
);
2216 purple_blist_rename_buddy(buddy
, buf
);
2220 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
2221 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
2222 purple_debug_info("sipe", "sipe_add_buddy: adding %s\n", buddy
->name
);
2223 b
->name
= g_strdup(buddy
->name
);
2224 b
->just_added
= TRUE
;
2225 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
2226 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
2227 /* @TODO should go to callback */
2228 sipe_subscribe_presence_single(sip
, b
->name
);
2230 purple_debug_info("sipe", "sipe_add_buddy: buddy %s already in internal list\n", buddy
->name
);
2235 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
2239 * We are calling g_hash_table_foreach_steal(). That means that no
2240 * key/value deallocation functions are called. Therefore the glib
2241 * hash code does not touch the key (buddy->name) or value (buddy)
2242 * of the to-be-deleted hash node at all. It follows that we
2244 * - MUST free the memory for the key ourselves and
2245 * - ARE allowed to do it in this function
2247 * Conclusion: glib must be broken on the Windows platform if sipe
2248 * crashes with SIGTRAP when closing. You'll have to live
2249 * with the memory leak until this is fixed.
2251 g_free(buddy
->name
);
2253 g_free(buddy
->activity
);
2254 g_free(buddy
->meeting_subject
);
2255 g_free(buddy
->meeting_location
);
2256 g_free(buddy
->note
);
2258 g_free(buddy
->cal_start_time
);
2259 g_free(buddy
->cal_free_busy_base64
);
2260 g_free(buddy
->cal_free_busy
);
2261 g_free(buddy
->last_non_cal_activity
);
2263 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
2265 g_free(buddy
->device_name
);
2266 g_slist_free(buddy
->groups
);
2271 * Unassociates buddy from group first.
2272 * Then see if no groups left, removes buddy completely.
2273 * Otherwise updates buddy groups on server.
2275 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2277 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2278 struct sipe_buddy
*b
;
2279 struct sipe_group
*g
= NULL
;
2281 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2284 b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
2288 g
= sipe_group_find_by_name(sip
, group
->name
);
2292 b
->groups
= g_slist_remove(b
->groups
, g
);
2293 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
2296 if (g_slist_length(b
->groups
) < 1) {
2297 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2298 sipe_cancel_scheduled_action(sip
, action_name
);
2299 g_free(action_name
);
2301 g_hash_table_remove(sip
->buddies
, buddy
->name
);
2304 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
2305 send_soap_request(sip
, body
);
2311 //updates groups on server
2312 sipe_group_set_user(sip
, b
->name
);
2318 sipe_rename_group(PurpleConnection
*gc
,
2319 const char *old_name
,
2321 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
2323 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2324 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, old_name
);
2326 sipe_group_rename(sip
, s_group
, group
->name
);
2328 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
2333 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
2335 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2336 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
2339 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
2340 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
2341 send_soap_request(sip
, body
);
2344 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
2345 g_free(s_group
->name
);
2348 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
2352 /** All statuses need message attribute to pass Note */
2353 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
2355 PurpleStatusType
*type
;
2356 GList
*types
= NULL
;
2358 /* Macros to reduce code repetition.
2359 Translators: noun */
2360 #define SIPE_ADD_STATUS(prim,id,name,user) type = purple_status_type_new_with_attrs( \
2362 TRUE, user, FALSE, \
2363 SIPE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
2365 types = g_list_append(types, type);
2368 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2374 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2375 sipe_activity_map
[SIPE_ACTIVITY_BUSY
].status_id
,
2376 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSY
),
2379 /* Do Not Disturb */
2380 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2381 sipe_activity_map
[SIPE_ACTIVITY_DND
].status_id
,
2386 /* Goes first in the list as
2387 * purple picks the first status with the AWAY type
2390 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2396 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2397 sipe_activity_map
[SIPE_ACTIVITY_BRB
].status_id
,
2398 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BRB
),
2401 /* Appear Offline */
2402 SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE
,
2408 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
,
2412 types
= g_list_append(types
, type
);
2418 * A callback for g_hash_table_foreach
2421 sipe_buddy_subscribe_cb(char *buddy_name
,
2422 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
2423 struct sipe_account_data
*sip
)
2425 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy_name
);
2426 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
2427 guint time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
2428 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
2430 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy_name
));
2431 g_free(action_name
);
2435 * Removes entries from purple buddy list
2436 * that does not correspond ones in the roaming contact list.
2438 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
2439 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
2440 GSList
*entry
= buddies
;
2441 struct sipe_buddy
*buddy
;
2445 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
2446 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
2449 g
= purple_buddy_get_group(b
);
2450 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
2452 gboolean in_sipe_groups
= FALSE
;
2453 GSList
*entry2
= buddy
->groups
;
2455 struct sipe_group
*group
= entry2
->data
;
2456 if (sipe_strequal(group
->name
, g
->name
)) {
2457 in_sipe_groups
= TRUE
;
2460 entry2
= entry2
->next
;
2462 if(!in_sipe_groups
) {
2463 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
2464 purple_blist_remove_buddy(b
);
2467 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
2468 purple_blist_remove_buddy(b
);
2470 entry
= entry
->next
;
2472 g_slist_free(buddies
);
2475 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2477 int len
= msg
->bodylen
;
2479 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
2482 const gchar
*contacts_delta
;
2483 xmlnode
*group_node
;
2484 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
2488 /* Convert the contact from XML to Purple Buddies */
2489 isc
= xmlnode_from_str(msg
->body
, len
);
2494 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
2495 if (contacts_delta
) {
2496 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2499 if (sipe_strequal(isc
->name
, "contactList")) {
2502 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
2503 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2504 const char *name
= xmlnode_get_attrib(group_node
, "name");
2506 if (g_str_has_prefix(name
, "~")) {
2507 name
= _("Other Contacts");
2509 group
->name
= g_strdup(name
);
2510 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2512 sipe_group_add(sip
, group
);
2515 // Make sure we have at least one group
2516 if (g_slist_length(sip
->groups
) == 0) {
2517 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2518 PurpleGroup
*purple_group
;
2519 group
->name
= g_strdup(_("Other Contacts"));
2521 purple_group
= purple_group_new(group
->name
);
2522 purple_blist_add_group(purple_group
, NULL
);
2523 sip
->groups
= g_slist_append(sip
->groups
, group
);
2526 /* Parse contacts */
2527 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2528 const gchar
*uri
= xmlnode_get_attrib(item
, "uri");
2529 const gchar
*name
= xmlnode_get_attrib(item
, "name");
2531 struct sipe_buddy
*buddy
= NULL
;
2533 gchar
**item_groups
;
2536 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2537 tmp
= sip_uri_from_name(uri
);
2538 buddy_name
= g_ascii_strdown(tmp
, -1);
2541 /* assign to group Other Contacts if nothing else received */
2542 tmp
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2544 struct sipe_group
*group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
2546 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
2548 item_groups
= g_strsplit(tmp
, " ", 0);
2551 while (item_groups
[i
]) {
2552 struct sipe_group
*group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2554 // If couldn't find the right group for this contact, just put them in the first group we have
2555 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2556 group
= sip
->groups
->data
;
2559 if (group
!= NULL
) {
2560 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2562 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2563 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2565 purple_debug_info("sipe", "Created new buddy %s with alias %s\n", buddy_name
, uri
);
2568 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2569 if (name
!= NULL
&& strlen(name
) != 0) {
2570 purple_blist_alias_buddy(b
, name
);
2572 purple_debug_info("sipe", "Replaced buddy %s alias with %s\n", buddy_name
, name
);
2577 buddy
= g_new0(struct sipe_buddy
, 1);
2578 buddy
->name
= g_strdup(b
->name
);
2579 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2582 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2584 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2586 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2591 } // while, contact groups
2592 g_strfreev(item_groups
);
2597 sipe_cleanup_local_blist(sip
);
2599 /* Add self-contact if not there yet. 2005 systems. */
2600 /* This will resemble subscription to roaming_self in 2007 systems */
2601 if (!sip
->ocs2007
) {
2602 gchar
*self_uri
= sip_uri_self(sip
);
2603 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, self_uri
);
2606 buddy
= g_new0(struct sipe_buddy
, 1);
2607 buddy
->name
= g_strdup(self_uri
);
2608 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2615 /* subscribe to buddies */
2616 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2617 if (sip
->batched_support
) {
2618 sipe_subscribe_presence_batched(sip
, NULL
);
2620 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2622 sip
->subscribed_buddies
= TRUE
;
2624 /* for 2005 systems schedule contacts' status update
2625 * based on their calendar information
2627 if (!sip
->ocs2007
) {
2628 sipe_sched_calendar_status_update(sip
, time(NULL
));
2635 * Subscribe roaming contacts
2637 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2639 gchar
*to
= sip_uri_self(sip
);
2640 gchar
*tmp
= get_contact(sip
);
2641 gchar
*hdr
= g_strdup_printf(
2642 "Event: vnd-microsoft-roaming-contacts\r\n"
2643 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2644 "Supported: com.microsoft.autoextend\r\n"
2645 "Supported: ms-benotify\r\n"
2646 "Proxy-Require: ms-benotify\r\n"
2647 "Supported: ms-piggyback-first-notify\r\n"
2648 "Contact: %s\r\n", tmp
);
2651 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2656 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2657 SIPE_UNUSED_PARAMETER
void *unused
)
2660 struct sip_dialog
*dialog
;
2661 gchar
*to
= sip_uri_self(sip
);
2662 gchar
*tmp
= get_contact(sip
);
2663 gchar
*hdr
= g_strdup_printf(
2664 "Event: presence.wpending\r\n"
2665 "Accept: text/xml+msrtc.wpending\r\n"
2666 "Supported: com.microsoft.autoextend\r\n"
2667 "Supported: ms-benotify\r\n"
2668 "Proxy-Require: ms-benotify\r\n"
2669 "Supported: ms-piggyback-first-notify\r\n"
2670 "Contact: %s\r\n", tmp
);
2673 /* Subscription is identified by <event> key */
2674 key
= g_strdup_printf("<%s>", "presence.wpending");
2675 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2676 purple_debug_info("sipe", "sipe_subscribe_presence_wpending: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2678 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", dialog
, process_subscribe_response
);
2686 * Fires on deregistration event initiated by server.
2687 * [MS-SIPREGE] SIP extension.
2692 // Content-Type: text/registration-event
2693 // subscription-state: terminated;expires=0
2694 // ms-diagnostics-public: 4141;reason="User disabled"
2696 // deregistered;event=rejected
2698 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2700 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2701 gchar
*event
= NULL
;
2702 gchar
*reason
= NULL
;
2703 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
2706 diagnostics
= diagnostics
? diagnostics
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2707 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2709 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2710 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2711 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2712 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2714 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2718 if (diagnostics
!= NULL
) {
2719 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
2720 } else { // for LCS2005
2722 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2723 error_id
= 4140; // [MS-SIPREGE]
2724 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2725 reason
= g_strdup(_("you are already signed in at another location"));
2726 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2728 reason
= g_strdup(_("user disabled")); // [MS-OCER]
2729 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2731 reason
= g_strdup(_("user moved")); // [MS-OCER]
2735 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
2738 sip
->gc
->wants_to_die
= TRUE
;
2739 purple_connection_error(sip
->gc
, warning
);
2744 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2746 xmlnode
*xn_provision_group_list
;
2749 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2751 /* provisionGroup */
2752 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2753 if (sipe_strequal("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2754 g_free(sip
->focus_factory_uri
);
2755 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2756 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2757 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2761 xmlnode_free(xn_provision_group_list
);
2764 /** for 2005 system */
2766 sipe_process_provisioning(struct sipe_account_data
*sip
,
2769 xmlnode
*xn_provision
;
2772 xn_provision
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2773 if ((node
= xmlnode_get_child(xn_provision
, "user"))) {
2774 purple_debug_info("sipe", "sipe_process_provisioning: uri=%s\n", xmlnode_get_attrib(node
, "uri"));
2775 if ((node
= xmlnode_get_child(node
, "line"))) {
2776 const gchar
*line_uri
= xmlnode_get_attrib(node
, "uri");
2777 const gchar
*server
= xmlnode_get_attrib(node
, "server");
2778 purple_debug_info("sipe", "sipe_process_provisioning: line_uri=%s server=%s\n", line_uri
, server
);
2779 sip_csta_open(sip
, line_uri
, server
);
2782 xmlnode_free(xn_provision
);
2785 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2787 const gchar
*contacts_delta
;
2790 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2796 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2799 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2806 free_container(struct sipe_container
*container
)
2810 if (!container
) return;
2812 entry
= container
->members
;
2814 void *data
= entry
->data
;
2815 entry
= g_slist_remove(entry
, data
);
2822 * Finds locally stored MS-PRES container member
2824 static struct sipe_container_member
*
2825 sipe_find_container_member(struct sipe_container
*container
,
2829 struct sipe_container_member
*member
;
2832 if (container
== NULL
|| type
== NULL
) {
2836 entry
= container
->members
;
2838 member
= entry
->data
;
2839 if (!g_strcasecmp(member
->type
, type
)
2840 && ((!member
->value
&& !value
)
2841 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2845 entry
= entry
->next
;
2851 * Finds locally stored MS-PRES container by id
2853 static struct sipe_container
*
2854 sipe_find_container(struct sipe_account_data
*sip
,
2857 struct sipe_container
*container
;
2864 entry
= sip
->containers
;
2866 container
= entry
->data
;
2867 if (id
== container
->id
) {
2870 entry
= entry
->next
;
2884 sipe_find_access_level(struct sipe_account_data
*sip
,
2888 guint containers
[] = {32000, 400, 300, 200, 100};
2891 for (i
= 0; i
< 5; i
++) {
2892 struct sipe_container_member
*member
;
2893 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2894 if (!container
) continue;
2896 member
= sipe_find_container_member(container
, type
, value
);
2898 return containers
[i
];
2906 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2908 guint container_version
,
2909 const gchar
* action
,
2913 gchar
*self
= sip_uri_self(sip
);
2914 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2917 gchar
*body
= g_strdup_printf(
2918 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2919 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2920 "</setContainerMembers>",
2928 contact
= get_contact(sip
);
2929 hdr
= g_strdup_printf("Contact: %s\r\n"
2930 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2933 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2941 free_publication(struct sipe_publication
*publication
)
2943 g_free(publication
->category
);
2944 g_free(publication
->cal_event_hash
);
2945 g_free(publication
->note
);
2947 g_free(publication
->working_hours_xml_str
);
2948 g_free(publication
->fb_start_str
);
2949 g_free(publication
->free_busy_base64
);
2951 g_free(publication
);
2954 /* key is <category><instance><container> */
2956 sipe_is_our_publication(struct sipe_account_data
*sip
,
2961 /* filling keys for our publications if not yet cached */
2962 if (!sip
->our_publication_keys
) {
2963 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2964 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2965 guint user_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
);
2966 guint calendar_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
2967 guint cal_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
);
2968 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
2969 guint note_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
);
2971 purple_debug_info("sipe", "* Our Publication Instances *\n");
2972 purple_debug_info("sipe", "\tDevice : %u\t0x%08X\n", device_instance
, device_instance
);
2973 purple_debug_info("sipe", "\tMachine State : %u\t0x%08X\n", machine_instance
, machine_instance
);
2974 purple_debug_info("sipe", "\tUser Stare : %u\t0x%08X\n", user_instance
, user_instance
);
2975 purple_debug_info("sipe", "\tCalendar State : %u\t0x%08X\n", calendar_instance
, calendar_instance
);
2976 purple_debug_info("sipe", "\tCalendar OOF State : %u\t0x%08X\n", cal_oof_instance
, cal_oof_instance
);
2977 purple_debug_info("sipe", "\tCalendar FreeBusy : %u\t0x%08X\n", cal_data_instance
, cal_data_instance
);
2978 purple_debug_info("sipe", "\tOOF Note : %u\t0x%08X\n", note_oof_instance
, note_oof_instance
);
2979 purple_debug_info("sipe", "\tNote : %u\n", 0);
2980 purple_debug_info("sipe", "\tCalendar WorkingHours: %u\n", 0);
2983 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2984 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2986 /* state:machineState */
2987 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2988 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2989 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2990 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2992 /* state:userState */
2993 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2994 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2995 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2996 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2998 /* state:calendarState */
2999 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3000 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
3001 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3002 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
3004 /* state:calendarState OOF */
3005 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3006 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
3007 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3008 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
3011 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3012 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
3013 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3014 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
3015 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3016 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
3019 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3020 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
3021 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3022 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
3023 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3024 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
3026 /* calendarData:WorkingHours */
3027 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3028 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
3029 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3030 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
3031 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3032 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
3033 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3034 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
3035 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3036 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
3037 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3038 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
3040 /* calendarData:FreeBusy */
3041 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3042 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
3043 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3044 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
3045 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3046 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
3047 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3048 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
3049 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3050 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
3051 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3052 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
3054 //purple_debug_info("sipe", "sipe_is_our_publication: sip->our_publication_keys length=%d\n",
3055 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
3058 //purple_debug_info("sipe", "sipe_is_our_publication: key=%s\n", key);
3060 entry
= sip
->our_publication_keys
;
3062 //purple_debug_info("sipe", " sipe_is_our_publication: entry->data=%s\n", entry->data);
3063 if (sipe_strequal(entry
->data
, key
)) {
3066 entry
= entry
->next
;
3071 /** Property names to store in blist.xml */
3072 #define ALIAS_PROP "alias"
3073 #define EMAIL_PROP "email"
3074 #define PHONE_PROP "phone"
3075 #define PHONE_DISPLAY_PROP "phone-display"
3076 #define PHONE_MOBILE_PROP "phone-mobile"
3077 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
3078 #define PHONE_HOME_PROP "phone-home"
3079 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
3080 #define PHONE_OTHER_PROP "phone-other"
3081 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
3082 #define PHONE_CUSTOM1_PROP "phone-custom1"
3083 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
3084 #define SITE_PROP "site"
3085 #define COMPANY_PROP "company"
3086 #define DEPARTMENT_PROP "department"
3087 #define TITLE_PROP "title"
3088 #define OFFICE_PROP "office"
3089 /** implies work address */
3090 #define ADDRESS_STREET_PROP "address-street"
3091 #define ADDRESS_CITY_PROP "address-city"
3092 #define ADDRESS_STATE_PROP "address-state"
3093 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
3094 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
3097 * Tries to figure out user first and last name
3098 * based on Display Name and email properties.
3100 * Allocates memory - must be g_free()'d
3102 * Examples to parse:
3104 * First Last - Company Name
3107 * Last, First (C)(STP) (Company)
3108 * first.last@company.com (preprocessed as "first last")
3109 * first.last.company.com@reuters.net (preprocessed as "first last company com")
3111 * Unusable examples:
3112 * user@company.com (preprocessed as "user")
3113 * first.m.last@company.com (preprocessed as "first m last")
3114 * user.company.com@reuters.net (preprocessed as "user company com")
3117 sipe_get_first_last_names(struct sipe_account_data
*sip
,
3122 PurpleBuddy
*p_buddy
;
3125 const char *first
, *last
;
3128 gboolean has_comma
= FALSE
;
3130 if (!sip
|| !uri
) return;
3132 p_buddy
= purple_find_buddy(sip
->account
, uri
);
3134 if (!p_buddy
) return;
3136 display_name
= g_strdup(purple_buddy_get_alias(p_buddy
));
3137 email
= purple_blist_node_get_string(&p_buddy
->node
, EMAIL_PROP
);
3139 if (!display_name
&& !email
) return;
3141 /* if no display name, make "first last anything_else" out of email */
3142 if (email
&& !display_name
) {
3143 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
3144 display_name
= purple_strreplace((tmp
= display_name
), ".", " ");
3149 has_comma
= (strstr(display_name
, ",") != NULL
);
3150 display_name
= purple_strreplace((tmp
= display_name
), ", ", " ");
3152 display_name
= purple_strreplace((tmp
= display_name
), ",", " ");
3156 parts
= g_strsplit(display_name
, " ", 0);
3158 if (!parts
[0] || !parts
[1]) {
3159 g_free(display_name
);
3173 *first_name
= g_strstrip(g_strdup(first
));
3177 *last_name
= g_strstrip(g_strdup(last
));
3180 g_free(display_name
);
3185 * Update user information
3187 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3188 * @param property_name
3189 * @param property_value may be modified to strip white space
3192 sipe_update_user_info(struct sipe_account_data
*sip
,
3194 const char *property_name
,
3195 char *property_value
)
3197 GSList
*buddies
, *entry
;
3199 if (!property_name
|| strlen(property_name
) == 0) return;
3202 property_value
= g_strstrip(property_value
);
3204 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
3206 const char *prop_str
;
3207 const char *server_alias
;
3208 PurpleBuddy
*p_buddy
= entry
->data
;
3210 /* for Display Name */
3211 if (sipe_strequal(property_name
, ALIAS_PROP
)) {
3212 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
3213 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, property_value
);
3214 purple_blist_alias_buddy(p_buddy
, property_value
);
3217 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3218 if (!is_empty(property_value
) &&
3219 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
3221 purple_blist_server_alias_buddy(p_buddy
, property_value
);
3224 /* for other properties */
3226 if (!is_empty(property_value
)) {
3227 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
3228 if (!prop_str
|| g_ascii_strcasecmp(prop_str
, property_value
)) {
3229 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
3234 entry
= entry
->next
;
3236 g_slist_free(buddies
);
3241 * Suitable for both 2005 and 2007 systems.
3243 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3245 * @param phone may be modified to strip white space
3246 * @param phone_display_string may be modified to strip white space
3249 sipe_update_user_phone(struct sipe_account_data
*sip
,
3251 const gchar
*phone_type
,
3253 gchar
*phone_display_string
)
3255 const char *phone_node
= PHONE_PROP
; /* work phone by default */
3256 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
3258 if(!phone
|| strlen(phone
) == 0) return;
3260 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
3261 phone_node
= PHONE_MOBILE_PROP
;
3262 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
3263 } else if (sipe_strequal(phone_type
, "home")) {
3264 phone_node
= PHONE_HOME_PROP
;
3265 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
3266 } else if (sipe_strequal(phone_type
, "other")) {
3267 phone_node
= PHONE_OTHER_PROP
;
3268 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
3269 } else if (sipe_strequal(phone_type
, "custom1")) {
3270 phone_node
= PHONE_CUSTOM1_PROP
;
3271 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
3274 sipe_update_user_info(sip
, uri
, phone_node
, phone
);
3275 if (phone_display_string
) {
3276 sipe_update_user_info(sip
, uri
, phone_display_node
, phone_display_string
);
3281 sipe_update_calendar(struct sipe_account_data
*sip
)
3283 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
3285 purple_debug_info("sipe", "sipe_update_calendar: started.\n");
3287 if (sipe_strequal(calendar
, "EXCH")) {
3288 sipe_ews_update_calendar(sip
);
3291 /* schedule repeat */
3292 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_INTERVAL
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3294 purple_debug_info("sipe", "sipe_update_calendar: finished.\n");
3298 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
3299 * by using standard Purple's means of signals and saved statuses.
3301 * Thus all UI elements get updated: Status Button with Note, docklet.
3302 * This is ablolutely important as both our status and note can come
3303 * inbound (roaming) or be updated programmatically (e.g. based on our
3307 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
3308 const char *status_id
,
3309 const char *message
,
3310 time_t do_not_publish
[])
3312 PurpleStatus
*status
= purple_account_get_active_status(account
);
3313 gboolean changed
= TRUE
;
3315 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
3316 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
3321 if (purple_savedstatus_is_idleaway()) {
3326 PurpleSavedStatus
*saved_status
;
3327 const PurpleStatusType
*acct_status_type
=
3328 purple_status_type_find_with_id(account
->status_types
, status_id
);
3329 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
3330 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
3332 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
3334 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
3337 /* If this type+message is unique then create a new transient saved status
3338 * Ref: gtkstatusbox.c
3340 if (!saved_status
) {
3342 GList
*active_accts
= purple_accounts_get_all_active();
3344 saved_status
= purple_savedstatus_new(NULL
, primitive
);
3345 purple_savedstatus_set_message(saved_status
, message
);
3347 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
3348 purple_savedstatus_set_substatus(saved_status
,
3349 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
3351 g_list_free(active_accts
);
3354 do_not_publish
[activity
] = time(NULL
);
3355 purple_debug_info("sipe", "sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]\n",
3356 status_id
, (int)do_not_publish
[activity
]);
3358 /* Set the status for each account */
3359 purple_savedstatus_activate(saved_status
);
3363 struct hash_table_delete_payload
{
3364 GHashTable
*hash_table
;
3369 sipe_remove_category_container_publications_cb(const char *name
,
3370 struct sipe_publication
*publication
,
3371 struct hash_table_delete_payload
*payload
)
3373 if (publication
->container
== payload
->container
) {
3374 g_hash_table_remove(payload
->hash_table
, name
);
3378 sipe_remove_category_container_publications(GHashTable
*our_publications
,
3379 const char *category
,
3382 struct hash_table_delete_payload payload
;
3383 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
3385 if (!payload
.hash_table
) return;
3387 payload
.container
= container
;
3388 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
3392 send_publish_category_initial(struct sipe_account_data
*sip
);
3395 * When we receive some self (BE) NOTIFY with a new subscriber
3396 * we sends a setSubscribers request to him [SIP-PRES] 4.8
3399 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3406 char *display_name
= NULL
;
3408 GSList
*category_names
= NULL
;
3409 int aggreg_avail
= 0;
3410 static sipe_activity aggreg_activity
= SIPE_ACTIVITY_UNSET
;
3411 gboolean do_update_status
= FALSE
;
3412 gboolean has_note_cleaned
= FALSE
;
3414 purple_debug_info("sipe", "sipe_process_roaming_self\n");
3416 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3419 contact
= get_contact(sip
);
3420 to
= sip_uri_self(sip
);
3424 /* set list of categories participating in this XML */
3425 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3426 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3427 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
3429 purple_debug_info("sipe", "sipe_process_roaming_self: category_names length=%d\n",
3430 category_names
? (int) g_slist_length(category_names
) : -1);
3431 /* drop category information */
3432 if (category_names
) {
3433 GSList
*entry
= category_names
;
3435 GHashTable
*cat_publications
;
3436 const gchar
*category
= entry
->data
;
3437 entry
= entry
->next
;
3438 purple_debug_info("sipe", "sipe_process_roaming_self: dropping category: %s\n", category
);
3439 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
3440 if (cat_publications
) {
3441 g_hash_table_remove(sip
->our_publications
, category
);
3442 purple_debug_info("sipe", " sipe_process_roaming_self: dropped category: %s\n", category
);
3446 g_slist_free(category_names
);
3447 /* filling our categories reflected in roaming data */
3448 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3450 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3451 guint container
= xmlnode_get_int_attrib(node
, "container", -1);
3452 guint instance
= xmlnode_get_int_attrib(node
, "instance", -1);
3453 guint version
= xmlnode_get_int_attrib(node
, "version", 0);
3454 time_t publish_time
= (tmp
= xmlnode_get_attrib(node
, "publishTime")) ?
3455 sipe_utils_str_to_time(tmp
) : 0;
3457 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
3459 /* Ex. clear note: <category name="note"/> */
3460 if (container
== (guint
)-1) {
3463 do_update_status
= TRUE
;
3467 /* Ex. clear note: <category name="note" container="200"/> */
3468 if (instance
== (guint
)-1) {
3469 if (container
== 200) {
3472 do_update_status
= TRUE
;
3474 purple_debug_info("sipe", "sipe_process_roaming_self: removing publications for: %s/%u\n", name
, container
);
3475 sipe_remove_category_container_publications(
3476 sip
->our_publications
, name
, container
);
3480 /* key is <category><instance><container> */
3481 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
3482 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version
);
3484 /* capture all userState publication for later clean up if required */
3485 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
3486 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3488 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "userState")) {
3489 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3490 publication
->category
= g_strdup(name
);
3491 publication
->instance
= instance
;
3492 publication
->container
= container
;
3493 publication
->version
= version
;
3495 if (!sip
->user_state_publications
) {
3496 sip
->user_state_publications
= g_hash_table_new_full(
3497 g_str_hash
, g_str_equal
,
3498 g_free
, (GDestroyNotify
)free_publication
);
3500 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
3501 purple_debug_info("sipe", "sipe_process_roaming_self: added to user_state_publications key=%s version=%d\n",
3506 if (sipe_is_our_publication(sip
, key
)) {
3507 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3509 publication
->category
= g_strdup(name
);
3510 publication
->instance
= instance
;
3511 publication
->container
= container
;
3512 publication
->version
= version
;
3514 /* filling publication->availability */
3515 if (sipe_strequal(name
, "state")) {
3516 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3517 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3520 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3522 publication
->availability
= atoi(avail_str
);
3526 /* for calendarState */
3527 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "calendarState")) {
3528 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3529 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
3531 event
->start_time
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "startTime"));
3533 if (sipe_strequal(xmlnode_get_attrib(xn_activity
, "token"),
3534 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
3536 event
->is_meeting
= TRUE
;
3539 event
->subject
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingSubject"));
3540 event
->location
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingLocation"));
3542 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
3543 purple_debug_info("sipe", "sipe_process_roaming_self: hash=%s\n",
3544 publication
->cal_event_hash
);
3545 sipe_cal_event_free(event
);
3548 /* filling publication->note */
3549 if (sipe_strequal(name
, "note")) {
3550 xmlnode
*xn_body
= xmlnode_get_descendant(node
, "note", "body", NULL
);
3552 if (!has_note_cleaned
) {
3553 has_note_cleaned
= TRUE
;
3557 sip
->note_since
= publish_time
;
3559 do_update_status
= TRUE
;
3562 g_free(publication
->note
);
3563 publication
->note
= NULL
;
3567 publication
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_body
)), -1);
3569 if (publish_time
>= sip
->note_since
) {
3571 sip
->note
= g_strdup(publication
->note
);
3572 sip
->note_since
= publish_time
;
3573 sip
->is_oof_note
= sipe_strequal(xmlnode_get_attrib(xn_body
, "type"), "OOF");
3575 do_update_status
= TRUE
;
3580 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
3581 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
3582 xmlnode
*xn_free_busy
= xmlnode_get_descendant(node
, "calendarData", "freeBusy", NULL
);
3583 xmlnode
*xn_working_hours
= xmlnode_get_descendant(node
, "calendarData", "WorkingHours", NULL
);
3585 publication
->fb_start_str
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
3586 publication
->free_busy_base64
= xmlnode_get_data(xn_free_busy
);
3588 if (xn_working_hours
) {
3589 publication
->working_hours_xml_str
= xmlnode_to_str(xn_working_hours
, NULL
);
3593 if (!cat_publications
) {
3594 cat_publications
= g_hash_table_new_full(
3595 g_str_hash
, g_str_equal
,
3596 g_free
, (GDestroyNotify
)free_publication
);
3597 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
3598 purple_debug_info("sipe", "sipe_process_roaming_self: added GHashTable cat=%s\n", name
);
3600 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
3601 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version
);
3605 /* aggregateState (not an our publication) from 2-nd container */
3606 if (sipe_strequal(name
, "state") && container
== 2) {
3607 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3609 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "aggregateState")) {
3610 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3611 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3614 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3616 aggreg_avail
= atoi(avail_str
);
3622 const char *activity_token
= xmlnode_get_attrib(xn_activity
, "token");
3624 aggreg_activity
= sipe_get_activity_by_token(activity_token
);
3627 do_update_status
= TRUE
;
3631 /* userProperties published by server from AD */
3632 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
3634 /* line, for Remote Call Control (RCC) */
3635 for (line
= xmlnode_get_descendant(node
, "userProperties", "lines", "line", NULL
); line
; line
= xmlnode_get_next_twin(line
)) {
3636 const gchar
*line_server
= xmlnode_get_attrib(line
, "lineServer");
3637 const gchar
*line_type
= xmlnode_get_attrib(line
, "lineType");
3640 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
3642 line_uri
= xmlnode_get_data(line
);
3644 purple_debug_info("sipe", "sipe_process_roaming_self: line_uri=%s server=%s\n", line_uri
, line_server
);
3645 sip_csta_open(sip
, line_uri
, line_server
);
3653 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
3654 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
3657 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3658 guint id
= xmlnode_get_int_attrib(node
, "id", 0);
3659 struct sipe_container
*container
= sipe_find_container(sip
, id
);
3662 sip
->containers
= g_slist_remove(sip
->containers
, container
);
3663 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
3664 free_container(container
);
3666 container
= g_new0(struct sipe_container
, 1);
3668 container
->version
= xmlnode_get_int_attrib(node
, "version", 0);
3669 sip
->containers
= g_slist_append(sip
->containers
, container
);
3670 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
3672 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
3673 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
3674 member
->type
= xmlnode_get_attrib(node2
, "type");
3675 member
->value
= xmlnode_get_attrib(node2
, "value");
3676 container
->members
= g_slist_append(container
->members
, member
);
3677 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
3678 member
->type
, member
->value
? member
->value
: "");
3682 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
3683 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
3684 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
3685 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
3686 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
3687 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
3688 /* initial set-up to let counterparties see your status */
3689 if (sameEnterpriseAL
< 0) {
3690 struct sipe_container
*container
= sipe_find_container(sip
, 200);
3691 guint version
= container
? container
->version
: 0;
3692 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
3694 if (federatedAL
< 0) {
3695 struct sipe_container
*container
= sipe_find_container(sip
, 100);
3696 guint version
= container
? container
->version
: 0;
3697 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
3699 sip
->access_level_set
= TRUE
;
3703 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3705 const char *acknowledged
;
3709 user
= xmlnode_get_attrib(node
, "user"); /* without 'sip:' prefix */
3710 if (!user
) continue;
3711 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
3712 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
3713 uri
= sip_uri_from_name(user
);
3715 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
3717 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
3718 if(!g_ascii_strcasecmp(acknowledged
,"false")){
3719 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
3720 if (!purple_find_buddy(sip
->account
, uri
)) {
3721 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
3724 hdr
= g_strdup_printf(
3726 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
3728 body
= g_strdup_printf(
3729 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
3730 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
3731 "</setSubscribers>", user
);
3733 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
3737 g_free(display_name
);
3744 /* Publish initial state if not yet.
3745 * Assuming this happens on initial responce to subscription to roaming-self
3746 * so we've already updated our roaming data in full.
3749 if (!sip
->initial_state_published
) {
3750 send_publish_category_initial(sip
);
3751 sip
->initial_state_published
= TRUE
;
3753 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3754 do_update_status
= FALSE
;
3755 } else if (aggreg_avail
) {
3757 g_free(sip
->status
);
3758 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
3759 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
3761 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
3765 if (do_update_status
) {
3766 purple_debug_info("sipe", "sipe_process_roaming_self: switch to '%s' for the account\n", sip
->status
);
3767 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
3773 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
3775 gchar
*to
= sip_uri_self(sip
);
3776 gchar
*tmp
= get_contact(sip
);
3777 gchar
*hdr
= g_strdup_printf(
3778 "Event: vnd-microsoft-roaming-ACL\r\n"
3779 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
3780 "Supported: com.microsoft.autoextend\r\n"
3781 "Supported: ms-benotify\r\n"
3782 "Proxy-Require: ms-benotify\r\n"
3783 "Supported: ms-piggyback-first-notify\r\n"
3784 "Contact: %s\r\n", tmp
);
3787 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
3793 * To request for presence information about the user, access level settings that have already been configured by the user
3794 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
3795 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
3798 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
3800 gchar
*to
= sip_uri_self(sip
);
3801 gchar
*tmp
= get_contact(sip
);
3802 gchar
*hdr
= g_strdup_printf(
3803 "Event: vnd-microsoft-roaming-self\r\n"
3804 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
3805 "Supported: ms-benotify\r\n"
3806 "Proxy-Require: ms-benotify\r\n"
3807 "Supported: ms-piggyback-first-notify\r\n"
3809 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
3811 gchar
*body
=g_strdup(
3812 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
3813 "<roaming type=\"categories\"/>"
3814 "<roaming type=\"containers\"/>"
3815 "<roaming type=\"subscribers\"/></roamingList>");
3818 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3827 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
3829 gchar
*to
= sip_uri_self(sip
);
3830 gchar
*tmp
= get_contact(sip
);
3831 gchar
*hdr
= g_strdup_printf(
3832 "Event: vnd-microsoft-provisioning\r\n"
3833 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
3834 "Supported: com.microsoft.autoextend\r\n"
3835 "Supported: ms-benotify\r\n"
3836 "Proxy-Require: ms-benotify\r\n"
3837 "Supported: ms-piggyback-first-notify\r\n"
3839 "Contact: %s\r\n", tmp
);
3842 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
3847 /** Subscription for provisioning information to help with initial
3848 * configuration. This subscription is a one-time query (denoted by the Expires header,
3849 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
3850 * configuration, meeting policies, and policy settings that Communicator must enforce.
3851 * TODO: for what we need this information.
3854 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
3856 gchar
*to
= sip_uri_self(sip
);
3857 gchar
*tmp
= get_contact(sip
);
3858 gchar
*hdr
= g_strdup_printf(
3859 "Event: vnd-microsoft-provisioning-v2\r\n"
3860 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
3861 "Supported: com.microsoft.autoextend\r\n"
3862 "Supported: ms-benotify\r\n"
3863 "Proxy-Require: ms-benotify\r\n"
3864 "Supported: ms-piggyback-first-notify\r\n"
3867 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
3868 gchar
*body
= g_strdup(
3869 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
3870 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
3871 "<provisioningGroup name=\"ucPolicy\"/>"
3872 "</provisioningGroupList>");
3875 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3882 sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
3883 gpointer value
, gpointer user_data
)
3885 struct sip_subscription
*subscription
= value
;
3886 struct sip_dialog
*dialog
= &subscription
->dialog
;
3887 struct sipe_account_data
*sip
= user_data
;
3888 gchar
*tmp
= get_contact(sip
);
3889 gchar
*hdr
= g_strdup_printf(
3892 "Contact: %s\r\n", subscription
->event
, tmp
);
3895 /* Rate limit to max. 25 requests per seconds */
3896 g_usleep(1000000 / 25);
3898 send_sip_request(sip
->gc
, "SUBSCRIBE", dialog
->with
, dialog
->with
, hdr
, NULL
, dialog
, NULL
);
3902 /* IM Session (INVITE and MESSAGE methods) */
3904 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
3906 get_end_points (struct sipe_account_data
*sip
,
3907 struct sip_session
*session
)
3911 if (session
== NULL
) {
3915 res
= g_strdup_printf("<sip:%s>", sip
->username
);
3917 SIPE_DIALOG_FOREACH
{
3919 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
3922 if (dialog
->theirepid
) {
3924 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
3927 } SIPE_DIALOG_FOREACH_END
;
3933 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
3935 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3937 gboolean ret
= TRUE
;
3939 if (msg
->response
!= 200) {
3940 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
3944 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
3950 * Asks UA/proxy about its capabilities.
3952 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
3954 gchar
*to
= sip_uri(who
);
3955 gchar
*contact
= get_contact(sip
);
3956 gchar
*request
= g_strdup_printf(
3957 "Accept: application/sdp\r\n"
3958 "Contact: %s\r\n", contact
);
3961 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
3968 sipe_notify_user(struct sipe_account_data
*sip
,
3969 struct sip_session
*session
,
3970 PurpleMessageFlags flags
,
3971 const gchar
*message
)
3973 PurpleConversation
*conv
;
3975 if (!session
->conv
) {
3976 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
3978 conv
= session
->conv
;
3980 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
3984 sipe_present_info(struct sipe_account_data
*sip
,
3985 struct sip_session
*session
,
3986 const gchar
*message
)
3988 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
3992 sipe_present_err(struct sipe_account_data
*sip
,
3993 struct sip_session
*session
,
3994 const gchar
*message
)
3996 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
4000 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
4001 struct sip_session
*session
,
4005 const gchar
*message
)
4007 char *msg
, *msg_tmp
, *msg_tmp2
;
4010 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
4011 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
4013 /* Service unavailable; Server Internal Error; Server Time-out */
4014 if (sip_error
== 606 && sip_warning
== 309) { /* Not acceptable all. */ /* Message contents not allowed by policy */
4015 label
= _("Your message or invitation was not delivered, possibly because it contains a hyperlink or other content that the system administrator has blocked.");
4018 } else if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
4019 label
= _("This message was not delivered to %s because the service is not available");
4020 } else if (sip_error
== 486) { /* Busy Here */
4021 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
4022 } else if (sip_error
== 415) { /* Unsupported media type */
4023 label
= _("This message was not delivered to %s because one or more recipients don't support this type of message");
4025 label
= _("This message was not delivered to %s because one or more recipients are offline");
4028 msg_tmp
= g_strdup_printf( "%s%s\n%s" ,
4029 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""),
4032 sipe_present_err(sip
, session
, msg_tmp
);
4040 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4041 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4043 gboolean ret
= TRUE
;
4044 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4045 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
4046 struct sip_dialog
*dialog
;
4049 struct queued_message
*message
;
4052 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
4057 dialog
= sipe_dialog_find(session
, with
);
4059 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
4064 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4065 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
4067 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4069 if (msg
->response
>= 400) {
4070 PurpleBuddy
*pbuddy
;
4071 const char *alias
= with
;
4072 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
4075 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
4078 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
4080 warning
= atoi(parts
[0]);
4085 /* cancel file transfer as rejected by server */
4086 if (msg
->response
== 606 && /* Not acceptable all. */
4087 warning
== 309 && /* Message contents not allowed by policy */
4088 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
4090 GSList
*parsed_body
= sipe_ft_parse_msg_body(msg
->body
);
4091 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4092 sipe_utils_nameval_free(parsed_body
);
4095 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4096 alias
= purple_buddy_get_alias(pbuddy
);
4099 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, warning
, alias
, (message
? message
->body
: NULL
));
4101 /* drop dangling IM sessions: assume that BYE from remote never reached us */
4102 if (msg
->response
== 408 || /* Request timeout */
4103 msg
->response
== 480 || /* Temporarily Unavailable */
4104 msg
->response
== 481) { /* Call/Transaction Does Not Exist */
4105 purple_debug_info("sipe", "process_message_response: assuming dangling IM session, dropping it.\n");
4106 send_sip_request(sip
->gc
, "BYE", with
, with
, NULL
, NULL
, dialog
, NULL
);
4111 const gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
4113 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
->body
));
4114 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
4115 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
4118 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4119 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
4120 key
, g_hash_table_size(session
->unconfirmed_messages
));
4126 if (ret
) sipe_im_process_queue(sip
, session
);
4131 sipe_is_election_finished(struct sip_session
*session
);
4134 sipe_election_result(struct sipe_account_data
*sip
,
4138 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4139 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4141 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4142 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4143 struct sip_dialog
*dialog
;
4144 struct sip_session
*session
;
4146 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4148 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
4152 if (msg
->response
== 200 && g_str_has_prefix(contenttype
, "application/x-ms-mim")) {
4153 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4154 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
4155 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
4157 if (xn_request_rm_response
) {
4158 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
4159 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
4161 dialog
= sipe_dialog_find(session
, with
);
4163 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
4164 xmlnode_free(xn_action
);
4168 if (allow
&& !g_strcasecmp(allow
, "true")) {
4169 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
4170 dialog
->election_vote
= 1;
4171 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
4172 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
4173 dialog
->election_vote
= -1;
4176 if (sipe_is_election_finished(session
)) {
4177 sipe_election_result(sip
, session
);
4180 } else if (xn_set_rm_response
) {
4183 xmlnode_free(xn_action
);
4190 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
, const char *content_type
)
4194 char *msgtext
= NULL
;
4195 const gchar
*msgr
= "";
4198 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
4202 sipe_parse_html(msg
, &msgformat
, &msgtext
);
4203 purple_debug_info("sipe", "sipe_send_message: msgformat=%s\n", msgformat
);
4205 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4208 msgr
= tmp2
= g_strdup_printf(";msgr=%s", msgr_value
);
4212 msgtext
= g_strdup(msg
);
4215 tmp
= get_contact(sip
);
4216 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
4217 //hdr = g_strdup("Content-Type: text/rtf\r\n");
4218 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
4219 if (content_type
== NULL
)
4220 content_type
= "text/plain";
4222 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
4226 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
4233 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
4235 GSList
*entry2
= session
->outgoing_message_queue
;
4237 struct queued_message
*msg
= entry2
->data
;
4239 /* for multiparty chat or conference */
4240 if (session
->is_multiparty
|| session
->focus_uri
) {
4241 gchar
*who
= sip_uri_self(sip
);
4242 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
4243 PURPLE_MESSAGE_SEND
, msg
->body
, time(NULL
));
4247 SIPE_DIALOG_FOREACH
{
4249 struct queued_message
*message
;
4251 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
4253 message
= g_new0(struct queued_message
,1);
4254 message
->body
= g_strdup(msg
->body
);
4255 if (msg
->content_type
!= NULL
)
4256 message
->content_type
= g_strdup(msg
->content_type
);
4258 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
4259 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
4260 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
4261 key
, g_hash_table_size(session
->unconfirmed_messages
));
4264 sipe_send_message(sip
, dialog
, msg
->body
, msg
->content_type
);
4265 } SIPE_DIALOG_FOREACH_END
;
4267 entry2
= sipe_session_dequeue_message(session
);
4272 sipe_refer_notify(struct sipe_account_data
*sip
,
4273 struct sip_session
*session
,
4280 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4282 hdr
= g_strdup_printf(
4284 "Subscription-State: %s\r\n"
4285 "Content-Type: message/sipfrag\r\n",
4286 status
>= 200 ? "terminated" : "active");
4288 body
= g_strdup_printf(
4289 "SIP/2.0 %d %s\r\n",
4292 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
4299 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
4301 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4302 struct sip_session
*session
;
4303 struct sip_dialog
*dialog
;
4306 struct queued_message
*message
;
4307 struct sipmsg
*request_msg
= trans
->msg
;
4309 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4312 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4314 session
= sipe_session_find_im(sip
, with
);
4317 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
4322 dialog
= sipe_dialog_find(session
, with
);
4324 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
4329 sipe_dialog_parse(dialog
, msg
, TRUE
);
4331 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4332 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
4334 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4336 if (msg
->response
!= 200) {
4337 PurpleBuddy
*pbuddy
;
4338 const char *alias
= with
;
4339 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
4342 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
4345 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
4347 warning
= atoi(parts
[0]);
4352 /* cancel file transfer as rejected by server */
4353 if (msg
->response
== 606 && /* Not acceptable all. */
4354 warning
== 309 && /* Message contents not allowed by policy */
4355 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
4357 GSList
*parsed_body
= sipe_ft_parse_msg_body(message
->body
);
4358 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4359 sipe_utils_nameval_free(parsed_body
);
4362 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4363 alias
= purple_buddy_get_alias(pbuddy
);
4367 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, warning
, alias
, message
->body
);
4369 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
4370 sipe_present_err(sip
, session
, tmp_msg
);
4374 sipe_dialog_remove(session
, with
);
4382 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4383 dialog
->outgoing_invite
= NULL
;
4384 dialog
->is_established
= TRUE
;
4386 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
4388 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
4389 g_free(referred_by
);
4392 /* add user to chat if it is a multiparty session */
4393 if (session
->is_multiparty
) {
4394 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4396 PURPLE_CBFLAGS_NONE
, TRUE
);
4399 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
4400 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
4401 sipe_session_dequeue_message(session
);
4404 sipe_im_process_queue(sip
, session
);
4406 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4407 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
4408 key
, g_hash_table_size(session
->unconfirmed_messages
));
4417 sipe_invite(struct sipe_account_data
*sip
,
4418 struct sip_session
*session
,
4420 const gchar
*msg_body
,
4421 const gchar
*msg_content_type
,
4422 const gchar
*referred_by
,
4423 const gboolean is_triggered
)
4430 char *ms_text_format
= NULL
;
4431 gchar
*roster_manager
;
4433 gchar
*referred_by_str
;
4434 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4436 if (dialog
&& dialog
->is_established
) {
4437 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
4442 dialog
= sipe_dialog_add(session
);
4443 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
4444 dialog
->with
= g_strdup(who
);
4447 if (!(dialog
->ourtag
)) {
4448 dialog
->ourtag
= gentag();
4454 char *msgtext
= NULL
;
4456 const gchar
*msgr
= "";
4458 struct queued_message
*message
;
4461 if (!g_str_has_prefix(msg_content_type
, "text/x-msmsgsinvite")) {
4465 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
4466 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
4468 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4471 msgr
= tmp
= g_strdup_printf(";msgr=%s", msgr_value
);
4475 msgtext
= g_strdup(msg_body
);
4478 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
4479 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
,
4480 msg_content_type
? msg_content_type
: "text/plain",
4487 message
= g_new0(struct queued_message
,1);
4488 message
->body
= g_strdup(msg_body
);
4489 if (msg_content_type
!= NULL
)
4490 message
->content_type
= g_strdup(msg_content_type
);
4492 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
4493 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
4494 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
4495 key
, g_hash_table_size(session
->unconfirmed_messages
));
4499 contact
= get_contact(sip
);
4500 end_points
= get_end_points(sip
, session
);
4501 self
= sip_uri_self(sip
);
4502 roster_manager
= g_strdup_printf(
4503 "Roster-Manager: %s\r\n"
4504 "EndPoints: %s\r\n",
4507 referred_by_str
= referred_by
?
4509 "Referred-By: %s\r\n",
4512 hdr
= g_strdup_printf(
4513 "Supported: ms-sender\r\n"
4519 "Content-Type: application/sdp\r\n",
4520 sipe_strequal(session
->roster_manager
, self
) ? roster_manager
: "",
4522 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
4523 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
4525 ms_text_format
? ms_text_format
: "");
4526 g_free(ms_text_format
);
4529 body
= g_strdup_printf(
4531 "o=- 0 0 IN IP4 %s\r\n"
4535 "m=%s %d sip null\r\n"
4536 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
4537 purple_network_get_my_ip(-1),
4538 purple_network_get_my_ip(-1),
4539 sip
->ocs2007
? "message" : "x-ms-message",
4542 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
4543 to
, to
, hdr
, body
, dialog
, process_invite_response
);
4546 g_free(roster_manager
);
4548 g_free(referred_by_str
);
4555 sipe_refer(struct sipe_account_data
*sip
,
4556 struct sip_session
*session
,
4561 gchar
*epid
= get_epid(sip
);
4562 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
4563 session
->roster_manager
);
4564 const char *ourtag
= dialog
&& dialog
->ourtag
? dialog
->ourtag
: NULL
;
4566 contact
= get_contact(sip
);
4567 hdr
= g_strdup_printf(
4569 "Refer-to: <%s>\r\n"
4570 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
4571 "Require: com.microsoft.rtc-multiparty\r\n",
4575 ourtag
? ";tag=" : "",
4576 ourtag
? ourtag
: "",
4580 send_sip_request(sip
->gc
, "REFER",
4581 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
4588 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
4589 struct sip_dialog
*dialog
,
4592 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4594 gchar
*body
= g_strdup_printf(
4595 "<?xml version=\"1.0\"?>\r\n"
4596 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4597 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
4598 sip
->username
, bid
);
4600 send_sip_request(sip
->gc
, "INFO",
4601 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4607 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
4608 struct sip_dialog
*dialog
)
4610 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4612 gchar
*body
= g_strdup_printf(
4613 "<?xml version=\"1.0\"?>\r\n"
4614 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4615 "<SetRM uri=\"sip:%s\"/></action>\r\n",
4618 send_sip_request(sip
->gc
, "INFO",
4619 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4625 sipe_session_close(struct sipe_account_data
*sip
,
4626 struct sip_session
* session
)
4628 if (session
&& session
->focus_uri
) {
4629 sipe_conf_immcu_closed(sip
, session
);
4630 conf_session_close(sip
, session
);
4634 SIPE_DIALOG_FOREACH
{
4635 /* @TODO slow down BYE message sending rate */
4636 /* @see single subscription code */
4637 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4638 } SIPE_DIALOG_FOREACH_END
;
4640 sipe_session_remove(sip
, session
);
4645 sipe_session_close_all(struct sipe_account_data
*sip
)
4648 while ((entry
= sip
->sessions
) != NULL
) {
4649 sipe_session_close(sip
, entry
->data
);
4654 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
4656 struct sipe_account_data
*sip
= gc
->proto_data
;
4658 purple_debug_info("sipe", "conversation with %s closed\n", who
);
4659 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
4663 sipe_chat_leave (PurpleConnection
*gc
, int id
)
4665 struct sipe_account_data
*sip
= gc
->proto_data
;
4666 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
4668 sipe_session_close(sip
, session
);
4671 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
4672 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4674 struct sipe_account_data
*sip
= gc
->proto_data
;
4675 struct sip_session
*session
;
4676 struct sip_dialog
*dialog
;
4677 gchar
*uri
= sip_uri(who
);
4679 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
4681 session
= sipe_session_find_or_add_im(sip
, uri
);
4682 dialog
= sipe_dialog_find(session
, uri
);
4684 // Queue the message
4685 sipe_session_enqueue_message(session
, what
, NULL
);
4687 if (dialog
&& !dialog
->outgoing_invite
) {
4688 sipe_im_process_queue(sip
, session
);
4689 } else if (!dialog
|| !dialog
->outgoing_invite
) {
4690 // Need to send the INVITE to get the outgoing dialog setup
4691 sipe_invite(sip
, session
, uri
, what
, NULL
, NULL
, FALSE
);
4698 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
4699 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4701 struct sipe_account_data
*sip
= gc
->proto_data
;
4702 struct sip_session
*session
;
4704 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
4706 session
= sipe_session_find_chat_by_id(sip
, id
);
4708 // Queue the message
4709 if (session
&& session
->dialogs
) {
4710 sipe_session_enqueue_message(session
,what
,NULL
);
4711 sipe_im_process_queue(sip
, session
);
4713 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
4714 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
4716 purple_debug_info("sipe", "sipe_chat_send: chat_name='%s'\n", chat_name
? chat_name
: "NULL");
4717 purple_debug_info("sipe", "sipe_chat_send: proto_chat_id='%s'\n", proto_chat_id
? proto_chat_id
: "NULL");
4720 struct sip_session
*session
= sipe_session_add_chat(sip
);
4722 session
->is_multiparty
= FALSE
;
4723 session
->focus_uri
= g_strdup(proto_chat_id
);
4724 sipe_session_enqueue_message(session
, what
, NULL
);
4725 sipe_invite_conf_focus(sip
, session
);
4732 /* End IM Session (INVITE and MESSAGE methods) */
4734 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4736 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4737 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4739 struct sip_session
*session
;
4741 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
4743 /* Call Control protocol */
4744 if (g_str_has_prefix(contenttype
, "application/csta+xml"))
4746 process_incoming_info_csta(sip
, msg
);
4750 from
= parse_from(sipmsg_find_header(msg
, "From"));
4751 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4753 session
= sipe_session_find_im(sip
, from
);
4760 if (g_str_has_prefix(contenttype
, "application/x-ms-mim"))
4762 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4763 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
4764 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
4766 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
4768 if (xn_request_rm
) {
4769 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
4770 int bid
= xmlnode_get_int_attrib(xn_request_rm
, "bid", 0);
4771 gchar
*body
= g_strdup_printf(
4772 "<?xml version=\"1.0\"?>\r\n"
4773 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4774 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
4776 session
->bid
< bid
? "true" : "false");
4777 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4779 } else if (xn_set_rm
) {
4781 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
4782 g_free(session
->roster_manager
);
4783 session
->roster_manager
= g_strdup(rm
);
4785 body
= g_strdup_printf(
4786 "<?xml version=\"1.0\"?>\r\n"
4787 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4788 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
4790 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4793 xmlnode_free(xn_action
);
4798 /* looks like purple lacks typing notification for chat */
4799 if (!session
->is_multiparty
&& !session
->focus_uri
) {
4800 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4801 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
4803 if (sipe_strequal(status
, "type")) {
4804 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4805 } else if (sipe_strequal(status
, "idle")) {
4806 serv_got_typing_stopped(sip
->gc
, from
);
4808 xmlnode_free(xn_keyboard_activity
);
4811 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4816 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4818 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4819 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4820 struct sip_session
*session
;
4821 struct sip_dialog
*dialog
;
4823 /* collect dialog identification
4824 * we need callid, ourtag and theirtag to unambiguously identify dialog
4826 /* take data before 'msg' will be modified by send_sip_response */
4827 dialog
= g_new0(struct sip_dialog
, 1);
4828 dialog
->callid
= g_strdup(callid
);
4829 dialog
->cseq
= parse_cseq(sipmsg_find_header(msg
, "CSeq"));
4830 dialog
->with
= g_strdup(from
);
4831 sipe_dialog_parse(dialog
, msg
, FALSE
);
4833 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4835 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4837 session
= sipe_session_find_im(sip
, from
);
4840 sipe_dialog_free(dialog
);
4845 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
4846 g_free(session
->roster_manager
);
4847 session
->roster_manager
= NULL
;
4850 /* This what BYE is essentially for - terminating dialog */
4851 sipe_dialog_remove_3(session
, dialog
);
4852 sipe_dialog_free(dialog
);
4853 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
4854 sipe_conf_immcu_closed(sip
, session
);
4855 } else if (session
->is_multiparty
) {
4856 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
4862 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4864 gchar
*self
= sip_uri_self(sip
);
4865 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4866 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4867 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
4868 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
4869 struct sip_session
*session
;
4870 struct sip_dialog
*dialog
;
4872 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4873 dialog
= sipe_dialog_find(session
, from
);
4875 if (!session
|| !dialog
|| !session
->roster_manager
|| !sipe_strequal(session
->roster_manager
, self
)) {
4876 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
4878 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
4880 sipe_invite(sip
, session
, refer_to
, NULL
, NULL
, referred_by
, FALSE
);
4886 g_free(referred_by
);
4890 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
4892 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
4893 struct sip_session
*session
;
4894 struct sip_dialog
*dialog
;
4896 if (state
== PURPLE_NOT_TYPING
)
4899 session
= sipe_session_find_im(sip
, who
);
4900 dialog
= sipe_dialog_find(session
, who
);
4902 if (session
&& dialog
&& dialog
->is_established
) {
4903 send_sip_request(gc
, "INFO", who
, who
,
4904 "Content-Type: application/xml\r\n",
4905 SIPE_SEND_TYPING
, dialog
, NULL
);
4907 return SIPE_TYPING_SEND_TIMEOUT
;
4910 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
4912 GSList
*tmp
= sip
->transactions
;
4913 time_t currtime
= time(NULL
);
4915 struct transaction
*trans
= tmp
->data
;
4917 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
4918 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
4921 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
4923 sendout_sipmsg(sip
, trans
->msg
);
4930 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
4931 SIPE_UNUSED_PARAMETER
void *unused
)
4933 /* register again when security token expires */
4934 /* we have to start a new authentication as the security token
4935 * is almost expired by sending a not signed REGISTER message */
4936 purple_debug_info("sipe", "do a full reauthentication\n");
4937 sipe_auth_free(&sip
->registrar
);
4938 sipe_auth_free(&sip
->proxy
);
4939 sip
->registerstatus
= 0;
4941 sip
->reauthenticate_set
= FALSE
;
4945 sipe_process_incoming_x_msmsgsinvite(struct sipe_account_data
*sip
,
4947 GSList
*parsed_body
)
4949 gboolean found
= FALSE
;
4952 const gchar
*invitation_command
= sipe_utils_nameval_find(parsed_body
, "Invitation-Command");
4954 if (sipe_strequal(invitation_command
, "INVITE")) {
4955 sipe_ft_incoming_transfer(sip
->gc
->account
, msg
, parsed_body
);
4957 } else if (sipe_strequal(invitation_command
, "CANCEL")) {
4958 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4960 } else if (sipe_strequal(invitation_command
, "ACCEPT")) {
4961 sipe_ft_incoming_accept(sip
->gc
->account
, parsed_body
);
4968 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4971 const gchar
*contenttype
;
4972 gboolean found
= FALSE
;
4974 from
= parse_from(sipmsg_find_header(msg
, "From"));
4978 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
4980 contenttype
= sipmsg_find_header(msg
, "Content-Type");
4981 if (g_str_has_prefix(contenttype
, "text/plain")
4982 || g_str_has_prefix(contenttype
, "text/html")
4983 || g_str_has_prefix(contenttype
, "multipart/related")
4984 || g_str_has_prefix(contenttype
, "multipart/alternative"))
4986 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4987 gchar
*html
= get_html_message(contenttype
, msg
->body
);
4989 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4991 session
= sipe_session_find_im(sip
, from
);
4994 if (session
&& session
->focus_uri
) { /* a conference */
4995 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
4996 gchar
*sender
= parse_from(tmp
);
4998 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
4999 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5001 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
5002 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5003 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5005 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5008 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5011 } else if (g_str_has_prefix(contenttype
, "application/im-iscomposing+xml")) {
5012 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5017 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
5022 state
= xmlnode_get_child(isc
, "state");
5025 purple_debug_info("sipe", "process_incoming_message: no state found\n");
5031 statedata
= xmlnode_get_data(state
);
5033 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
5034 else serv_got_typing_stopped(sip
->gc
, from
);
5039 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5041 } else if (g_str_has_prefix(contenttype
, "text/x-msmsgsinvite")) {
5042 GSList
*body
= sipe_ft_parse_msg_body(msg
->body
);
5043 found
= sipe_process_incoming_x_msmsgsinvite(sip
, msg
, body
);
5044 sipe_utils_nameval_free(body
);
5046 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5050 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5051 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
5053 session
= sipe_session_find_im(sip
, from
);
5056 gchar
*errmsg
= g_strdup_printf(_("Received a message with unrecognized contents from %s"),
5058 sipe_present_err(sip
, session
, errmsg
);
5062 purple_debug_info("sipe", "got unknown mime-type '%s'\n", contenttype
);
5063 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
5068 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5072 const gchar
*oldHeader
;
5074 gboolean is_multiparty
= FALSE
;
5075 gboolean is_triggered
= FALSE
;
5076 gboolean was_multiparty
= TRUE
;
5077 gboolean just_joined
= FALSE
;
5079 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5080 const gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
5081 const gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
5082 const gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
5083 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
5084 GSList
*end_points
= NULL
;
5086 struct sip_session
*session
;
5087 const gchar
*ms_text_format
;
5089 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? tmp
= fix_newlines(msg
->body
) : "");
5092 /* Invitation to join conference */
5093 if (g_str_has_prefix(content_type
, "application/ms-conf-invite+xml")) {
5094 process_incoming_invite_conf(sip
, msg
);
5098 /* Only accept text invitations */
5099 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
5100 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
5104 // TODO There *must* be a better way to clean up the To header to add a tag...
5105 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
5106 oldHeader
= sipmsg_find_header(msg
, "To");
5108 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
5109 sipmsg_remove_header_now(msg
, "To");
5110 sipmsg_add_header_now(msg
, "To", newHeader
);
5113 if (end_points_hdr
) {
5114 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
5116 if (g_slist_length(end_points
) > 2) {
5117 is_multiparty
= TRUE
;
5120 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
5121 is_triggered
= TRUE
;
5122 is_multiparty
= TRUE
;
5125 session
= sipe_session_find_chat_by_callid(sip
, callid
);
5126 /* Convert to multiparty */
5127 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
5128 g_free(session
->with
);
5129 session
->with
= NULL
;
5130 was_multiparty
= FALSE
;
5131 session
->is_multiparty
= TRUE
;
5132 session
->chat_id
= rand();
5135 if (!session
&& is_multiparty
) {
5136 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
5139 from
= parse_from(sipmsg_find_header(msg
, "From"));
5141 session
= sipe_session_find_or_add_im(sip
, from
);
5145 g_free(session
->callid
);
5146 session
->callid
= g_strdup(callid
);
5148 session
->is_multiparty
= is_multiparty
;
5149 if (roster_manager
) {
5150 session
->roster_manager
= g_strdup(roster_manager
);
5154 if (is_multiparty
&& end_points
) {
5155 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
5156 GSList
*entry
= end_points
;
5158 struct sip_dialog
*dialog
;
5159 struct sipendpoint
*end_point
= entry
->data
;
5160 entry
= entry
->next
;
5162 if (!g_strcasecmp(from
, end_point
->contact
) ||
5163 !g_strcasecmp(to
, end_point
->contact
))
5166 dialog
= sipe_dialog_find(session
, end_point
->contact
);
5168 g_free(dialog
->theirepid
);
5169 dialog
->theirepid
= end_point
->epid
;
5170 end_point
->epid
= NULL
;
5172 dialog
= sipe_dialog_add(session
);
5174 dialog
->callid
= g_strdup(session
->callid
);
5175 dialog
->with
= end_point
->contact
;
5176 end_point
->contact
= NULL
;
5177 dialog
->theirepid
= end_point
->epid
;
5178 end_point
->epid
= NULL
;
5182 /* send triggered INVITE */
5183 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, NULL
, TRUE
);
5190 GSList
*entry
= end_points
;
5192 struct sipendpoint
*end_point
= entry
->data
;
5193 entry
= entry
->next
;
5194 g_free(end_point
->contact
);
5195 g_free(end_point
->epid
);
5198 g_slist_free(end_points
);
5202 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
5204 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
5205 sipe_dialog_parse_routes(dialog
, msg
, FALSE
);
5207 dialog
= sipe_dialog_add(session
);
5209 dialog
->callid
= g_strdup(session
->callid
);
5210 dialog
->with
= g_strdup(from
);
5211 sipe_dialog_parse(dialog
, msg
, FALSE
);
5213 if (!dialog
->ourtag
) {
5214 dialog
->ourtag
= newTag
;
5221 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
5225 if (is_multiparty
&& !session
->conv
) {
5226 gchar
*chat_title
= sipe_chat_get_name(callid
);
5227 gchar
*self
= sip_uri_self(sip
);
5228 /* create prpl chat */
5229 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_title
);
5230 session
->chat_title
= g_strdup(chat_title
);
5231 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
5233 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5235 PURPLE_CBFLAGS_NONE
, FALSE
);
5240 if (is_multiparty
&& !was_multiparty
) {
5241 /* add current IM counterparty to chat */
5242 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5243 sipe_dialog_first(session
)->with
, NULL
,
5244 PURPLE_CBFLAGS_NONE
, FALSE
);
5247 /* add inviting party to chat */
5248 if (just_joined
&& session
->conv
) {
5249 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5251 PURPLE_CBFLAGS_NONE
, TRUE
);
5254 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
5256 /* This used only in 2005 official client, not 2007 or Reuters.
5257 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
5258 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
5260 /* also enabled for 2005 file transfer. Didn't work otherwise. */
5261 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
5262 if (is_multiparty
||
5263 (ms_text_format
&& g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite")) )
5265 if (ms_text_format
) {
5266 if (g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite"))
5268 gchar
*tmp
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
5270 gchar
*body
= (gchar
*) purple_base64_decode(tmp
, NULL
);
5272 GSList
*parsed_body
= sipe_ft_parse_msg_body(body
);
5274 sipe_process_incoming_x_msmsgsinvite(sip
, msg
, parsed_body
);
5275 sipe_utils_nameval_free(parsed_body
);
5276 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5280 else if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html"))
5282 /* please do not optimize logic inside as this code may be re-enabled for other cases */
5283 gchar
*html
= get_html_message(ms_text_format
, NULL
);
5285 if (is_multiparty
) {
5286 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5287 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5289 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5292 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5300 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
5301 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5302 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5304 body
= g_strdup_printf(
5306 "o=- 0 0 IN IP4 %s\r\n"
5310 "m=%s %d sip sip:%s\r\n"
5311 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5312 purple_network_get_my_ip(-1),
5313 purple_network_get_my_ip(-1),
5314 sip
->ocs2007
? "message" : "x-ms-message",
5317 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5321 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5325 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
5326 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5327 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5329 body
= g_strdup_printf(
5331 "o=- 0 0 IN IP4 0.0.0.0\r\n"
5333 "c=IN IP4 0.0.0.0\r\n"
5335 "m=%s %d sip sip:%s\r\n"
5336 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5337 sip
->ocs2007
? "message" : "x-ms-message",
5340 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5345 sipe_get_auth_scheme_name(struct sipe_account_data
*sip
)
5347 const char *res
= "NTLM";
5349 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5353 (void) sip
; /* make compiler happy */
5358 static void sipe_connection_cleanup(struct sipe_account_data
*);
5359 static void create_connection(struct sipe_account_data
*, gchar
*, int);
5361 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5362 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5365 const gchar
*expires_header
;
5367 GSList
*hdr
= msg
->headers
;
5368 struct sipnameval
*elem
;
5370 expires_header
= sipmsg_find_header(msg
, "Expires");
5371 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
5372 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
5374 switch (msg
->response
) {
5377 sip
->registerstatus
= 0;
5379 const gchar
*contact_hdr
;
5384 const gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
5385 const char *auth_scheme
;
5387 if (!sip
->reregister_set
) {
5388 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
5389 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
5390 g_free(action_name
);
5391 sip
->reregister_set
= TRUE
;
5394 sip
->registerstatus
= 3;
5396 if (server_hdr
&& !sip
->server_version
) {
5397 sip
->server_version
= g_strdup(server_hdr
);
5402 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5403 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5406 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5407 fill_auth(tmp
, &sip
->registrar
);
5410 if (!sip
->reauthenticate_set
) {
5411 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
5412 guint reauth_timeout
;
5413 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
5414 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
5415 reauth_timeout
= sip
->registrar
.expires
- 300;
5417 /* NTLM: we have to reauthenticate as our security token expires
5418 after eight hours (be five minutes early) */
5419 reauth_timeout
= (8 * 3600) - 300;
5421 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
5422 g_free(action_name
);
5423 sip
->reauthenticate_set
= TRUE
;
5426 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
5428 epid
= get_epid(sip
);
5429 uuid
= generateUUIDfromEPID(epid
);
5432 // There can be multiple Contact headers (one per location where the user is logged in) so
5433 // make sure to only get the one for this uuid
5434 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
5435 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
5436 if (valid_contact
) {
5437 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
5438 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
5439 g_free(valid_contact
);
5442 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
5447 g_free(sip
->contact
);
5449 sip
->contact
= g_strdup_printf("<%s>", gruu
);
5452 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
5453 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
);
5455 sip
->ocs2007
= FALSE
;
5456 sip
->batched_support
= FALSE
;
5461 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
5462 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
5463 /* We interpret this as OCS2007+ indicator */
5464 sip
->ocs2007
= TRUE
;
5465 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
5467 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
5468 sip
->batched_support
= TRUE
;
5469 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
5472 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
5473 gchar
**caps
= g_strsplit(elem
->value
,",",0);
5476 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
5477 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
5482 hdr
= g_slist_next(hdr
);
5485 /* rejoin open chats to be able to use them by continue to send messages */
5486 purple_conversation_foreach(sipe_rejoin_chat
);
5489 if (!sip
->subscribed
) { //do it just once, not every re-register
5491 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
5492 (GCompareFunc
)g_ascii_strcasecmp
)) {
5493 sipe_subscribe_roaming_contacts(sip
);
5496 /* For 2007+ it does not make sence to subscribe to:
5497 * vnd-microsoft-roaming-ACL
5498 * vnd-microsoft-provisioning (not v2)
5500 * These are for backward compatibility.
5504 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
5505 (GCompareFunc
)g_ascii_strcasecmp
)) {
5506 sipe_subscribe_roaming_self(sip
);
5508 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
5509 (GCompareFunc
)g_ascii_strcasecmp
)) {
5510 sipe_subscribe_roaming_provisioning_v2(sip
);
5513 /* For 2005- servers */
5516 //sipe_options_request(sip, sip->sipdomain);
5518 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
5519 (GCompareFunc
)g_ascii_strcasecmp
)) {
5520 sipe_subscribe_roaming_acl(sip
);
5522 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
5523 (GCompareFunc
)g_ascii_strcasecmp
)) {
5524 sipe_subscribe_roaming_provisioning(sip
);
5526 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
5527 (GCompareFunc
)g_ascii_strcasecmp
)) {
5528 sipe_subscribe_presence_wpending(sip
, msg
);
5531 /* For 2007+ we publish our initial statuses and calendar data only after
5532 * received our existing publications in sipe_process_roaming_self()
5533 * Only in this case we know versions of current publications made
5536 /* For 2005- we publish our initial statuses only after
5537 * received our existing UserInfo data in response to
5538 * self subscription.
5539 * Only in this case we won't override existing UserInfo data
5540 * set earlier or by other client on our behalf.
5544 sip
->subscribed
= TRUE
;
5547 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
5548 "timeout=", ";", NULL
);
5549 if (timeout
!= NULL
) {
5550 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
5551 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
5552 sip
->keepalive_timeout
);
5556 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
5561 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
5563 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
5564 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
5568 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
5571 tmp
= g_strsplit(parts
[0], ":", 0);
5572 hostname
= g_strdup(tmp
[0]);
5573 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
5577 tmp
= g_strsplit(parts
[i
], "=", 0);
5579 if (g_strcasecmp("transport", tmp
[0]) == 0) {
5580 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
5581 transport
= SIPE_TRANSPORT_TCP
;
5582 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
5583 transport
= SIPE_TRANSPORT_UDP
;
5592 /* Close old connection */
5593 sipe_connection_cleanup(sip
);
5595 /* Create new connection */
5596 sip
->transport
= transport
;
5597 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
5598 hostname
, port
, TRANSPORT_DESCRIPTOR
);
5599 create_connection(sip
, hostname
, port
);
5605 if (sip
->registerstatus
!= 2) {
5606 const char *auth_scheme
;
5607 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
5608 if (sip
->registrar
.retries
> 3) {
5609 sip
->gc
->wants_to_die
= TRUE
;
5610 purple_connection_error(sip
->gc
, _("Authentication failed"));
5614 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5615 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5617 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
? tmp
: "");
5619 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
5620 sip
->gc
->wants_to_die
= TRUE
;
5621 purple_connection_error(sip
->gc
, tmp2
);
5625 fill_auth(tmp
, &sip
->registrar
);
5626 sip
->registerstatus
= 2;
5627 if (sip
->account
->disconnecting
) {
5628 do_register_exp(sip
, 0);
5636 const gchar
*diagnostics
= sipmsg_find_header(msg
, "Warning");
5637 gchar
**reason
= NULL
;
5639 if (diagnostics
!= NULL
) {
5641 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
5643 reason
= g_strsplit(diagnostics
, "\"", 0);
5645 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
5646 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
5649 sip
->gc
->wants_to_die
= TRUE
;
5650 purple_connection_error(sip
->gc
, warning
);
5657 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5658 gchar
*reason
= NULL
;
5660 if (diagnostics
!= NULL
) {
5661 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5663 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
5664 diagnostics
? (reason
? reason
: _("no reason given")) :
5665 _("SIP is either not enabled for the destination URI or it does not exist"));
5668 sip
->gc
->wants_to_die
= TRUE
;
5669 purple_connection_error(sip
->gc
, warning
);
5675 case 504: /* Server time-out */
5677 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5678 gchar
*reason
= NULL
;
5680 if (diagnostics
!= NULL
) {
5681 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5683 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
5686 sip
->gc
->wants_to_die
= TRUE
;
5687 purple_connection_error(sip
->gc
, warning
);
5697 * Returns 2005-style activity and Availability.
5699 * @param status Sipe statis id.
5702 sipe_get_act_avail_by_status_2005(const char *status
,
5706 int avail
= 300; /* online */
5707 int act
= 400; /* Available */
5709 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
5711 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
5713 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
5715 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
5717 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
5719 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
5720 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
5722 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
5723 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
5724 avail
= 0; /* offline */
5727 act
= 400; /* Available */
5730 if (activity
) *activity
= act
;
5731 if (availability
) *availability
= avail
;
5737 * @param activity 2005 aggregated activity. Ex.: 600
5738 * @param availablity 2005 aggregated availablity. Ex.: 300
5741 sipe_get_status_by_act_avail_2005(const int activity
,
5742 const int availablity
,
5743 char **activity_desc
)
5745 const char *status_id
= NULL
;
5746 const char *act
= NULL
;
5748 if (activity
< 150) {
5749 status_id
= SIPE_STATUS_ID_AWAY
;
5750 } else if (activity
< 200) {
5751 //status_id = SIPE_STATUS_ID_LUNCH;
5752 status_id
= SIPE_STATUS_ID_AWAY
;
5753 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
5754 } else if (activity
< 300) {
5755 //status_id = SIPE_STATUS_ID_IDLE;
5756 status_id
= SIPE_STATUS_ID_AWAY
;
5757 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5758 } else if (activity
< 400) {
5759 status_id
= SIPE_STATUS_ID_BRB
;
5760 } else if (activity
< 500) {
5761 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5762 } else if (activity
< 600) {
5763 //status_id = SIPE_STATUS_ID_ON_PHONE;
5764 status_id
= SIPE_STATUS_ID_BUSY
;
5765 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
5766 } else if (activity
< 700) {
5767 status_id
= SIPE_STATUS_ID_BUSY
;
5768 } else if (activity
< 800) {
5769 status_id
= SIPE_STATUS_ID_AWAY
;
5771 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5774 if (availablity
< 100)
5775 status_id
= SIPE_STATUS_ID_OFFLINE
;
5777 if (activity_desc
&& act
) {
5778 g_free(*activity_desc
);
5779 *activity_desc
= g_strdup(act
);
5786 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
5789 sipe_get_status_by_availability(int avail
,
5790 char** activity_desc
)
5793 const char *act
= NULL
;
5796 status
= SIPE_STATUS_ID_OFFLINE
;
5797 } else if (avail
< 4500) {
5798 status
= SIPE_STATUS_ID_AVAILABLE
;
5799 } else if (avail
< 6000) {
5800 //status = SIPE_STATUS_ID_IDLE;
5801 status
= SIPE_STATUS_ID_AVAILABLE
;
5802 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5803 } else if (avail
< 7500) {
5804 status
= SIPE_STATUS_ID_BUSY
;
5805 } else if (avail
< 9000) {
5806 //status = SIPE_STATUS_ID_BUSYIDLE;
5807 status
= SIPE_STATUS_ID_BUSY
;
5808 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
5809 } else if (avail
< 12000) {
5810 status
= SIPE_STATUS_ID_DND
;
5811 } else if (avail
< 15000) {
5812 status
= SIPE_STATUS_ID_BRB
;
5813 } else if (avail
< 18000) {
5814 status
= SIPE_STATUS_ID_AWAY
;
5816 status
= SIPE_STATUS_ID_OFFLINE
;
5819 if (activity_desc
&& act
) {
5820 g_free(*activity_desc
);
5821 *activity_desc
= g_strdup(act
);
5828 * Returns 2007-style availability value
5830 * @param sipe_status_id (in)
5831 * @param activity_token (out) Must be g_free()'d after use if consumed.
5834 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
5837 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
5839 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
5840 availability
= 15500;
5841 if (!activity_token
|| !(*activity_token
)) {
5842 activity
= SIPE_ACTIVITY_AWAY
;
5844 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
5845 availability
= 12500;
5846 activity
= SIPE_ACTIVITY_BRB
;
5847 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
5848 availability
= 9500;
5849 activity
= SIPE_ACTIVITY_DND
;
5850 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
5851 availability
= 6500;
5852 if (!activity_token
|| !(*activity_token
)) {
5853 activity
= SIPE_ACTIVITY_BUSY
;
5855 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
5856 availability
= 3500;
5857 activity
= SIPE_ACTIVITY_ONLINE
;
5858 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
5861 // Offline or invisible
5862 availability
= 18500;
5863 activity
= SIPE_ACTIVITY_OFFLINE
;
5866 if (activity_token
) {
5867 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
5869 return availability
;
5872 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5875 xmlnode
*xn_categories
;
5876 xmlnode
*xn_category
;
5878 const char *status
= NULL
;
5879 gboolean do_update_status
= FALSE
;
5880 gboolean has_note_cleaned
= FALSE
;
5881 gboolean has_free_busy_cleaned
= FALSE
;
5883 xn_categories
= xmlnode_from_str(data
, len
);
5884 uri
= xmlnode_get_attrib(xn_categories
, "uri"); /* with 'sip:' prefix */
5886 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
5888 xn_category
= xmlnode_get_next_twin(xn_category
) )
5891 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
5892 time_t publish_time
= (tmp
= xmlnode_get_attrib(xn_category
, "publishTime")) ?
5893 sipe_utils_str_to_time(tmp
) : 0;
5896 if (sipe_strequal(attrVar
, "contactCard"))
5899 /* identity - Display Name and email */
5900 node
= xmlnode_get_descendant(xn_category
, "contactCard", "identity", NULL
);
5902 char* display_name
= xmlnode_get_data(
5903 xmlnode_get_descendant(node
, "name", "displayName", NULL
));
5904 char* email
= xmlnode_get_data(
5905 xmlnode_get_child(node
, "email"));
5907 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5908 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5910 g_free(display_name
);
5914 node
= xmlnode_get_descendant(xn_category
, "contactCard", "company", NULL
);
5916 char* company
= xmlnode_get_data(node
);
5917 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
5921 node
= xmlnode_get_descendant(xn_category
, "contactCard", "department", NULL
);
5923 char* department
= xmlnode_get_data(node
);
5924 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
5928 node
= xmlnode_get_descendant(xn_category
, "contactCard", "title", NULL
);
5930 char* title
= xmlnode_get_data(node
);
5931 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
5935 node
= xmlnode_get_descendant(xn_category
, "contactCard", "office", NULL
);
5937 char* office
= xmlnode_get_data(node
);
5938 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
5942 node
= xmlnode_get_descendant(xn_category
, "contactCard", "url", NULL
);
5944 char* site
= xmlnode_get_data(node
);
5945 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
5949 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "phone", NULL
);
5951 node
= xmlnode_get_next_twin(node
))
5953 const char *phone_type
= xmlnode_get_attrib(node
, "type");
5954 char* phone
= xmlnode_get_data(xmlnode_get_child(node
, "uri"));
5955 char* phone_display_string
= xmlnode_get_data(xmlnode_get_child(node
, "displayString"));
5957 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
5960 g_free(phone_display_string
);
5963 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "address", NULL
);
5965 node
= xmlnode_get_next_twin(node
))
5967 if (sipe_strequal(xmlnode_get_attrib(node
, "type"), "work")) {
5968 char* street
= xmlnode_get_data(xmlnode_get_child(node
, "street"));
5969 char* city
= xmlnode_get_data(xmlnode_get_child(node
, "city"));
5970 char* state
= xmlnode_get_data(xmlnode_get_child(node
, "state"));
5971 char* zipcode
= xmlnode_get_data(xmlnode_get_child(node
, "zipcode"));
5972 char* country_code
= xmlnode_get_data(xmlnode_get_child(node
, "countryCode"));
5974 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
5975 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
5976 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
5977 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
5978 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
5984 g_free(country_code
);
5991 else if (sipe_strequal(attrVar
, "note"))
5994 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
5996 if (!has_note_cleaned
) {
5997 has_note_cleaned
= TRUE
;
5999 g_free(sbuddy
->note
);
6000 sbuddy
->note
= NULL
;
6001 sbuddy
->is_oof_note
= FALSE
;
6002 sbuddy
->note_since
= publish_time
;
6004 do_update_status
= TRUE
;
6006 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
6007 /* clean up in case no 'note' element is supplied
6008 * which indicate note removal in client
6010 g_free(sbuddy
->note
);
6011 sbuddy
->note
= NULL
;
6012 sbuddy
->is_oof_note
= FALSE
;
6013 sbuddy
->note_since
= publish_time
;
6015 xn_node
= xmlnode_get_descendant(xn_category
, "note", "body", NULL
);
6018 sbuddy
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_node
)), -1);
6020 sbuddy
->is_oof_note
= sipe_strequal(xmlnode_get_attrib(xn_node
, "type"), "OOF");
6021 sbuddy
->note_since
= publish_time
;
6023 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s), note(%s)\n",
6024 uri
, sbuddy
->note
? sbuddy
->note
: "");
6026 /* to trigger UI refresh in case no status info is supplied in this update */
6027 do_update_status
= TRUE
;
6032 else if(sipe_strequal(attrVar
, "state"))
6036 xmlnode
*xn_availability
;
6037 xmlnode
*xn_activity
;
6038 xmlnode
*xn_meeting_subject
;
6039 xmlnode
*xn_meeting_location
;
6040 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6042 xn_node
= xmlnode_get_child(xn_category
, "state");
6043 if (!xn_node
) continue;
6044 xn_availability
= xmlnode_get_child(xn_node
, "availability");
6045 if (!xn_availability
) continue;
6046 xn_activity
= xmlnode_get_child(xn_node
, "activity");
6047 xn_meeting_subject
= xmlnode_get_child(xn_node
, "meetingSubject");
6048 xn_meeting_location
= xmlnode_get_child(xn_node
, "meetingLocation");
6050 tmp
= xmlnode_get_data(xn_availability
);
6051 availability
= atoi(tmp
);
6054 /* activity, meeting_subject, meeting_location */
6059 g_free(sbuddy
->activity
);
6060 sbuddy
->activity
= NULL
;
6062 const char *token
= xmlnode_get_attrib(xn_activity
, "token");
6063 xmlnode
*xn_custom
= xmlnode_get_child(xn_activity
, "custom");
6066 if (!is_empty(token
)) {
6067 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
6069 /* from custom element */
6071 char *custom
= xmlnode_get_data(xn_custom
);
6073 if (!is_empty(custom
)) {
6074 sbuddy
->activity
= custom
;
6080 /* meeting_subject */
6081 g_free(sbuddy
->meeting_subject
);
6082 sbuddy
->meeting_subject
= NULL
;
6083 if (xn_meeting_subject
) {
6084 char *meeting_subject
= xmlnode_get_data(xn_meeting_subject
);
6086 if (!is_empty(meeting_subject
)) {
6087 sbuddy
->meeting_subject
= meeting_subject
;
6088 meeting_subject
= NULL
;
6090 g_free(meeting_subject
);
6092 /* meeting_location */
6093 g_free(sbuddy
->meeting_location
);
6094 sbuddy
->meeting_location
= NULL
;
6095 if (xn_meeting_location
) {
6096 char *meeting_location
= xmlnode_get_data(xn_meeting_location
);
6098 if (!is_empty(meeting_location
)) {
6099 sbuddy
->meeting_location
= meeting_location
;
6100 meeting_location
= NULL
;
6102 g_free(meeting_location
);
6105 status
= sipe_get_status_by_availability(availability
, &tmp
);
6106 if (sbuddy
->activity
&& tmp
) {
6107 char *tmp2
= sbuddy
->activity
;
6109 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
6113 sbuddy
->activity
= tmp
;
6117 do_update_status
= TRUE
;
6120 else if(sipe_strequal(attrVar
, "calendarData"))
6122 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6123 xmlnode
*xn_free_busy
= xmlnode_get_descendant(xn_category
, "calendarData", "freeBusy", NULL
);
6124 xmlnode
*xn_working_hours
= xmlnode_get_descendant(xn_category
, "calendarData", "WorkingHours", NULL
);
6126 if (sbuddy
&& xn_free_busy
) {
6127 if (!has_free_busy_cleaned
) {
6128 has_free_busy_cleaned
= TRUE
;
6130 g_free(sbuddy
->cal_start_time
);
6131 sbuddy
->cal_start_time
= NULL
;
6133 g_free(sbuddy
->cal_free_busy_base64
);
6134 sbuddy
->cal_free_busy_base64
= NULL
;
6136 g_free(sbuddy
->cal_free_busy
);
6137 sbuddy
->cal_free_busy
= NULL
;
6139 sbuddy
->cal_free_busy_published
= publish_time
;
6142 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
6143 g_free(sbuddy
->cal_start_time
);
6144 sbuddy
->cal_start_time
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
6146 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(xmlnode_get_attrib(xn_free_busy
, "granularity"), "PT15M") ?
6149 g_free(sbuddy
->cal_free_busy_base64
);
6150 sbuddy
->cal_free_busy_base64
= xmlnode_get_data(xn_free_busy
);
6152 g_free(sbuddy
->cal_free_busy
);
6153 sbuddy
->cal_free_busy
= NULL
;
6155 sbuddy
->cal_free_busy_published
= publish_time
;
6157 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
);
6161 if (sbuddy
&& xn_working_hours
) {
6162 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
6167 if (do_update_status
) {
6168 if (!status
) { /* no status category in this update, using contact's current status */
6169 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6170 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
6171 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
6172 status
= purple_status_get_id(pstatus
);
6175 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
6176 sipe_got_user_status(sip
, uri
, status
);
6179 xmlnode_free(xn_categories
);
6182 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
6184 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6185 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
6186 payload
->host
= g_strdup(host
);
6187 payload
->buddies
= server
;
6188 sipe_subscribe_presence_batched_routed(sip
, payload
);
6189 sipe_subscribe_presence_batched_routed_free(payload
);
6192 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6195 xmlnode
*xn_resource
;
6196 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
6201 xn_list
= xmlnode_from_str(data
, len
);
6203 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
6205 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
6207 const char *uri
, *state
;
6208 xmlnode
*xn_instance
;
6210 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
6211 if (!xn_instance
) continue;
6213 uri
= xmlnode_get_attrib(xn_resource
, "uri");
6214 state
= xmlnode_get_attrib(xn_instance
, "state");
6215 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
6217 if (strstr(state
, "resubscribe")) {
6218 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
6220 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
6221 gchar
*user
= g_strdup(uri
);
6222 host
= g_strdup(poolFqdn
);
6223 server
= g_hash_table_lookup(servers
, host
);
6224 server
= g_slist_append(server
, user
);
6225 g_hash_table_insert(servers
, host
, server
);
6227 sipe_subscribe_presence_single(sip
, (void *) uri
);
6232 /* Send out any deferred poolFqdn subscriptions */
6233 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
6234 g_hash_table_destroy(servers
);
6236 xmlnode_free(xn_list
);
6239 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6243 gchar
*activity
= NULL
;
6245 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
6246 gboolean isonline
= FALSE
;
6247 xmlnode
*display_name_node
;
6249 pidf
= xmlnode_from_str(data
, len
);
6251 purple_debug_info("sipe", "process_incoming_notify_pidf: no parseable pidf:%s\n",data
);
6255 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
6257 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6258 basicstatus
= xmlnode_get_child(status
, "basic");
6263 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic found\n");
6268 getbasic
= xmlnode_get_data(basicstatus
);
6270 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic data found\n");
6275 purple_debug_info("sipe", "process_incoming_notify_pidf: basic-status(%s)\n", getbasic
);
6276 if (strstr(getbasic
, "open")) {
6281 uri
= sip_uri(xmlnode_get_attrib(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
6283 display_name_node
= xmlnode_get_child(pidf
, "display-name");
6284 if (display_name_node
) {
6285 char * display_name
= xmlnode_get_data(display_name_node
);
6287 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6288 g_free(display_name
);
6291 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
6292 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6293 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
6294 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
6295 activity
= xmlnode_get_data(basicstatus
);
6296 purple_debug_info("sipe", "process_incoming_notify_pidf: activity(%s)\n", activity
);
6303 const gchar
* status_id
= NULL
;
6305 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
6306 status_id
= SIPE_STATUS_ID_BUSY
;
6307 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
6308 status_id
= SIPE_STATUS_ID_AWAY
;
6313 status_id
= SIPE_STATUS_ID_AVAILABLE
;
6316 purple_debug_info("sipe", "process_incoming_notify_pidf: status_id(%s)\n", status_id
);
6317 sipe_got_user_status(sip
, uri
, status_id
);
6319 sipe_got_user_status(sip
, uri
, SIPE_STATUS_ID_OFFLINE
);
6329 sipe_user_info_has_updated(struct sipe_account_data
*sip
,
6330 xmlnode
*xn_userinfo
)
6332 if (sip
->user_info
) {
6333 xmlnode_free(sip
->user_info
);
6335 sip
->user_info
= xmlnode_copy(xn_userinfo
);
6337 /* Publish initial state if not yet.
6338 * Assuming this happens on initial responce to self subscription
6339 * so we've already updated our UserInfo.
6341 if (!sip
->initial_state_published
) {
6342 send_presence_soap(sip
, FALSE
);
6344 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
6348 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6350 char *activity
= NULL
;
6352 const char *status_id
= NULL
;
6355 char *self_uri
= sip_uri_self(sip
);
6358 const char *device_name
= NULL
;
6359 const char *cal_start_time
= NULL
;
6360 const char *cal_granularity
= NULL
;
6361 char *cal_free_busy_base64
= NULL
;
6362 struct sipe_buddy
*sbuddy
;
6364 xmlnode
*xn_presentity
;
6365 xmlnode
*xn_availability
;
6366 xmlnode
*xn_activity
;
6367 xmlnode
*xn_display_name
;
6369 xmlnode
*xn_phone_number
;
6370 xmlnode
*xn_userinfo
;
6374 xmlnode
*xn_contact
;
6376 char *free_activity
;
6378 const char *user_avail_nil
;
6380 time_t user_avail_since
= 0;
6381 time_t activity_since
= 0;
6383 /* fix for Reuters environment on Linux */
6384 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
6386 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
6387 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
6390 xn_presentity
= xmlnode_from_str(data
, len
);
6393 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
6394 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
6395 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
6396 xn_email
= xmlnode_get_child(xn_presentity
, "email");
6397 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
6398 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
6399 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
6400 xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
): NULL
;
6401 user_avail
= xn_state
? xmlnode_get_int_attrib(xn_state
, "avail", 0) : 0;
6402 user_avail_since
= xn_state
? sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since")) : 0;
6403 user_avail_nil
= xn_state
? xmlnode_get_attrib(xn_state
, "nil") : NULL
;
6404 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
6405 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
6406 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
6408 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
6410 user_avail_since
= 0;
6413 free_activity
= NULL
;
6415 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
6416 uri
= sip_uri_from_name(name
);
6417 avl
= xmlnode_get_int_attrib(xn_availability
, "aggregate", 0);
6418 epid
= xmlnode_get_attrib(xn_availability
, "epid");
6419 act
= xmlnode_get_int_attrib(xn_activity
, "aggregate", 0);
6421 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
6422 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
6423 if (user_avail
> res_avail
) {
6424 res_avail
= user_avail
;
6425 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
6428 if (xn_display_name
) {
6429 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
6430 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
6431 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
6432 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
6433 char *tel_uri
= sip_to_tel_uri(phone_number
);
6435 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6436 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
6437 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
6438 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
6441 g_free(phone_label
);
6442 g_free(phone_number
);
6444 g_free(display_name
);
6449 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
6451 /* Ex.: <tel type="work">tel:+3222220000</tel> */
6452 const char *phone_type
= xmlnode_get_attrib(node
, "type");
6453 char* phone
= xmlnode_get_data(node
);
6455 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
6461 /* devicePresence */
6462 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
6463 xmlnode
*xn_device_name
;
6464 xmlnode
*xn_calendar_info
;
6469 if (sipe_strequal(xmlnode_get_attrib(node
, "epid"), epid
)) {
6470 xn_device_name
= xmlnode_get_child(node
, "deviceName");
6471 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
6475 xn_calendar_info
= xmlnode_get_child(node
, "calendarInfo");
6476 if (xn_calendar_info
) {
6477 const char *cal_start_time_tmp
= xmlnode_get_attrib(xn_calendar_info
, "startTime");
6479 if (cal_start_time
) {
6480 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
6481 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
6483 if (cal_start_time_t_tmp
> cal_start_time_t
) {
6484 cal_start_time
= cal_start_time_tmp
;
6485 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6486 g_free(cal_free_busy_base64
);
6487 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6489 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
);
6492 cal_start_time
= cal_start_time_tmp
;
6493 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6494 g_free(cal_free_busy_base64
);
6495 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6497 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
);
6502 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
6504 int dev_avail
= xmlnode_get_int_attrib(xn_state
, "avail", 0);
6505 time_t dev_avail_since
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since"));
6507 state
= xmlnode_get_data(xn_state
);
6508 if (dev_avail_since
> user_avail_since
&&
6509 dev_avail
>= res_avail
)
6511 res_avail
= dev_avail
;
6512 if (!is_empty(state
))
6514 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
6516 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
6517 } else if (sipe_strequal(state
, "presenting")) {
6519 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
6524 activity_since
= dev_avail_since
;
6526 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
6533 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
6535 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
6539 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6542 g_free(sbuddy
->activity
);
6543 sbuddy
->activity
= activity
;
6546 sbuddy
->activity_since
= activity_since
;
6548 sbuddy
->user_avail
= user_avail
;
6549 sbuddy
->user_avail_since
= user_avail_since
;
6551 g_free(sbuddy
->note
);
6552 sbuddy
->note
= NULL
;
6553 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
6555 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
6557 g_free(sbuddy
->device_name
);
6558 sbuddy
->device_name
= NULL
;
6559 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
6561 if (!is_empty(cal_free_busy_base64
)) {
6562 g_free(sbuddy
->cal_start_time
);
6563 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
6565 sbuddy
->cal_granularity
= !g_ascii_strcasecmp(cal_granularity
, "PT15M") ? 15 : 0;
6567 g_free(sbuddy
->cal_free_busy_base64
);
6568 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
6569 cal_free_busy_base64
= NULL
;
6571 g_free(sbuddy
->cal_free_busy
);
6572 sbuddy
->cal_free_busy
= NULL
;
6575 sbuddy
->last_non_cal_status_id
= status_id
;
6576 g_free(sbuddy
->last_non_cal_activity
);
6577 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
6579 if (sipe_strequal(sbuddy
->name
, self_uri
)) {
6580 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
6582 sip
->is_oof_note
= sbuddy
->is_oof_note
;
6585 sip
->note
= g_strdup(sbuddy
->note
);
6587 sip
->note_since
= time(NULL
);
6590 g_free(sip
->status
);
6591 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
6594 g_free(cal_free_busy_base64
);
6597 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
6598 sipe_got_user_status(sip
, uri
, status_id
);
6600 if (!sip
->ocs2007
&& sipe_strequal(self_uri
, uri
)) {
6601 sipe_user_info_has_updated(sip
, xn_userinfo
);
6605 xmlnode_free(xn_presentity
);
6610 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
6612 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6614 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
6616 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
6617 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
6619 const char *content
= msg
->body
;
6620 unsigned length
= msg
->bodylen
;
6621 PurpleMimeDocument
*mime
= NULL
;
6623 if (strstr(ctype
, "multipart"))
6625 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6626 const char *content_type
;
6628 mime
= purple_mime_document_parse(doc
);
6629 parts
= purple_mime_document_get_parts(mime
);
6631 content
= purple_mime_part_get_data(parts
->data
);
6632 length
= purple_mime_part_get_length(parts
->data
);
6633 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
6634 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
6636 process_incoming_notify_rlmi_resub(sip
, content
, length
);
6638 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
6640 process_incoming_notify_msrtc(sip
, content
, length
);
6644 process_incoming_notify_rlmi(sip
, content
, length
);
6646 parts
= parts
->next
;
6652 purple_mime_document_free(mime
);
6655 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
6657 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
6659 else if(strstr(ctype
, "application/rlmi+xml"))
6661 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
6664 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
6666 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
6670 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
6674 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
6676 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6677 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6679 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
6682 strstr(ctype
, "multipart") &&
6683 (strstr(ctype
, "application/rlmi+xml") ||
6684 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
6685 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6686 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
6687 GList
*parts
= purple_mime_document_get_parts(mime
);
6688 GSList
*buddies
= NULL
;
6689 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6692 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
6693 purple_mime_part_get_length(parts
->data
));
6695 if (xml
&& !sipe_strequal(xml
->name
, "list")) {
6696 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
6698 buddies
= g_slist_append(buddies
, uri
);
6702 parts
= parts
->next
;
6705 if (mime
) purple_mime_document_free(mime
);
6707 payload
->host
= g_strdup(who
);
6708 payload
->buddies
= buddies
;
6709 sipe_schedule_action(action_name
, timeout
,
6710 sipe_subscribe_presence_batched_routed
,
6711 sipe_subscribe_presence_batched_routed_free
,
6713 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
6716 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6717 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
6719 g_free(action_name
);
6723 * Dispatcher for all incoming subscription information
6724 * whether it comes from NOTIFY, BENOTIFY requests or
6725 * piggy-backed to subscription's OK responce.
6727 * @param request whether initiated from BE/NOTIFY request or OK-response message.
6728 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
6730 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
6732 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
6733 const gchar
*event
= sipmsg_find_header(msg
, "Event");
6734 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
6737 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
6739 tmp
= fix_newlines(msg
->body
));
6741 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
6743 /* implicit subscriptions */
6744 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
6745 sipe_process_imdn(sip
, msg
);
6749 /* for one off subscriptions (send with Expire: 0) */
6750 if (!g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
6752 sipe_process_provisioning_v2(sip
, msg
);
6754 else if (!g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning"))
6756 sipe_process_provisioning(sip
, msg
);
6758 else if (!g_ascii_strcasecmp(event
, "presence"))
6760 sipe_process_presence(sip
, msg
);
6762 else if (!g_ascii_strcasecmp(event
, "registration-notify"))
6764 sipe_process_registration_notify(sip
, msg
);
6767 if (!subscription_state
|| strstr(subscription_state
, "active"))
6769 if (!g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
6771 sipe_process_roaming_contacts(sip
, msg
);
6773 else if (!g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
6775 sipe_process_roaming_self(sip
, msg
);
6777 else if (!g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
6779 sipe_process_roaming_acl(sip
, msg
);
6781 else if (!g_ascii_strcasecmp(event
, "presence.wpending"))
6783 sipe_process_presence_wpending(sip
, msg
);
6785 else if (!g_ascii_strcasecmp(event
, "conference"))
6787 sipe_process_conference(sip
, msg
);
6792 /* The server sends status 'terminated' */
6793 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
6794 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6795 gchar
*key
= sipe_get_subscription_key(event
, who
);
6797 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
6800 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
6801 g_hash_table_remove(sip
->subscriptions
, key
);
6802 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
6808 if (!request
&& event
) {
6809 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
6810 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
6811 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
6814 /* 2 min ahead of expiration */
6815 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
6817 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
6818 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
6820 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
6821 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
6822 g_free(action_name
);
6824 else if (!g_ascii_strcasecmp(event
, "presence") &&
6825 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
6827 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
6828 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6830 if (sip
->batched_support
) {
6831 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
6834 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6835 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
6837 g_free(action_name
);
6843 /* The client responses on received a NOTIFY message */
6844 if (request
&& !benotify
)
6846 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
6851 * Whether user manually changed status or
6852 * it was changed automatically due to user
6853 * became inactive/active again
6856 sipe_is_user_state(struct sipe_account_data
*sip
)
6859 time_t now
= time(NULL
);
6861 purple_debug_info("sipe", "sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
6862 purple_debug_info("sipe", "sipe_is_user_state: now : %s", asctime(localtime(&now
)));
6864 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
6866 purple_debug_info("sipe", "sipe_is_user_state: res = %s\n", res
? "USER" : "MACHINE");
6871 send_presence_soap0(struct sipe_account_data
*sip
,
6872 gboolean do_publish_calendar
,
6873 gboolean do_reset_status
)
6875 struct sipe_ews
* ews
= sip
->ews
;
6876 int availability
= 0;
6881 gchar
*res_note
= NULL
;
6882 gchar
*res_oof
= NULL
;
6883 const gchar
*note_pub
= NULL
;
6884 gchar
*states
= NULL
;
6885 gchar
*calendar_data
= NULL
;
6886 gchar
*epid
= get_epid(sip
);
6887 time_t now
= time(NULL
);
6888 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
6889 const gchar
*oof_note
= ews
? sipe_ews_get_oof_note(ews
) : NULL
;
6890 const char *user_input
;
6891 gboolean pub_oof
= ews
&& oof_note
&& (!sip
->note
|| ews
->updated
> sip
->note_since
);
6893 if (oof_note
&& sip
->note
) {
6894 purple_debug_info("sipe", "ews->oof_start : %s", asctime(localtime(&(ews
->oof_start
))));
6895 purple_debug_info("sipe", "sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
6898 purple_debug_info("sipe", "sip->note : %s", sip
->note
? sip
->note
: "");
6900 if (!sip
->initial_state_published
||
6903 g_free(sip
->status
);
6904 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
6907 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
6911 note_pub
= oof_note
;
6912 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
6913 ews
->published
= TRUE
;
6914 } else if (sip
->note
) {
6915 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in ews already */
6918 sip
->is_oof_note
= FALSE
;
6919 sip
->note_since
= 0;
6921 note_pub
= sip
->note
;
6922 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
6928 /* to protocol internal plain text format */
6929 tmp
= purple_markup_strip_html(note_pub
);
6930 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
6935 if (!do_reset_status
) {
6936 if (sipe_is_user_state(sip
) && !do_publish_calendar
&& sip
->initial_state_published
)
6938 gchar
*activity_token
= NULL
;
6939 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
6941 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
6946 g_free(activity_token
);
6948 else /* preserve existing publication */
6951 if (sip
->user_info
&& (xn_states
= xmlnode_get_child(sip
->user_info
, "states"))) {
6952 states
= xmlnode_to_str(xn_states
, NULL
);
6953 /* this is a hack-around to remove added newline after inner element,
6954 * state in this case, where it shouldn't be.
6955 * After several use of xmlnode_to_str, amount of added newlines
6956 * grows significantly.
6958 purple_str_strip_char(states
, '\n');
6959 //purple_str_strip_char(states, '\r');
6963 /* do nothing - then User state will be erased */
6965 sip
->initial_state_published
= TRUE
;
6968 if (ews
&& (!is_empty(ews
->legacy_dn
) || !is_empty(ews
->email
)) && ews
->fb_start
&& !is_empty(ews
->free_busy
))
6970 char *fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
6971 char *free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
6972 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
6973 !is_empty(ews
->legacy_dn
) ? ews
->legacy_dn
: ews
->email
,
6976 g_free(fb_start_str
);
6977 g_free(free_busy_base64
);
6980 user_input
= !sipe_is_user_state(sip
) && sip
->status
!= SIPE_STATUS_ID_AVAILABLE
? "idle" : "active";
6982 /* forming resulting XML */
6983 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
6987 (tmp
= g_ascii_strup(sipe_get_host_name(), -1)),
6988 res_note
? res_note
: "",
6989 res_oof
? res_oof
: "",
6990 states
? states
: "",
6991 calendar_data
? calendar_data
: "",
7000 g_free(calendar_data
);
7002 send_soap_request(sip
, body
);
7005 g_free(since_time_str
);
7010 send_presence_soap(struct sipe_account_data
*sip
,
7011 gboolean do_publish_calendar
)
7013 return send_presence_soap0(sip
, do_publish_calendar
, FALSE
);
7018 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
7020 struct transaction
*trans
)
7022 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
7024 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
7030 gboolean has_device_publication
= FALSE
;
7032 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
7034 /* test if version mismatch fault */
7035 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
7036 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
7037 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
7044 /* accumulating information about faulty versions */
7045 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
7046 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
7048 node
= xmlnode_get_next_twin(node
))
7050 const gchar
*index
= xmlnode_get_attrib(node
, "index");
7051 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
7053 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
7054 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
7058 /* here we are parsing own request to figure out what publication
7059 * referensed here only by index went wrong
7061 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
7064 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
7065 index_our
= 1; /* starts with 1 - our first publication */
7067 node
= xmlnode_get_next_twin(node
), index_our
++)
7069 gchar
*idx
= g_strdup_printf("%d", index_our
);
7070 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
7071 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
7074 if (sipe_strequal("device", categoryName
)) {
7075 has_device_publication
= TRUE
;
7078 if (curVersion
) { /* fault exist on this index */
7079 const gchar
*container
= xmlnode_get_attrib(node
, "container");
7080 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
7081 /* key is <category><instance><container> */
7082 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
7083 struct sipe_publication
*publication
=
7084 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
7086 purple_debug_info("sipe", "key is %s\n", key
);
7089 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
7090 key
, curVersion
, publication
->version
);
7091 /* updating publication's version to the correct one */
7092 publication
->version
= atoi(curVersion
);
7098 g_hash_table_destroy(faults
);
7100 /* rebublishing with right versions */
7101 if (has_device_publication
) {
7102 send_publish_category_initial(sip
);
7104 send_presence_status(sip
);
7111 * Returns 'device' XML part for publication.
7112 * Must be g_free'd after use.
7115 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
7119 gchar
*epid
= get_epid(sip
);
7120 gchar
*uuid
= generateUUIDfromEPID(epid
);
7121 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
7122 /* key is <category><instance><container> */
7123 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
7124 struct sipe_publication
*publication
=
7125 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
7130 uri
= sip_uri_self(sip
);
7131 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
7133 publication
? publication
->version
: 0,
7136 "00:00:00+01:00", /* @TODO make timezone real*/
7137 sipe_get_host_name()
7147 * A service method - use
7148 * - send_publish_get_category_state_machine and
7149 * - send_publish_get_category_state_user instead.
7150 * Must be g_free'd after use.
7153 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
7154 gboolean is_user_state
)
7156 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
7157 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
7158 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
7159 /* key is <category><instance><container> */
7160 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7161 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7162 struct sipe_publication
*publication_2
=
7163 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7164 struct sipe_publication
*publication_3
=
7165 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7170 if (publication_2
&& (publication_2
->availability
== availability
))
7172 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
7173 return NULL
; /* nothing to update */
7176 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
7178 publication_2
? publication_2
->version
: 0,
7181 publication_3
? publication_3
->version
: 0,
7186 * Only Busy and OOF calendar event are published.
7187 * Different instances are used for that.
7189 * Must be g_free'd after use.
7192 sipe_publish_get_category_state_calendar(struct sipe_account_data
*sip
,
7193 struct sipe_cal_event
*event
,
7197 gchar
*start_time_str
;
7198 int availability
= 0;
7201 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
7202 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
) :
7203 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
7205 /* key is <category><instance><container> */
7206 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7207 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7208 struct sipe_publication
*publication_2
=
7209 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7210 struct sipe_publication
*publication_3
=
7211 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7216 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
7217 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7218 "Exiting as no publication and no event for cal_satus:%d\n", cal_satus
);
7224 (publication_3
->availability
== availability
) &&
7225 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
7228 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7229 "cal state has NOT changed for cal_satus:%d. Exiting.\n", cal_satus
);
7230 return NULL
; /* nothing to update */
7235 (event
->cal_status
== SIPE_CAL_BUSY
||
7236 event
->cal_status
== SIPE_CAL_OOF
))
7238 gchar
*availability_xml_str
= NULL
;
7239 gchar
*activity_xml_str
= NULL
;
7241 if (event
->cal_status
== SIPE_CAL_BUSY
) {
7242 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
7245 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
7246 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7247 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
7248 "minAvailability=\"6500\"",
7249 "maxAvailability=\"8999\"");
7250 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
7251 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7252 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
7253 "minAvailability=\"12000\"",
7256 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
7258 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
7260 publication_2
? publication_2
->version
: 0,
7263 availability_xml_str
? availability_xml_str
: "",
7264 activity_xml_str
? activity_xml_str
: "",
7265 event
->subject
? event
->subject
: "",
7266 event
->location
? event
->location
: "",
7269 publication_3
? publication_3
->version
: 0,
7272 availability_xml_str
? availability_xml_str
: "",
7273 activity_xml_str
? activity_xml_str
: "",
7274 event
->subject
? event
->subject
: "",
7275 event
->location
? event
->location
: ""
7277 g_free(start_time_str
);
7278 g_free(availability_xml_str
);
7279 g_free(activity_xml_str
);
7282 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
7284 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
7286 publication_2
? publication_2
->version
: 0,
7289 publication_3
? publication_3
->version
: 0
7297 * Returns 'machineState' XML part for publication.
7298 * Must be g_free'd after use.
7301 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
7303 return sipe_publish_get_category_state(sip
, FALSE
);
7307 * Returns 'userState' XML part for publication.
7308 * Must be g_free'd after use.
7311 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
7313 return sipe_publish_get_category_state(sip
, TRUE
);
7317 * Returns 'note' XML part for publication.
7318 * Must be g_free'd after use.
7320 * Protocol format for Note is plain text.
7322 * @param note a note in Sipe internal HTML format
7323 * @param note_type either personal or OOF
7326 sipe_publish_get_category_note(struct sipe_account_data
*sip
,
7327 const char *note
, /* html */
7328 const char *note_type
,
7332 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
) : 0;
7333 /* key is <category><instance><container> */
7334 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
7335 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
7336 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
7338 struct sipe_publication
*publication_note_200
=
7339 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
7340 struct sipe_publication
*publication_note_300
=
7341 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
7342 struct sipe_publication
*publication_note_400
=
7343 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
7345 char *tmp
= note
? purple_markup_strip_html(note
) : NULL
;
7346 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
7347 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
7348 char *res
, *tmp1
, *tmp2
, *tmp3
;
7349 char *start_time_attr
;
7350 char *end_time_attr
;
7354 g_free(key_note_200
);
7355 g_free(key_note_300
);
7356 g_free(key_note_400
);
7358 /* we even need to republish empty note */
7359 if (sipe_strequal(n1
, n2
))
7361 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
7363 return NULL
; /* nothing to update */
7366 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
7369 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
7373 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7376 publication_note_200
? publication_note_200
->version
: 0,
7378 start_time_attr
? start_time_attr
: "",
7379 end_time_attr
? end_time_attr
: "",
7382 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7385 publication_note_300
? publication_note_300
->version
: 0,
7387 start_time_attr
? start_time_attr
: "",
7388 end_time_attr
? end_time_attr
: "",
7391 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7394 publication_note_400
? publication_note_400
->version
: 0,
7396 start_time_attr
? start_time_attr
: "",
7397 end_time_attr
? end_time_attr
: "",
7400 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7404 publication_note_200
? publication_note_200
->version
: 0,
7406 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7410 publication_note_200
? publication_note_200
->version
: 0,
7412 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7416 publication_note_200
? publication_note_200
->version
: 0,
7419 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
7421 g_free(start_time_attr
);
7422 g_free(end_time_attr
);
7432 * Returns 'calendarData' XML part with WorkingHours for publication.
7433 * Must be g_free'd after use.
7436 sipe_publish_get_category_cal_working_hours(struct sipe_account_data
*sip
)
7438 struct sipe_ews
* ews
= sip
->ews
;
7440 /* key is <category><instance><container> */
7441 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
7442 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
7443 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
7444 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
7445 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
7446 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
7448 struct sipe_publication
*publication_cal_1
=
7449 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7450 struct sipe_publication
*publication_cal_100
=
7451 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7452 struct sipe_publication
*publication_cal_200
=
7453 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7454 struct sipe_publication
*publication_cal_300
=
7455 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7456 struct sipe_publication
*publication_cal_400
=
7457 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7458 struct sipe_publication
*publication_cal_32000
=
7459 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7461 const char *n1
= ews
? ews
->working_hours_xml_str
: NULL
;
7462 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
7465 g_free(key_cal_100
);
7466 g_free(key_cal_200
);
7467 g_free(key_cal_300
);
7468 g_free(key_cal_400
);
7469 g_free(key_cal_32000
);
7471 if (!ews
|| is_empty(ews
->email
) || is_empty(ews
->working_hours_xml_str
)) {
7472 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: no data to publish, exiting\n");
7476 if (sipe_strequal(n1
, n2
))
7478 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.\n");
7479 return NULL
; /* nothing to update */
7482 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
7484 publication_cal_1
? publication_cal_1
->version
: 0,
7486 ews
->working_hours_xml_str
,
7488 publication_cal_100
? publication_cal_100
->version
: 0,
7490 publication_cal_200
? publication_cal_200
->version
: 0,
7492 ews
->working_hours_xml_str
,
7494 publication_cal_300
? publication_cal_300
->version
: 0,
7496 ews
->working_hours_xml_str
,
7497 /* 400 - Personal */
7498 publication_cal_400
? publication_cal_400
->version
: 0,
7500 ews
->working_hours_xml_str
,
7501 /* 32000 - Blocked */
7502 publication_cal_32000
? publication_cal_32000
->version
: 0
7507 * Returns 'calendarData' XML part with FreeBusy for publication.
7508 * Must be g_free'd after use.
7511 sipe_publish_get_category_cal_free_busy(struct sipe_account_data
*sip
)
7513 struct sipe_ews
* ews
= sip
->ews
;
7514 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
7516 char *free_busy_base64
;
7521 /* key is <category><instance><container> */
7522 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
7523 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
7524 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
7525 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
7526 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
7527 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
7529 struct sipe_publication
*publication_cal_1
=
7530 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7531 struct sipe_publication
*publication_cal_100
=
7532 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7533 struct sipe_publication
*publication_cal_200
=
7534 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7535 struct sipe_publication
*publication_cal_300
=
7536 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7537 struct sipe_publication
*publication_cal_400
=
7538 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7539 struct sipe_publication
*publication_cal_32000
=
7540 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7543 g_free(key_cal_100
);
7544 g_free(key_cal_200
);
7545 g_free(key_cal_300
);
7546 g_free(key_cal_400
);
7547 g_free(key_cal_32000
);
7549 if (!ews
|| is_empty(ews
->email
) || !ews
->fb_start
|| is_empty(ews
->free_busy
)) {
7550 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: no data to publish, exiting\n");
7554 fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
7555 free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
7557 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
7558 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
7560 /* we will rebuplish the same data to refresh publication time,
7561 * so if data from multiple sources, most recent will be choosen
7563 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
7565 // purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.\n");
7566 // g_free(fb_start_str);
7567 // g_free(free_busy_base64);
7568 // return NULL; /* nothing to update */
7571 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
7574 publication_cal_1
? publication_cal_1
->version
: 0,
7577 publication_cal_100
? publication_cal_100
->version
: 0,
7580 publication_cal_200
? publication_cal_200
->version
: 0,
7586 publication_cal_300
? publication_cal_300
->version
: 0,
7590 /* 400 - Personal */
7592 publication_cal_400
? publication_cal_400
->version
: 0,
7596 /* 32000 - Blocked */
7598 publication_cal_32000
? publication_cal_32000
->version
: 0
7601 g_free(fb_start_str
);
7602 g_free(free_busy_base64
);
7606 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
7613 uri
= sip_uri_self(sip
);
7614 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
7618 tmp
= get_contact(sip
);
7619 hdr
= g_strdup_printf("Contact: %s\r\n"
7620 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
7622 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
7631 send_publish_category_initial(struct sipe_account_data
*sip
)
7633 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
7635 gchar
*publications
;
7637 g_free(sip
->status
);
7638 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
7640 pub_machine
= sipe_publish_get_category_state_machine(sip
);
7641 publications
= g_strdup_printf("%s%s",
7643 pub_machine
? pub_machine
: "");
7645 g_free(pub_machine
);
7647 send_presence_publish(sip
, publications
);
7648 g_free(publications
);
7652 send_presence_category_publish(struct sipe_account_data
*sip
)
7654 gchar
*pub_state
= sipe_is_user_state(sip
) ?
7655 sipe_publish_get_category_state_user(sip
) :
7656 sipe_publish_get_category_state_machine(sip
);
7657 gchar
*pub_note
= sipe_publish_get_category_note(sip
,
7659 sip
->is_oof_note
? "OOF" : "personal",
7662 gchar
*publications
;
7664 if (!pub_state
&& !pub_note
) {
7665 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
7669 publications
= g_strdup_printf("%s%s",
7670 pub_state
? pub_state
: "",
7671 pub_note
? pub_note
: "");
7676 send_presence_publish(sip
, publications
);
7677 g_free(publications
);
7681 * Publishes self status
7682 * based on own calendar information.
7687 publish_calendar_status_self(struct sipe_account_data
*sip
)
7689 struct sipe_cal_event
* event
= NULL
;
7690 gchar
*pub_cal_working_hours
= NULL
;
7691 gchar
*pub_cal_free_busy
= NULL
;
7692 gchar
*pub_calendar
= NULL
;
7693 gchar
*pub_calendar2
= NULL
;
7694 gchar
*pub_oof_note
= NULL
;
7695 const gchar
*oof_note
;
7696 time_t oof_start
= 0;
7700 purple_debug_info("sipe", "publish_calendar_status_self() no calendar data.\n");
7704 purple_debug_info("sipe", "publish_calendar_status_self() started.\n");
7705 if (sip
->ews
->cal_events
) {
7706 event
= sipe_cal_get_event(sip
->ews
->cal_events
, time(NULL
));
7710 purple_debug_info("sipe", "publish_calendar_status_self: current event is NULL\n");
7712 char *desc
= sipe_cal_event_describe(event
);
7713 purple_debug_info("sipe", "publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
7719 OOF publish, Busy clean
7721 OOF clean, Busy publish
7723 OOF clean, Busy clean
7725 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
7726 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_OOF
);
7727 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7728 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
7729 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7730 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7732 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7733 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7736 oof_note
= sipe_ews_get_oof_note(sip
->ews
);
7737 if (sipe_strequal("Scheduled", sip
->ews
->oof_state
)) {
7738 oof_start
= sip
->ews
->oof_start
;
7739 oof_end
= sip
->ews
->oof_end
;
7741 pub_oof_note
= sipe_publish_get_category_note(sip
, oof_note
, "OOF", oof_start
, oof_end
);
7743 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sip
);
7744 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sip
);
7746 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
7747 purple_debug_info("sipe", "publish_calendar_status_self: nothing has changed.\n");
7749 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
7750 pub_cal_working_hours
? pub_cal_working_hours
: "",
7751 pub_cal_free_busy
? pub_cal_free_busy
: "",
7752 pub_calendar
? pub_calendar
: "",
7753 pub_calendar2
? pub_calendar2
: "",
7754 pub_oof_note
? pub_oof_note
: "");
7756 send_presence_publish(sip
, publications
);
7757 g_free(publications
);
7760 g_free(pub_cal_working_hours
);
7761 g_free(pub_cal_free_busy
);
7762 g_free(pub_calendar
);
7763 g_free(pub_calendar2
);
7764 g_free(pub_oof_note
);
7766 /* repeat scheduling */
7767 sipe_sched_calendar_status_self_publish(sip
, time(NULL
));
7770 static void send_presence_status(struct sipe_account_data
*sip
)
7772 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
7774 if (!status
) return;
7776 purple_debug_info("sipe", "send_presence_status: status: %s (%s)\n",
7777 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
7778 sipe_is_user_state(sip
) ? "USER" : "MACHINE");
7781 send_presence_category_publish(sip
);
7783 send_presence_soap(sip
, FALSE
);
7787 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
7789 gboolean found
= FALSE
;
7790 const char *method
= msg
->method
? msg
->method
: "NOT FOUND";
7791 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,method
);
7792 if (msg
->response
== 0) { /* request */
7793 if (sipe_strequal(method
, "MESSAGE")) {
7794 process_incoming_message(sip
, msg
);
7796 } else if (sipe_strequal(method
, "NOTIFY")) {
7797 purple_debug_info("sipe","send->process_incoming_notify\n");
7798 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
7800 } else if (sipe_strequal(method
, "BENOTIFY")) {
7801 purple_debug_info("sipe","send->process_incoming_benotify\n");
7802 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
7804 } else if (sipe_strequal(method
, "INVITE")) {
7805 process_incoming_invite(sip
, msg
);
7807 } else if (sipe_strequal(method
, "REFER")) {
7808 process_incoming_refer(sip
, msg
);
7810 } else if (sipe_strequal(method
, "OPTIONS")) {
7811 process_incoming_options(sip
, msg
);
7813 } else if (sipe_strequal(method
, "INFO")) {
7814 process_incoming_info(sip
, msg
);
7816 } else if (sipe_strequal(method
, "ACK")) {
7817 // ACK's don't need any response
7819 } else if (sipe_strequal(method
, "SUBSCRIBE")) {
7820 // LCS 2005 sends us these - just respond 200 OK
7822 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
7823 } else if (sipe_strequal(method
, "BYE")) {
7824 process_incoming_bye(sip
, msg
);
7827 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
7829 } else { /* response */
7830 struct transaction
*trans
= transactions_find(sip
, msg
);
7832 if (msg
->response
== 407) {
7833 gchar
*resend
, *auth
;
7836 if (sip
->proxy
.retries
> 30) return;
7837 sip
->proxy
.retries
++;
7838 /* do proxy authentication */
7840 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
7842 fill_auth(ptmp
, &sip
->proxy
);
7843 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
7844 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7845 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7847 resend
= sipmsg_to_string(trans
->msg
);
7848 /* resend request */
7849 sendout_pkt(sip
->gc
, resend
);
7852 if (msg
->response
< 200) {
7853 /* ignore provisional response */
7854 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
7856 sip
->proxy
.retries
= 0;
7857 if (sipe_strequal(trans
->msg
->method
, "REGISTER")) {
7858 if (msg
->response
== 401)
7860 sip
->registrar
.retries
++;
7864 sip
->registrar
.retries
= 0;
7866 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
7868 if (msg
->response
== 401) {
7869 gchar
*resend
, *auth
, *ptmp
;
7870 const char* auth_scheme
;
7872 if (sip
->registrar
.retries
> 4) return;
7873 sip
->registrar
.retries
++;
7875 auth_scheme
= sipe_get_auth_scheme_name(sip
);
7876 ptmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
7878 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
? ptmp
: "");
7880 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
7881 sip
->gc
->wants_to_die
= TRUE
;
7882 purple_connection_error(sip
->gc
, tmp2
);
7887 fill_auth(ptmp
, &sip
->registrar
);
7888 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
7889 sipmsg_remove_header_now(trans
->msg
, "Authorization");
7890 sipmsg_add_header_now_pos(trans
->msg
, "Authorization", auth
, 5);
7892 resend
= sipmsg_to_string(trans
->msg
);
7893 /* resend request */
7894 sendout_pkt(sip
->gc
, resend
);
7899 if (trans
->callback
) {
7900 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
7901 /* call the callback to process response*/
7902 (trans
->callback
)(sip
, msg
, trans
);
7905 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
7906 transactions_remove(sip
, trans
);
7912 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
7916 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", method
, msg
->response
);
7920 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
7929 /* according to the RFC remove CRLF at the beginning */
7930 while (*cur
== '\r' || *cur
== '\n') {
7933 if (cur
!= conn
->inbuf
) {
7934 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
7935 conn
->inbufused
= strlen(conn
->inbuf
);
7938 /* Received a full Header? */
7939 sip
->processing_input
= TRUE
;
7940 while (sip
->processing_input
&&
7941 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
7942 time_t currtime
= time(NULL
);
7945 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
7947 msg
= sipmsg_parse_header(conn
->inbuf
);
7950 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
7951 if (msg
&& restlen
>= msg
->bodylen
) {
7952 dummy
= g_malloc(msg
->bodylen
+ 1);
7953 memcpy(dummy
, cur
, msg
->bodylen
);
7954 dummy
[msg
->bodylen
] = '\0';
7956 cur
+= msg
->bodylen
;
7957 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
7958 conn
->inbufused
= strlen(conn
->inbuf
);
7961 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
7968 purple_debug_info("sipe", "body:\n%s", msg->body);
7971 // Verify the signature before processing it
7972 if (sip
->registrar
.gssapi_context
) {
7973 struct sipmsg_breakdown msgbd
;
7974 gchar
*signature_input_str
;
7977 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
7978 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
7980 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
7982 if (rspauth
!= NULL
) {
7983 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
7984 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
7985 process_input_message(sip
, msg
);
7987 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
7988 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
7989 sip
->gc
->wants_to_die
= TRUE
;
7991 } else if (msg
->response
== 401) {
7992 purple_connection_error(sip
->gc
, _("Authentication failed"));
7993 sip
->gc
->wants_to_die
= TRUE
;
7995 g_free(signature_input_str
);
7998 sipmsg_breakdown_free(&msgbd
);
8000 process_input_message(sip
, msg
);
8007 static void sipe_udp_process(gpointer data
, gint source
,
8008 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
8010 PurpleConnection
*gc
= data
;
8011 struct sipe_account_data
*sip
= gc
->proto_data
;
8014 static char buffer
[65536];
8015 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
8016 time_t currtime
= time(NULL
);
8019 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
8020 msg
= sipmsg_parse_msg(buffer
);
8021 if (msg
) process_input_message(sip
, msg
);
8025 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
8027 struct sipe_account_data
*sip
= gc
->proto_data
;
8028 PurpleSslConnection
*gsc
= sip
->gsc
;
8030 purple_debug_error("sipe", "%s",debug
);
8031 purple_connection_error(gc
, msg
);
8033 /* Invalidate this connection. Next send will open a new one */
8035 connection_remove(sip
, gsc
->fd
);
8036 purple_ssl_close(gsc
);
8042 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8043 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8045 PurpleConnection
*gc
= data
;
8046 struct sipe_account_data
*sip
;
8047 struct sip_connection
*conn
;
8049 gboolean firstread
= TRUE
;
8051 /* NOTE: This check *IS* necessary */
8052 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
8053 purple_ssl_close(gsc
);
8057 sip
= gc
->proto_data
;
8058 conn
= connection_find(sip
, gsc
->fd
);
8060 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
8061 gc
->wants_to_die
= TRUE
;
8062 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
8066 /* Read all available data from the SSL connection */
8068 /* Increase input buffer size as needed */
8069 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8070 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8071 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8072 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
8075 /* Try to read as much as there is space left in the buffer */
8076 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
8077 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
8079 if (len
< 0 && errno
== EAGAIN
) {
8080 /* Try again later */
8082 } else if (len
< 0) {
8083 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
8085 } else if (firstread
&& (len
== 0)) {
8086 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
8090 conn
->inbufused
+= len
;
8093 /* Equivalence indicates that there is possibly more data to read */
8094 } while (len
== readlen
);
8096 conn
->inbuf
[conn
->inbufused
] = '\0';
8097 process_input(sip
, conn
);
8101 static void sipe_input_cb(gpointer data
, gint source
,
8102 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8104 PurpleConnection
*gc
= data
;
8105 struct sipe_account_data
*sip
= gc
->proto_data
;
8107 struct sip_connection
*conn
= connection_find(sip
, source
);
8109 purple_debug_error("sipe", "Connection not found!\n");
8113 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8114 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8115 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8118 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
8120 if (len
< 0 && errno
== EAGAIN
)
8122 else if (len
<= 0) {
8123 purple_debug_info("sipe", "sipe_input_cb: read error\n");
8124 connection_remove(sip
, source
);
8125 if (sip
->fd
== source
) sip
->fd
= -1;
8129 conn
->inbufused
+= len
;
8130 conn
->inbuf
[conn
->inbufused
] = '\0';
8132 process_input(sip
, conn
);
8135 /* Callback for new connections on incoming TCP port */
8136 static void sipe_newconn_cb(gpointer data
, gint source
,
8137 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8139 PurpleConnection
*gc
= data
;
8140 struct sipe_account_data
*sip
= gc
->proto_data
;
8141 struct sip_connection
*conn
;
8143 int newfd
= accept(source
, NULL
, NULL
);
8145 conn
= connection_create(sip
, newfd
);
8147 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8150 static void login_cb(gpointer data
, gint source
,
8151 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
8153 PurpleConnection
*gc
= data
;
8154 struct sipe_account_data
*sip
;
8155 struct sip_connection
*conn
;
8157 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8165 purple_connection_error(gc
, _("Could not connect"));
8169 sip
= gc
->proto_data
;
8171 sip
->last_keepalive
= time(NULL
);
8173 conn
= connection_create(sip
, source
);
8177 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8180 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8181 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8183 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
8184 if (sip
== NULL
) return;
8189 static guint
sipe_ht_hash_nick(const char *nick
)
8191 char *lc
= g_utf8_strdown(nick
, -1);
8192 guint bucket
= g_str_hash(lc
);
8198 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
8200 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
8203 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
8205 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8207 sip
->listen_data
= NULL
;
8209 if (listenfd
== -1) {
8210 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8216 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
8217 sip
->listenfd
= sip
->fd
;
8219 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
8221 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
8225 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
8226 SIPE_UNUSED_PARAMETER
const char *error_message
)
8228 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8230 sip
->query_data
= NULL
;
8232 if (!hosts
|| !hosts
->data
) {
8233 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
8237 hosts
= g_slist_remove(hosts
, hosts
->data
);
8238 g_free(sip
->serveraddr
);
8239 sip
->serveraddr
= hosts
->data
;
8240 hosts
= g_slist_remove(hosts
, hosts
->data
);
8242 void *tmp
= hosts
->data
;
8243 hosts
= g_slist_remove(hosts
, tmp
);
8244 hosts
= g_slist_remove(hosts
, tmp
);
8248 /* create socket for incoming connections */
8249 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
8250 sipe_udp_host_resolved_listen_cb
, sip
);
8251 if (sip
->listen_data
== NULL
) {
8252 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8257 static const struct sipe_service_data
*current_service
= NULL
;
8259 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
8260 PurpleSslErrorType error
,
8263 PurpleConnection
*gc
= data
;
8264 struct sipe_account_data
*sip
;
8266 /* If the connection is already disconnected, we don't need to do anything else */
8267 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8270 sip
= gc
->proto_data
;
8271 current_service
= sip
->service_data
;
8272 if (current_service
) {
8273 purple_debug_info("sipe", "current_service: transport '%s' service '%s'\n",
8274 current_service
->transport
? current_service
->transport
: "NULL",
8275 current_service
->service
? current_service
->service
: "NULL");
8282 case PURPLE_SSL_CONNECT_FAILED
:
8283 purple_connection_error(gc
, _("Connection failed"));
8285 case PURPLE_SSL_HANDSHAKE_FAILED
:
8286 purple_connection_error(gc
, _("SSL handshake failed"));
8288 case PURPLE_SSL_CERTIFICATE_INVALID
:
8289 purple_connection_error(gc
, _("SSL certificate invalid"));
8295 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
8297 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8298 PurpleProxyConnectData
*connect_data
;
8300 sip
->listen_data
= NULL
;
8302 sip
->listenfd
= listenfd
;
8303 if (sip
->listenfd
== -1) {
8304 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8308 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
8309 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
8310 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
8311 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
8312 sipe_newconn_cb
, sip
->gc
);
8313 purple_debug_info("sipe", "connecting to %s port %d\n",
8314 sip
->realhostname
, sip
->realport
);
8315 /* open tcp connection to the server */
8316 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
8317 sip
->realport
, login_cb
, sip
->gc
);
8319 if (connect_data
== NULL
) {
8320 purple_connection_error(sip
->gc
, _("Could not create socket"));
8324 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
8326 PurpleAccount
*account
= sip
->account
;
8327 PurpleConnection
*gc
= sip
->gc
;
8330 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
8333 sip
->realhostname
= hostname
;
8334 sip
->realport
= port
;
8336 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
8339 /* TODO: is there a good default grow size? */
8340 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
8341 sip
->txbuf
= purple_circ_buffer_new(0);
8343 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
8345 if (!purple_ssl_is_supported()) {
8346 gc
->wants_to_die
= TRUE
;
8347 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
8351 purple_debug_info("sipe", "using SSL\n");
8353 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
8354 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
8355 if (sip
->gsc
== NULL
) {
8356 purple_connection_error(gc
, _("Could not create SSL context"));
8359 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
8361 purple_debug_info("sipe", "using UDP\n");
8363 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
8364 if (sip
->query_data
== NULL
) {
8365 purple_connection_error(gc
, _("Could not resolve hostname"));
8369 purple_debug_info("sipe", "using TCP\n");
8370 /* create socket for incoming connections */
8371 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
8372 sipe_tcp_connect_listen_cb
, sip
);
8373 if (sip
->listen_data
== NULL
) {
8374 purple_connection_error(gc
, _("Could not create listen socket"));
8380 /* Service list for autodection */
8381 static const struct sipe_service_data service_autodetect
[] = {
8382 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8383 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8384 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8385 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8389 /* Service list for SSL/TLS */
8390 static const struct sipe_service_data service_tls
[] = {
8391 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8392 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8396 /* Service list for TCP */
8397 static const struct sipe_service_data service_tcp
[] = {
8398 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8399 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8403 /* Service list for UDP */
8404 static const struct sipe_service_data service_udp
[] = {
8405 { "sip", "udp", SIPE_TRANSPORT_UDP
},
8409 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
8410 static void resolve_next_service(struct sipe_account_data
*sip
,
8411 const struct sipe_service_data
*start
)
8414 sip
->service_data
= start
;
8416 sip
->service_data
++;
8417 if (sip
->service_data
->service
== NULL
) {
8419 /* Try connecting to the SIP hostname directly */
8420 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
8421 if (sip
->auto_transport
) {
8422 // If SSL is supported, default to using it; OCS servers aren't configured
8423 // by default to accept TCP
8424 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
8425 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8426 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
8429 hostname
= g_strdup(sip
->sipdomain
);
8430 create_connection(sip
, hostname
, 0);
8435 /* Try to resolve next service */
8436 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
8437 sip
->service_data
->transport
,
8442 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
8444 struct sipe_account_data
*sip
= data
;
8446 sip
->srv_query_data
= NULL
;
8448 /* find the host to connect to */
8450 gchar
*hostname
= g_strdup(resp
->hostname
);
8451 int port
= resp
->port
;
8452 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
8456 sip
->transport
= sip
->service_data
->type
;
8458 create_connection(sip
, hostname
, port
);
8460 resolve_next_service(sip
, NULL
);
8464 static void sipe_login(PurpleAccount
*account
)
8466 PurpleConnection
*gc
;
8467 struct sipe_account_data
*sip
;
8468 gchar
**signinname_login
, **userserver
;
8469 const char *transport
;
8472 const char *username
= purple_account_get_username(account
);
8473 gc
= purple_account_get_connection(account
);
8475 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
8477 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
8478 gc
->wants_to_die
= TRUE
;
8479 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
8483 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
8484 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
8485 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
8487 sip
->account
= account
;
8488 sip
->reregister_set
= FALSE
;
8489 sip
->reauthenticate_set
= FALSE
;
8490 sip
->subscribed
= FALSE
;
8491 sip
->subscribed_buddies
= FALSE
;
8492 sip
->initial_state_published
= FALSE
;
8494 /* username format: <username>,[<optional login>] */
8495 signinname_login
= g_strsplit(username
, ",", 2);
8496 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
8498 /* ensure that username format is name@domain */
8499 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
8500 g_strfreev(signinname_login
);
8501 gc
->wants_to_die
= TRUE
;
8502 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
8505 sip
->username
= g_strdup(signinname_login
[0]);
8507 /* ensure that email format is name@domain if provided */
8508 email
= purple_account_get_string(sip
->account
, "email", NULL
);
8509 if (!is_empty(email
) &&
8510 (!strchr(email
, '@') || g_str_has_prefix(email
, "@") || g_str_has_suffix(email
, "@")))
8512 gc
->wants_to_die
= TRUE
;
8513 purple_connection_error(gc
, _("Email address should be valid if provided\nExample: user@company.com"));
8516 sip
->email
= !is_empty(email
) ? g_strdup(email
) : g_strdup(sip
->username
);
8518 /* login name specified? */
8519 if (signinname_login
[1] && strlen(signinname_login
[1])) {
8520 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
8521 gboolean has_domain
= domain_user
[1] != NULL
;
8522 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
8523 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
8524 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
8525 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
8526 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
8527 g_strfreev(domain_user
);
8530 userserver
= g_strsplit(signinname_login
[0], "@", 2);
8531 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
8532 purple_connection_set_display_name(gc
, userserver
[0]);
8533 sip
->sipdomain
= g_strdup(userserver
[1]);
8534 g_strfreev(userserver
);
8535 g_strfreev(signinname_login
);
8537 if (strchr(sip
->username
, ' ') != NULL
) {
8538 gc
->wants_to_die
= TRUE
;
8539 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
8543 sip
->password
= g_strdup(purple_connection_get_password(gc
));
8545 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
8546 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8547 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
8548 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8549 g_free
, (GDestroyNotify
)sipe_subscription_free
);
8551 sip
->filetransfers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,g_free
,NULL
);
8553 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
8555 g_free(sip
->status
);
8556 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
8558 sip
->auto_transport
= FALSE
;
8559 transport
= purple_account_get_string(account
, "transport", "auto");
8560 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
8561 if (userserver
[0]) {
8562 /* Use user specified server[:port] */
8566 port
= atoi(userserver
[1]);
8568 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
8569 userserver
[0], port
);
8571 if (sipe_strequal(transport
, "auto")) {
8572 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8573 } else if (sipe_strequal(transport
, "tls")) {
8574 sip
->transport
= SIPE_TRANSPORT_TLS
;
8575 } else if (sipe_strequal(transport
, "tcp")) {
8576 sip
->transport
= SIPE_TRANSPORT_TCP
;
8578 sip
->transport
= SIPE_TRANSPORT_UDP
;
8581 create_connection(sip
, g_strdup(userserver
[0]), port
);
8583 /* Server auto-discovery */
8584 if (sipe_strequal(transport
, "auto")) {
8585 sip
->auto_transport
= TRUE
;
8586 if (current_service
&& current_service
->transport
!= NULL
&& current_service
->service
!= NULL
){
8588 resolve_next_service(sip
, current_service
);
8590 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
8592 } else if (sipe_strequal(transport
, "tls")) {
8593 resolve_next_service(sip
, service_tls
);
8594 } else if (sipe_strequal(transport
, "tcp")) {
8595 resolve_next_service(sip
, service_tcp
);
8597 resolve_next_service(sip
, service_udp
);
8600 g_strfreev(userserver
);
8603 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
8605 connection_free_all(sip
);
8610 if (sip
->query_data
!= NULL
)
8611 purple_dnsquery_destroy(sip
->query_data
);
8612 sip
->query_data
= NULL
;
8614 if (sip
->srv_query_data
!= NULL
)
8615 purple_srv_cancel(sip
->srv_query_data
);
8616 sip
->srv_query_data
= NULL
;
8618 if (sip
->listen_data
!= NULL
)
8619 purple_network_listen_cancel(sip
->listen_data
);
8620 sip
->listen_data
= NULL
;
8622 if (sip
->gsc
!= NULL
)
8623 purple_ssl_close(sip
->gsc
);
8626 sipe_auth_free(&sip
->registrar
);
8627 sipe_auth_free(&sip
->proxy
);
8630 purple_circ_buffer_destroy(sip
->txbuf
);
8633 g_free(sip
->realhostname
);
8634 sip
->realhostname
= NULL
;
8636 g_free(sip
->server_version
);
8637 sip
->server_version
= NULL
;
8640 purple_input_remove(sip
->listenpa
);
8642 if (sip
->tx_handler
)
8643 purple_input_remove(sip
->tx_handler
);
8644 sip
->tx_handler
= 0;
8645 if (sip
->resendtimeout
)
8646 purple_timeout_remove(sip
->resendtimeout
);
8647 sip
->resendtimeout
= 0;
8648 if (sip
->timeouts
) {
8649 GSList
*entry
= sip
->timeouts
;
8651 struct scheduled_action
*sched_action
= entry
->data
;
8652 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
8653 purple_timeout_remove(sched_action
->timeout_handler
);
8654 if (sched_action
->destroy
) {
8655 (*sched_action
->destroy
)(sched_action
->payload
);
8657 g_free(sched_action
->name
);
8658 g_free(sched_action
);
8659 entry
= entry
->next
;
8662 g_slist_free(sip
->timeouts
);
8664 if (sip
->allow_events
) {
8665 GSList
*entry
= sip
->allow_events
;
8667 g_free(entry
->data
);
8668 entry
= entry
->next
;
8671 g_slist_free(sip
->allow_events
);
8673 if (sip
->containers
) {
8674 GSList
*entry
= sip
->containers
;
8676 free_container((struct sipe_container
*)entry
->data
);
8677 entry
= entry
->next
;
8680 g_slist_free(sip
->containers
);
8683 g_free(sip
->contact
);
8684 sip
->contact
= NULL
;
8686 g_free(sip
->regcallid
);
8687 sip
->regcallid
= NULL
;
8689 if (sip
->serveraddr
)
8690 g_free(sip
->serveraddr
);
8691 sip
->serveraddr
= NULL
;
8693 if (sip
->focus_factory_uri
)
8694 g_free(sip
->focus_factory_uri
);
8695 sip
->focus_factory_uri
= NULL
;
8698 sip
->processing_input
= FALSE
;
8701 sipe_ews_free(sip
->ews
);
8707 * A callback for g_hash_table_foreach_remove
8709 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
8710 SIPE_UNUSED_PARAMETER gpointer user_data
)
8712 sipe_free_buddy((struct sipe_buddy
*) buddy
);
8714 /* We must return TRUE as the key/value have already been deleted */
8718 static void sipe_close(PurpleConnection
*gc
)
8720 struct sipe_account_data
*sip
= gc
->proto_data
;
8723 /* leave all conversations */
8724 sipe_session_close_all(sip
);
8725 sipe_session_remove_all(sip
);
8728 sip_csta_close(sip
);
8731 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
8732 /* unsubscribe all */
8733 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
8736 do_register_exp(sip
, 0);
8739 sipe_connection_cleanup(sip
);
8740 g_free(sip
->sipdomain
);
8741 g_free(sip
->username
);
8743 g_free(sip
->password
);
8744 g_free(sip
->authdomain
);
8745 g_free(sip
->authuser
);
8746 g_free(sip
->status
);
8749 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
8750 g_hash_table_destroy(sip
->buddies
);
8751 g_hash_table_destroy(sip
->our_publications
);
8752 g_hash_table_destroy(sip
->user_state_publications
);
8753 g_hash_table_destroy(sip
->subscriptions
);
8754 g_hash_table_destroy(sip
->filetransfers
);
8757 GSList
*entry
= sip
->groups
;
8759 struct sipe_group
*group
= entry
->data
;
8760 g_free(group
->name
);
8762 entry
= entry
->next
;
8765 g_slist_free(sip
->groups
);
8767 if (sip
->our_publication_keys
) {
8768 GSList
*entry
= sip
->our_publication_keys
;
8770 g_free(entry
->data
);
8771 entry
= entry
->next
;
8774 g_slist_free(sip
->our_publication_keys
);
8776 while (sip
->transactions
)
8777 transactions_remove(sip
, sip
->transactions
->data
);
8779 g_free(gc
->proto_data
);
8780 gc
->proto_data
= NULL
;
8783 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
8784 SIPE_UNUSED_PARAMETER
void *user_data
)
8786 PurpleAccount
*acct
= purple_connection_get_account(gc
);
8787 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
8788 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
8790 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
8791 purple_conversation_present(conv
);
8795 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
8796 SIPE_UNUSED_PARAMETER
void *user_data
)
8799 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
8800 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
8803 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
8804 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
8806 PurpleNotifySearchResults
*results
;
8807 PurpleNotifySearchColumn
*column
;
8808 xmlnode
*searchResults
;
8810 int match_count
= 0;
8811 gboolean more
= FALSE
;
8814 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
8816 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
8817 if (!searchResults
) {
8818 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
8822 results
= purple_notify_searchresults_new();
8824 if (results
== NULL
) {
8825 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
8826 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
8828 xmlnode_free(searchResults
);
8832 column
= purple_notify_searchresults_column_new(_("User name"));
8833 purple_notify_searchresults_column_add(results
, column
);
8835 column
= purple_notify_searchresults_column_new(_("Name"));
8836 purple_notify_searchresults_column_add(results
, column
);
8838 column
= purple_notify_searchresults_column_new(_("Company"));
8839 purple_notify_searchresults_column_add(results
, column
);
8841 column
= purple_notify_searchresults_column_new(_("Country"));
8842 purple_notify_searchresults_column_add(results
, column
);
8844 column
= purple_notify_searchresults_column_new(_("Email"));
8845 purple_notify_searchresults_column_add(results
, column
);
8847 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
8850 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
8851 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
8852 g_strfreev(uri_parts
);
8854 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
8855 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
8856 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
8857 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
8859 purple_notify_searchresults_row_add(results
, row
);
8863 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
8864 char *data
= xmlnode_get_data_unescaped(mrow
);
8865 more
= (g_strcasecmp(data
, "true") == 0);
8869 secondary
= g_strdup_printf(
8870 dngettext(GETTEXT_PACKAGE
,
8871 "Found %d contact%s:",
8872 "Found %d contacts%s:", match_count
),
8873 match_count
, more
? _(" (more matched your query)") : "");
8875 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
8876 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
8877 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
8880 xmlnode_free(searchResults
);
8884 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
8886 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
8887 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
8893 PurpleRequestField
*field
= entries
->data
;
8894 const char *id
= purple_request_field_get_id(field
);
8895 const char *value
= purple_request_field_string_get_value(field
);
8897 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
8899 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
8900 } while ((entries
= g_list_next(entries
)) != NULL
);
8904 struct sipe_account_data
*sip
= gc
->proto_data
;
8905 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
8906 gchar
*query
= g_strjoinv(NULL
, attrs
);
8907 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
8908 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
8909 send_soap_request_with_cb(sip
, domain_uri
, body
,
8910 (TransCallback
) process_search_contact_response
, NULL
);
8919 static void sipe_show_find_contact(PurplePluginAction
*action
)
8921 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8922 PurpleRequestFields
*fields
;
8923 PurpleRequestFieldGroup
*group
;
8924 PurpleRequestField
*field
;
8926 fields
= purple_request_fields_new();
8927 group
= purple_request_field_group_new(NULL
);
8928 purple_request_fields_add_group(fields
, group
);
8930 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
8931 purple_request_field_group_add_field(group
, field
);
8932 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
8933 purple_request_field_group_add_field(group
, field
);
8934 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
8935 purple_request_field_group_add_field(group
, field
);
8936 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
8937 purple_request_field_group_add_field(group
, field
);
8939 purple_request_fields(gc
,
8941 _("Search for a contact"),
8942 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
8944 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
8946 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
8949 static void sipe_show_about_plugin(PurplePluginAction
*action
)
8951 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8952 char *tmp
= g_strdup_printf(
8954 * Non-translatable parts, like markup, are hard-coded
8955 * into the format string. This requires more translatable
8956 * texts but it makes the translations less error prone.
8958 "<b><font size=\"+1\">SIPE " SIPE_VERSION
" </font></b><br/>"
8961 "<li> - MS Office Communications Server 2007 R2</li><br/>"
8962 "<li> - MS Office Communications Server 2007</li><br/>"
8963 "<li> - MS Live Communications Server 2005</li><br/>"
8964 "<li> - MS Live Communications Server 2003</li><br/>"
8965 "<li> - Reuters Messaging</li><br/>"
8967 /* 2 */ "%s: <a href=\"http://sipe.sourceforge.net\">http://sipe.sourceforge.net</a><br/>"
8968 /* 3,4 */ "%s: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">%s</a><br/>"
8969 /* 5 */ "%s: <a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a><br/>"
8970 /* 6 */ "%s: GPLv2+<br/>"
8974 " - Reuters Messaging network<br/>"
8975 " - Deutsche Bank<br/>"
8976 " - Merrill Lynch<br/>"
8984 " - Alcatel-Lucent<br/>"
8987 /* 8,9 */ "%s<a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a>%s.<br/>"
8989 /* 10 */ "<b>%s:</b><br/>"
8990 " - Anibal Avelar<br/>"
8991 " - Gabriel Burt<br/>"
8992 " - Stefan Becker<br/>"
8994 " - Jakub Adam<br/>"
8995 " - Tomáš Hrabčík<br/>"
8999 /* The next 11 texts make up the SIPE about note text */
9000 /* About note, part 1/11: introduction */
9001 _("A third-party plugin implementing extended version of SIP/SIMPLE used by various products"),
9002 /* About note, part 2/11: home page URL (label) */
9004 /* About note, part 3/11: support forum URL (label) */
9006 /* About note, part 4/11: support forum name (hyperlink text) */
9008 /* About note, part 5/11: translation service URL (label) */
9010 /* About note, part 6/11: license type (label) */
9012 /* About note, part 7/11: known users */
9013 _("We support users in such organizations as"),
9014 /* About note, part 8/11: translation request, text before Transifex.net URL */
9015 /* append a space if text is not empty */
9016 _("Please help us to translate SIPE to your native language here at "),
9017 /* About note, part 9/11: translation request, text after Transifex.net URL */
9018 /* start with a space if text is not empty */
9019 _(" using convenient web interface"),
9020 /* About note, part 10/11: author list (header) */
9022 /* About note, part 11/11: Localization credit */
9023 /* PLEASE NOTE: do *NOT* simply translate the english original */
9024 /* but write something similar to the following sentence: */
9025 /* "Localization for <language name> (<language code>): <name>" */
9026 _("Original texts in English (en): SIPE developers")
9028 purple_notify_formatted(gc
, NULL
, " ", NULL
, tmp
, NULL
, NULL
);
9032 static void sipe_republish_calendar(PurplePluginAction
*action
)
9034 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9035 struct sipe_account_data
*sip
= gc
->proto_data
;
9037 sipe_update_calendar(sip
);
9040 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
9044 struct sipe_publication
*publication
= value
;
9046 g_string_append_printf( str
,
9047 SIPE_PUB_XML_PUBLICATION_CLEAR
,
9048 publication
->category
,
9049 publication
->instance
,
9050 publication
->container
,
9051 publication
->version
,
9055 static void sipe_reset_status(PurplePluginAction
*action
)
9057 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9058 struct sipe_account_data
*sip
= gc
->proto_data
;
9060 if (sip
->ocs2007
) /* 2007+ */
9062 GString
* str
= g_string_new(NULL
);
9063 gchar
*publications
;
9065 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
9066 purple_debug_info("sipe", "sipe_reset_status: no userState publications, exiting.\n");
9070 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
9071 publications
= g_string_free(str
, FALSE
);
9073 send_presence_publish(sip
, publications
);
9074 g_free(publications
);
9078 send_presence_soap0(sip
, FALSE
, TRUE
);
9082 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
9085 PurpleConnection
*gc
= (PurpleConnection
*)context
;
9086 struct sipe_account_data
*sip
= gc
->proto_data
;
9088 PurplePluginAction
*act
;
9089 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
9091 act
= purple_plugin_action_new(_("About SIPE plugin..."), sipe_show_about_plugin
);
9092 menu
= g_list_prepend(menu
, act
);
9094 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
9095 menu
= g_list_prepend(menu
, act
);
9097 if (sipe_strequal(calendar
, "EXCH")) {
9098 act
= purple_plugin_action_new(_("Republish Calendar"), sipe_republish_calendar
);
9099 menu
= g_list_prepend(menu
, act
);
9102 act
= purple_plugin_action_new(_("Reset status"), sipe_reset_status
);
9103 menu
= g_list_prepend(menu
, act
);
9105 menu
= g_list_reverse(menu
);
9110 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
9114 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9120 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9126 static char *sipe_status_text(PurpleBuddy
*buddy
)
9128 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9129 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9130 const char *status_id
= purple_status_get_id(status
);
9131 struct sipe_account_data
*sip
= (struct sipe_account_data
*)buddy
->account
->gc
->proto_data
;
9132 struct sipe_buddy
*sbuddy
;
9135 if (!sip
) return NULL
; /* happens on pidgin exit */
9137 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9139 const char *activity_str
= sbuddy
->activity
?
9141 sipe_strequal(status_id
, SIPE_STATUS_ID_BUSY
) || sipe_strequal(status_id
, SIPE_STATUS_ID_BRB
) ?
9142 purple_status_get_name(status
) : NULL
;
9144 if (activity_str
&& sbuddy
->note
)
9146 text
= g_strdup_printf("%s - <i>%s</i>", activity_str
, sbuddy
->note
);
9148 else if (activity_str
)
9150 text
= g_strdup(activity_str
);
9152 else if (sbuddy
->note
)
9154 text
= g_strdup_printf("<i>%s</i>", sbuddy
->note
);
9161 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
9163 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9164 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9165 struct sipe_account_data
*sip
;
9166 struct sipe_buddy
*sbuddy
;
9168 gboolean is_oof_note
= FALSE
;
9169 char *activity
= NULL
;
9170 char *calendar
= NULL
;
9171 char *meeting_subject
= NULL
;
9172 char *meeting_location
= NULL
;
9174 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
9175 if (sip
) //happens on pidgin exit
9177 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9180 note
= sbuddy
->note
;
9181 is_oof_note
= sbuddy
->is_oof_note
;
9182 activity
= sbuddy
->activity
;
9183 calendar
= sipe_cal_get_description(sbuddy
);
9184 meeting_subject
= sbuddy
->meeting_subject
;
9185 meeting_location
= sbuddy
->meeting_location
;
9190 if (purple_presence_is_online(presence
))
9192 const char *status_str
= activity
? activity
: purple_status_get_name(status
);
9194 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
9196 if (purple_presence_is_online(presence
) &&
9197 !is_empty(calendar
))
9199 purple_notify_user_info_add_pair(user_info
, _("Calendar"), calendar
);
9202 if (!is_empty(meeting_location
))
9204 purple_notify_user_info_add_pair(user_info
, _("Meeting in"), meeting_location
);
9206 if (!is_empty(meeting_subject
))
9208 purple_notify_user_info_add_pair(user_info
, _("Meeting about"), meeting_subject
);
9213 char *tmp
= g_strdup_printf("<i>%s</i>", note
);
9214 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, note
);
9216 purple_notify_user_info_add_pair(user_info
, is_oof_note
? _("Out of office note") : _("Note"), tmp
);
9222 #if PURPLE_VERSION_CHECK(2,5,0)
9224 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
9227 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
9228 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
9233 static PurpleBuddy
*
9234 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
9237 const gchar
*server_alias
, *email
;
9238 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
9240 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
9242 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
9244 server_alias
= purple_buddy_get_server_alias(buddy
);
9246 purple_blist_server_alias_buddy(clone
, server_alias
);
9249 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9251 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
9254 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
9256 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
9261 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
9263 PurpleBuddy
*buddy
, *b
;
9264 PurpleConnection
*gc
;
9265 PurpleGroup
* group
= purple_find_group(group_name
);
9267 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
9269 buddy
= (PurpleBuddy
*)node
;
9271 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
9272 gc
= purple_account_get_connection(buddy
->account
);
9274 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
9276 purple_blist_add_buddy_clone(group
, buddy
);
9279 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
9283 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
9285 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9287 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
9289 /* 2007+ conference */
9292 sipe_conf_add(sip
, buddy
->name
);
9294 else /* 2005- multiparty chat */
9296 gchar
*self
= sip_uri_self(sip
);
9297 struct sip_session
*session
;
9299 session
= sipe_session_add_chat(sip
);
9300 session
->chat_title
= sipe_chat_get_name(session
->callid
);
9301 session
->roster_manager
= g_strdup(self
);
9303 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
9304 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
9305 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
9306 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
9313 sipe_is_election_finished(struct sip_session
*session
)
9315 gboolean res
= TRUE
;
9317 SIPE_DIALOG_FOREACH
{
9318 if (dialog
->election_vote
== 0) {
9322 } SIPE_DIALOG_FOREACH_END
;
9325 session
->is_voting_in_progress
= FALSE
;
9331 sipe_election_start(struct sipe_account_data
*sip
,
9332 struct sip_session
*session
)
9334 int election_timeout
;
9336 if (session
->is_voting_in_progress
) {
9337 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
9340 session
->is_voting_in_progress
= TRUE
;
9342 session
->bid
= rand();
9344 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
9346 SIPE_DIALOG_FOREACH
{
9347 /* reset election_vote for each chat participant */
9348 dialog
->election_vote
= 0;
9350 /* send RequestRM to each chat participant*/
9351 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
9352 } SIPE_DIALOG_FOREACH_END
;
9354 election_timeout
= 15; /* sec */
9355 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
9359 * @param who a URI to whom to invite to chat
9362 sipe_invite_to_chat(struct sipe_account_data
*sip
,
9363 struct sip_session
*session
,
9367 if (session
->focus_uri
)
9369 sipe_invite_conf(sip
, session
, who
);
9371 else /* a multi-party chat */
9373 gchar
*self
= sip_uri_self(sip
);
9374 if (session
->roster_manager
) {
9375 if (sipe_strequal(session
->roster_manager
, self
)) {
9376 sipe_invite(sip
, session
, who
, NULL
, NULL
, NULL
, FALSE
);
9378 sipe_refer(sip
, session
, who
);
9381 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
9383 session
->pending_invite_queue
= slist_insert_unique_sorted(
9384 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
9386 sipe_election_start(sip
, session
);
9393 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
9394 struct sip_session
*session
)
9397 GSList
*entry
= session
->pending_invite_queue
;
9400 invitee
= entry
->data
;
9401 sipe_invite_to_chat(sip
, session
, invitee
);
9402 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
9408 sipe_election_result(struct sipe_account_data
*sip
,
9411 struct sip_session
*session
= (struct sip_session
*)sess
;
9413 gboolean has_won
= TRUE
;
9415 if (session
->roster_manager
) {
9416 purple_debug_info("sipe",
9417 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
9421 session
->is_voting_in_progress
= FALSE
;
9423 SIPE_DIALOG_FOREACH
{
9424 if (dialog
->election_vote
< 0) {
9426 rival
= dialog
->with
;
9429 } SIPE_DIALOG_FOREACH_END
;
9432 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
9434 session
->roster_manager
= sip_uri_self(sip
);
9436 SIPE_DIALOG_FOREACH
{
9437 /* send SetRM to each chat participant*/
9438 sipe_send_election_set_rm(sip
, dialog
);
9439 } SIPE_DIALOG_FOREACH_END
;
9441 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
9445 sipe_process_pending_invite_queue(sip
, session
);
9449 * For 2007+ conference only.
9452 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9454 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9455 struct sip_session
*session
;
9457 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
9458 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
9460 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9462 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
9466 * For 2007+ conference only.
9469 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9471 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9472 struct sip_session
*session
;
9474 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
9475 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
9477 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9479 sipe_conf_delete_user(sip
, session
, buddy
->name
);
9483 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
9485 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9486 struct sip_session
*session
;
9488 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
9489 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
9491 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9493 sipe_invite_to_chat(sip
, session
, buddy
->name
);
9497 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
9499 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9501 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
9503 char *tel_uri
= sip_to_tel_uri(phone
);
9505 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
9506 sip_csta_make_call(sip
, tel_uri
);
9513 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
9516 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
9518 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9521 char *mailto
= g_strdup_printf("mailto:%s", email
);
9522 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
9526 char *const parmList
[] = {"xdg-email", mailto
, NULL
};
9527 if ((pid
= fork()) == -1)
9529 purple_debug_info("sipe", "fork() error\n");
9533 execvp(parmList
[0], parmList
);
9534 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
9542 //@TODO resolve env variable %WINDIR% first
9543 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
9546 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
9555 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
9560 * A menu which appear when right-clicking on buddy in contact list.
9563 sipe_buddy_menu(PurpleBuddy
*buddy
)
9565 PurpleBlistNode
*g_node
;
9566 PurpleGroup
*group
, *gr_parent
;
9567 PurpleMenuAction
*act
;
9569 GList
*menu_groups
= NULL
;
9570 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9573 const char *phone_disp_str
;
9574 gchar
*self
= sip_uri_self(sip
);
9576 SIPE_SESSION_FOREACH
{
9577 if (g_ascii_strcasecmp(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
9579 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
9581 PurpleConvChatBuddyFlags flags
;
9582 PurpleConvChatBuddyFlags flags_us
;
9584 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
9585 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9586 if (session
->focus_uri
9587 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
9588 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9590 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
9591 act
= purple_menu_action_new(label
,
9592 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
9593 session
->chat_title
, NULL
);
9595 menu
= g_list_prepend(menu
, act
);
9598 if (session
->focus_uri
9599 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9601 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
9602 act
= purple_menu_action_new(label
,
9603 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
9604 session
->chat_title
, NULL
);
9606 menu
= g_list_prepend(menu
, act
);
9611 if (!session
->focus_uri
9612 || (session
->focus_uri
&& !session
->locked
))
9614 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
9615 act
= purple_menu_action_new(label
,
9616 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
9617 session
->chat_title
, NULL
);
9619 menu
= g_list_prepend(menu
, act
);
9623 } SIPE_SESSION_FOREACH_END
;
9625 act
= purple_menu_action_new(_("New chat"),
9626 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
9628 menu
= g_list_prepend(menu
, act
);
9630 if (sip
->csta
&& !sip
->csta
->line_status
) {
9633 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
9634 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
9636 gchar
*label
= g_strdup_printf(_("Work %s"),
9637 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9638 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9642 menu
= g_list_prepend(menu
, act
);
9646 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
9647 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
9649 gchar
*label
= g_strdup_printf(_("Mobile %s"),
9650 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9651 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9655 menu
= g_list_prepend(menu
, act
);
9659 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
9660 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
9662 gchar
*label
= g_strdup_printf(_("Home %s"),
9663 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9664 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9668 menu
= g_list_prepend(menu
, act
);
9672 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
9673 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
9675 gchar
*label
= g_strdup_printf(_("Other %s"),
9676 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9677 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9681 menu
= g_list_prepend(menu
, act
);
9685 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
9686 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
9688 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
9689 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9690 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9694 menu
= g_list_prepend(menu
, act
);
9698 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9700 act
= purple_menu_action_new(_("Send email..."),
9701 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
9703 menu
= g_list_prepend(menu
, act
);
9706 gr_parent
= purple_buddy_get_group(buddy
);
9707 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
9708 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
9711 group
= (PurpleGroup
*)g_node
;
9712 if (group
== gr_parent
)
9715 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
9718 act
= purple_menu_action_new(purple_group_get_name(group
),
9719 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
9721 menu_groups
= g_list_prepend(menu_groups
, act
);
9723 menu_groups
= g_list_reverse(menu_groups
);
9725 act
= purple_menu_action_new(_("Copy to"),
9728 menu
= g_list_prepend(menu
, act
);
9729 menu
= g_list_reverse(menu
);
9736 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
9738 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9739 struct sip_session
*session
;
9741 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9742 sipe_conf_modify_conference_lock(sip
, session
, locked
);
9746 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
9748 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
9749 sipe_conf_modify_lock(chat
, FALSE
);
9753 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
9755 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
9756 sipe_conf_modify_lock(chat
, TRUE
);
9760 sipe_chat_menu(PurpleChat
*chat
)
9762 PurpleMenuAction
*act
;
9763 PurpleConvChatBuddyFlags flags_us
;
9765 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9766 struct sip_session
*session
;
9769 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9770 if (!session
) return NULL
;
9772 self
= sip_uri_self(sip
);
9773 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9775 if (session
->focus_uri
9776 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9778 if (session
->locked
) {
9779 act
= purple_menu_action_new(_("Unlock"),
9780 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
9782 menu
= g_list_prepend(menu
, act
);
9784 act
= purple_menu_action_new(_("Lock"),
9785 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
9787 menu
= g_list_prepend(menu
, act
);
9791 menu
= g_list_reverse(menu
);
9798 sipe_blist_node_menu(PurpleBlistNode
*node
)
9800 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
9801 return sipe_buddy_menu((PurpleBuddy
*) node
);
9802 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
9803 return sipe_chat_menu((PurpleChat
*)node
);
9810 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
9812 char *uri
= trans
->payload
->data
;
9814 PurpleNotifyUserInfo
*info
;
9815 PurpleBuddy
*pbuddy
= NULL
;
9816 struct sipe_buddy
*sbuddy
;
9817 const char *alias
= NULL
;
9818 char *device_name
= NULL
;
9819 char *server_alias
= NULL
;
9820 char *phone_number
= NULL
;
9823 char *first_name
= NULL
;
9824 char *last_name
= NULL
;
9826 if (!sip
) return FALSE
;
9828 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
9830 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
9831 alias
= purple_buddy_get_local_alias(pbuddy
);
9833 //will query buddy UA's capabilities and send answer to log
9834 sipe_options_request(sip
, uri
);
9836 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
9838 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
9841 info
= purple_notify_user_info_new();
9843 if (msg
->response
!= 200) {
9844 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
9846 xmlnode
*searchResults
;
9849 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
9850 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
9851 if (!searchResults
) {
9852 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
9853 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
9855 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
9856 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
9857 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
9859 /* For 2007 system we will take this from ContactCard -
9860 * it has cleaner tel: URIs at least
9862 if (!sip
->ocs2007
) {
9863 char *tel_uri
= sip_to_tel_uri(phone_number
);
9864 /* trims its parameters, so call first */
9865 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
9866 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
9867 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
9868 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
9872 if (server_alias
&& strlen(server_alias
) > 0) {
9873 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9875 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
9876 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
9878 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
9879 purple_notify_user_info_add_pair(info
, _("Office"), value
);
9881 if (phone_number
&& strlen(phone_number
) > 0) {
9882 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
9884 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
9885 purple_notify_user_info_add_pair(info
, _("Company"), value
);
9887 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
9888 purple_notify_user_info_add_pair(info
, _("City"), value
);
9890 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
9891 purple_notify_user_info_add_pair(info
, _("State"), value
);
9893 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
9894 purple_notify_user_info_add_pair(info
, _("Country"), value
);
9896 if (email
&& strlen(email
) > 0) {
9897 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9901 xmlnode_free(searchResults
);
9904 purple_notify_user_info_add_section_break(info
);
9906 if (is_empty(server_alias
)) {
9907 g_free(server_alias
);
9908 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
9910 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9914 /* present alias if it differs from server alias */
9915 if (alias
&& !sipe_strequal(alias
, server_alias
))
9917 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
9920 if (is_empty(email
)) {
9922 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
9924 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9928 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
9930 purple_notify_user_info_add_pair(info
, _("Site"), site
);
9933 sipe_get_first_last_names(sip
, uri
, &first_name
, &last_name
);
9934 if (first_name
&& last_name
) {
9935 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
9937 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
9944 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
9947 /* show a buddy's user info in a nice dialog box */
9948 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
9949 uri
, /* buddy's URI */
9951 NULL
, /* callback called when dialog closed */
9952 NULL
); /* userdata for callback */
9954 g_free(phone_number
);
9955 g_free(server_alias
);
9957 g_free(device_name
);
9963 * AD search first, LDAP based
9965 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
9967 struct sipe_account_data
*sip
= gc
->proto_data
;
9968 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
9969 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
9970 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
9971 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
9973 payload
->destroy
= g_free
;
9974 payload
->data
= g_strdup(username
);
9976 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
9977 send_soap_request_with_cb(sip
, domain_uri
, body
,
9978 (TransCallback
) process_get_info_response
, payload
);
9984 static PurplePlugin
*my_protocol
= NULL
;
9986 static PurplePluginProtocolInfo prpl_info
=
9988 OPT_PROTO_CHAT_TOPIC
,
9989 NULL
, /* user_splits */
9990 NULL
, /* protocol_options */
9991 NO_BUDDY_ICONS
, /* icon_spec */
9992 sipe_list_icon
, /* list_icon */
9993 NULL
, /* list_emblems */
9994 sipe_status_text
, /* status_text */
9995 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
9996 sipe_status_types
, /* away_states */
9997 sipe_blist_node_menu
, /* blist_node_menu */
9998 NULL
, /* chat_info */
9999 NULL
, /* chat_info_defaults */
10000 sipe_login
, /* login */
10001 sipe_close
, /* close */
10002 sipe_im_send
, /* send_im */
10003 NULL
, /* set_info */ // TODO maybe
10004 sipe_send_typing
, /* send_typing */
10005 sipe_get_info
, /* get_info */
10006 sipe_set_status
, /* set_status */
10007 sipe_set_idle
, /* set_idle */
10008 NULL
, /* change_passwd */
10009 sipe_add_buddy
, /* add_buddy */
10010 NULL
, /* add_buddies */
10011 sipe_remove_buddy
, /* remove_buddy */
10012 NULL
, /* remove_buddies */
10013 sipe_add_permit
, /* add_permit */
10014 sipe_add_deny
, /* add_deny */
10015 sipe_add_deny
, /* rem_permit */
10016 sipe_add_permit
, /* rem_deny */
10017 dummy_permit_deny
, /* set_permit_deny */
10018 NULL
, /* join_chat */
10019 NULL
, /* reject_chat */
10020 NULL
, /* get_chat_name */
10021 sipe_chat_invite
, /* chat_invite */
10022 sipe_chat_leave
, /* chat_leave */
10023 NULL
, /* chat_whisper */
10024 sipe_chat_send
, /* chat_send */
10025 sipe_keep_alive
, /* keepalive */
10026 NULL
, /* register_user */
10027 NULL
, /* get_cb_info */ // deprecated
10028 NULL
, /* get_cb_away */ // deprecated
10029 sipe_alias_buddy
, /* alias_buddy */
10030 sipe_group_buddy
, /* group_buddy */
10031 sipe_rename_group
, /* rename_group */
10032 NULL
, /* buddy_free */
10033 sipe_convo_closed
, /* convo_closed */
10034 purple_normalize_nocase
, /* normalize */
10035 NULL
, /* set_buddy_icon */
10036 sipe_remove_group
, /* remove_group */
10037 NULL
, /* get_cb_real_name */ // TODO?
10038 NULL
, /* set_chat_topic */
10039 NULL
, /* find_blist_chat */
10040 NULL
, /* roomlist_get_list */
10041 NULL
, /* roomlist_cancel */
10042 NULL
, /* roomlist_expand_category */
10043 NULL
, /* can_receive_file */
10044 sipe_ft_send_file
, /* send_file */
10045 sipe_ft_new_xfer
, /* new_xfer */
10046 NULL
, /* offline_message */
10047 NULL
, /* whiteboard_prpl_ops */
10048 sipe_send_raw
, /* send_raw */
10049 NULL
, /* roomlist_room_serialize */
10050 NULL
, /* unregister_user */
10051 NULL
, /* send_attention */
10052 NULL
, /* get_attention_types */
10053 #if !PURPLE_VERSION_CHECK(2,5,0)
10054 /* Backward compatibility when compiling against 2.4.x API */
10055 (void (*)(void)) /* _purple_reserved4 */
10057 sizeof(PurplePluginProtocolInfo
), /* struct_size */
10058 #if PURPLE_VERSION_CHECK(2,5,0)
10059 sipe_get_account_text_table
, /* get_account_text_table */
10060 #if PURPLE_VERSION_CHECK(2,6,0)
10061 NULL
, /* initiate_media */
10062 NULL
, /* get_media_caps */
10068 PurplePluginInfo info
= {
10069 PURPLE_PLUGIN_MAGIC
,
10070 PURPLE_MAJOR_VERSION
,
10071 PURPLE_MINOR_VERSION
,
10072 PURPLE_PLUGIN_PROTOCOL
, /**< type */
10073 NULL
, /**< ui_requirement */
10075 NULL
, /**< dependencies */
10076 PURPLE_PRIORITY_DEFAULT
, /**< priority */
10077 "prpl-sipe", /**< id */
10078 "Office Communicator", /**< name */
10079 SIPE_VERSION
, /**< version */
10080 "Microsoft Office Communicator Protocol Plugin", /**< summary */
10081 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
10082 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
10083 "Anibal Avelar <avelar@gmail.com>, " /**< author */
10084 "Gabriel Burt <gburt@novell.com>, " /**< author */
10085 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
10086 "pier11 <pier11@operamail.com>", /**< author */
10087 "http://sipe.sourceforge.net/", /**< homepage */
10088 sipe_plugin_load
, /**< load */
10089 sipe_plugin_unload
, /**< unload */
10090 sipe_plugin_destroy
, /**< destroy */
10091 NULL
, /**< ui_info */
10092 &prpl_info
, /**< extra_info */
10101 static void sipe_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
10107 entry
= prpl_info
.protocol_options
;
10109 purple_account_option_destroy(entry
->data
);
10110 entry
= g_list_delete_link(entry
, entry
);
10112 prpl_info
.protocol_options
= NULL
;
10114 entry
= prpl_info
.user_splits
;
10116 purple_account_user_split_destroy(entry
->data
);
10117 entry
= g_list_delete_link(entry
, entry
);
10119 prpl_info
.user_splits
= NULL
;
10122 void init_plugin(PurplePlugin
*plugin
)
10124 PurpleAccountUserSplit
*split
;
10125 PurpleAccountOption
*option
;
10131 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
10132 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
10133 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
10134 textdomain(GETTEXT_PACKAGE
);
10137 purple_plugin_register(plugin
);
10139 split
= purple_account_user_split_new(_("Login\n user or DOMAIN\\user or\n user@company.com"), NULL
, ',');
10140 purple_account_user_split_set_reverse(split
, FALSE
);
10141 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
10143 option
= purple_account_option_string_new(_("Server[:Port]\n(leave empty for auto-discovery)"), "server", "");
10144 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10146 option
= purple_account_option_list_new(_("Connection type"), "transport", NULL
);
10147 purple_account_option_add_list_item(option
, _("Auto"), "auto");
10148 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
10149 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
10150 purple_account_option_add_list_item(option
, _("UDP"), "udp");
10151 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10153 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
10154 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
10156 option
= purple_account_option_string_new(_("User Agent"), "useragent", "");
10157 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10159 #ifdef USE_KERBEROS
10160 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
10161 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10163 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
10164 * No login/password is taken into account if this option present,
10165 * instead used default credentials stored in OS.
10167 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
10168 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10171 option
= purple_account_option_list_new(_("Calendar source"), "calendar", NULL
);
10172 purple_account_option_add_list_item(option
, _("Exchange 2007/2010"), "EXCH");
10173 purple_account_option_add_list_item(option
, _("None"), "NONE");
10174 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10176 /** Example: https://server.company.com/EWS/Exchange.asmx */
10177 option
= purple_account_option_string_new(_("Email services URL\n(leave empty for auto-discovery)"), "email_url", "");
10178 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10180 option
= purple_account_option_string_new(_("Email address\n(if different from Username)"), "email", "");
10181 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10183 /** Example: DOMAIN\user or user@company.com */
10184 option
= purple_account_option_string_new(_("Email login\n(if different from Login)"), "email_login", "");
10185 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10187 option
= purple_account_option_string_new(_("Email password\n(if different from Password)"), "email_password", "");
10188 purple_account_option_set_masked(option
, TRUE
);
10189 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
10191 my_protocol
= plugin
;
10194 PURPLE_INIT_PLUGIN(sipe
, init_plugin
, info
);
10199 c-file-style: "bsd"
10200 indent-tabs-mode: t