6 * Copyright (C) 2010 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 pier11 <pier11@operamail.com>
8 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
9 * Copyright (C) 2009 pier11 <pier11@operamail.com>
10 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
11 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
12 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
15 * Thanks to Google's Summer of Code Program and the helpful mentors
18 * Session-based SIP MESSAGE documentation:
19 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
41 #include "win32dep.h" /* for LOCALEDIR */
45 #define _LIBC_INTERNAL_
48 #include "libc_interface.h"
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <netinet/in.h>
64 #include "sipe-common.h"
68 #include "connection.h"
69 #include "conversation.h"
72 #include "circbuffer.h"
83 #include "savedstatuses.h"
89 #include "core-depurple.h" /* Temporary for the core de-purple transition */
94 #include "sipe-backend-debug.h"
96 #include "sipe-chat.h"
97 #include "sipe-conf.h"
98 #include "sipe-core-api.h"
99 #include "sipe-dialog.h"
100 #include "sipe-ews.h"
102 #include "sipe-nls.h"
103 #include "sipe-session.h"
104 #include "sipe-sign.h"
105 #include "sipe-utils.h"
106 #include "sipe-xml.h"
107 #include "http-conn.h"
111 /* Backward compatibility when compiling against 2.4.x API */
112 #if !PURPLE_VERSION_CHECK(2,5,0)
113 #define PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY 0x0100
116 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
118 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
119 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
121 /* Keep in sync with sipe_transport_type! */
122 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
123 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
125 /* Status identifiers (see also: sipe_status_types()) */
126 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
127 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
128 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
129 /* PURPLE_STATUS_UNAVAILABLE: */
130 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
131 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
132 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
133 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
134 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
135 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
136 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
137 /* PURPLE_STATUS_AWAY: */
138 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
139 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
140 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
141 /** Reuters status (user settable) */
142 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
143 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
144 /* ??? PURPLE_STATUS_MOBILE */
145 /* ??? PURPLE_STATUS_TUNE */
147 /* Status attributes (see also sipe_status_types() */
148 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
150 #define SDP_ACCEPT_TYPES "text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml text/x-msmsgsinvite"
152 static struct sipe_activity_map_struct
157 const char *status_id
;
159 } const sipe_activity_map
[] =
161 /* This has nothing to do with Availability numbers, like 3500 (online).
162 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
164 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
165 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
166 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
167 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
168 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
169 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
170 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
171 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
172 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
173 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
174 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
175 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
176 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
177 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
178 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
180 /** @param x is sipe_activity */
181 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
184 /* Action name templates */
185 #define ACTION_NAME_PRESENCE "<presence><%s>"
188 sipe_get_activity_by_token(const char *token
)
192 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
194 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
195 return sipe_activity_map
[i
].type
;
198 return sipe_activity_map
[0].type
;
202 sipe_get_activity_desc_by_token(const char *token
)
204 if (!token
) return NULL
;
206 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
209 /** Allows to send typed messages from chat window again after account reinstantiation. */
211 sipe_rejoin_chat(PurpleConversation
*conv
)
213 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
&&
214 PURPLE_CONV_CHAT(conv
)->left
)
216 PURPLE_CONV_CHAT(conv
)->left
= FALSE
;
217 purple_conversation_update(conv
, PURPLE_CONV_UPDATE_CHATLEFT
);
221 static char *genbranch()
223 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
224 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
225 rand() & 0xFFFF, rand() & 0xFFFF);
229 static char *default_ua
= NULL
;
231 sipe_get_useragent(struct sipe_account_data
*sip
)
233 const char *useragent
= purple_account_get_string(sip
->account
, "useragent", "");
234 if (is_empty(useragent
)) {
236 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
238 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
239 #define SIPE_TARGET_PLATFORM "linux"
240 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
241 #define SIPE_TARGET_PLATFORM "bsd"
242 #elif defined(__APPLE__) || defined(__MACOS__)
243 #define SIPE_TARGET_PLATFORM "macosx"
244 #elif defined(_AIX) || defined(__AIX__) || defined(__aix__)
245 #define SIPE_TARGET_PLATFORM "aix"
246 #elif defined(__solaris__) || defined(__sun)
247 #define SIPE_TARGET_PLATFORM "sun"
248 #elif defined(_WIN32)
249 #define SIPE_TARGET_PLATFORM "win"
250 #elif defined(__CYGWIN__)
251 #define SIPE_TARGET_PLATFORM "cygwin"
252 #elif defined(__hpux__)
253 #define SIPE_TARGET_PLATFORM "hpux"
254 #elif defined(__sgi__)
255 #define SIPE_TARGET_PLATFORM "irix"
257 #define SIPE_TARGET_PLATFORM "unknown"
260 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
261 #define SIPE_TARGET_ARCH "x86_64"
262 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
263 #define SIPE_TARGET_ARCH "i386"
264 #elif defined(__ppc64__)
265 #define SIPE_TARGET_ARCH "ppc64"
266 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
267 #define SIPE_TARGET_ARCH "ppc"
268 #elif defined(__hppa__) || defined(__hppa)
269 #define SIPE_TARGET_ARCH "hppa"
270 #elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000)
271 #define SIPE_TARGET_ARCH "mips"
272 #elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x)
273 #define SIPE_TARGET_ARCH "s390"
274 #elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8)
275 #define SIPE_TARGET_ARCH "sparc"
276 #elif defined(__arm__)
277 #define SIPE_TARGET_ARCH "arm"
279 #define SIPE_TARGET_ARCH "other"
282 default_ua
= g_strdup_printf("Purple/%s Sipe/" PACKAGE_VERSION
" (" SIPE_TARGET_PLATFORM
"-" SIPE_TARGET_ARCH
"; %s)",
283 purple_core_get_version(),
284 sip
->server_version
? sip
->server_version
: "");
286 useragent
= default_ua
;
291 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
292 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
297 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
);
299 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
300 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
303 static void sipe_close(PurpleConnection
*gc
);
305 static void send_presence_status(struct sipe_account_data
*sip
);
307 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
309 static void sipe_keep_alive(PurpleConnection
*gc
)
311 struct sipe_account_data
*sip
= gc
->proto_data
;
312 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
313 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
314 gchar buf
[2] = {0, 0};
315 purple_debug_info("sipe", "sending keep alive\n");
316 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
318 time_t now
= time(NULL
);
319 if ((sip
->keepalive_timeout
> 0) &&
320 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
) &&
321 ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
323 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
324 sendout_pkt(gc
, "\r\n\r\n");
325 sip
->last_keepalive
= now
;
330 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
332 struct sip_connection
*ret
= NULL
;
333 GSList
*entry
= sip
->openconns
;
336 if (ret
->fd
== fd
) return ret
;
342 static void sipe_auth_free(struct sip_auth
*auth
)
344 g_free(auth
->opaque
);
348 g_free(auth
->target
);
351 auth
->type
= AUTH_TYPE_UNSET
;
354 g_free(auth
->gssapi_data
);
355 auth
->gssapi_data
= NULL
;
356 sip_sec_destroy_context(auth
->gssapi_context
);
357 auth
->gssapi_context
= NULL
;
360 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
362 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
364 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
368 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
370 struct sip_connection
*conn
= connection_find(sip
, fd
);
372 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
373 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
379 static void connection_free_all(struct sipe_account_data
*sip
)
381 struct sip_connection
*ret
= NULL
;
382 GSList
*entry
= sip
->openconns
;
385 connection_remove(sip
, ret
->fd
);
386 entry
= sip
->openconns
;
391 sipe_make_signature(struct sipe_account_data
*sip
,
394 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
397 const char *authuser
= sip
->authuser
;
401 if (!authuser
|| strlen(authuser
) < 1) {
402 authuser
= sip
->username
;
405 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
406 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
409 // If we have a signature for the message, include that
410 if (msg
->signature
) {
411 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
);
414 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
415 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
418 gchar
*sign_str
= NULL
;
420 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
423 purple_account_get_bool(sip
->account
, "sso", TRUE
),
424 sip
->authdomain
? sip
->authdomain
: "",
429 if (!gssapi_data
|| !auth
->gssapi_context
) {
430 sip
->gc
->wants_to_die
= TRUE
;
431 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
435 if (auth
->version
> 3) {
436 sipe_make_signature(sip
, msg
);
437 sign_str
= g_strdup_printf(", crand=\"%s\", cnum=\"%s\", response=\"%s\"",
438 msg
->rand
, msg
->num
, msg
->signature
);
440 sign_str
= g_strdup("");
443 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
444 version_str
= auth
->version
> 2 ? g_strdup_printf(", version=%d", auth
->version
) : g_strdup("");
445 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
);
453 version_str
= auth
->version
> 2 ? g_strdup_printf(", version=%d", auth
->version
) : g_strdup("");
454 ret
= g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"%s", auth_protocol
, auth
->realm
, auth
->target
, version_str
);
458 } else { /* Digest */
460 /* Calculate new session key */
462 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
463 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
464 authuser
, auth
->realm
, sip
->password
,
465 auth
->gssapi_data
, NULL
);
468 sprintf(noncecount
, "%08d", auth
->nc
++);
469 response
= purple_cipher_http_digest_calculate_response("md5",
470 msg
->method
, msg
->target
, NULL
, NULL
,
471 auth
->gssapi_data
, noncecount
, NULL
,
473 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
475 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
);
481 static char *parse_attribute(const char *attrname
, const char *source
)
483 const char *tmp
, *tmp2
;
485 int len
= strlen(attrname
);
487 if (g_str_has_prefix(source
, attrname
)) {
489 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
491 retval
= g_strndup(tmp
, tmp2
- tmp
);
493 retval
= g_strdup(tmp
);
499 static void fill_auth(const gchar
*hdr
, struct sip_auth
*auth
)
505 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
509 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
510 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
511 auth
->type
= AUTH_TYPE_NTLM
;
514 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
515 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
516 auth
->type
= AUTH_TYPE_KERBEROS
;
520 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
521 auth
->type
= AUTH_TYPE_DIGEST
;
525 parts
= g_strsplit(hdr
, "\", ", 0);
526 for (i
= 0; parts
[i
]; i
++) {
529 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
531 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
532 g_free(auth
->gssapi_data
);
533 auth
->gssapi_data
= tmp
;
535 if (auth
->type
== AUTH_TYPE_NTLM
) {
536 /* NTLM module extracts nonce from gssapi-data */
540 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
541 /* Only used with AUTH_TYPE_DIGEST */
542 g_free(auth
->gssapi_data
);
543 auth
->gssapi_data
= tmp
;
544 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
545 g_free(auth
->opaque
);
547 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
551 if (auth
->type
== AUTH_TYPE_DIGEST
) {
552 /* Throw away old session key */
553 g_free(auth
->opaque
);
557 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
558 g_free(auth
->target
);
560 } else if ((tmp
= parse_attribute("version=", parts
[i
]))) {
561 auth
->version
= atoi(tmp
);
564 // uncomment to revert to previous functionality if version 3+ does not work.
565 // auth->version = 2;
572 static void sipe_canwrite_cb(gpointer data
,
573 SIPE_UNUSED_PARAMETER gint source
,
574 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
576 PurpleConnection
*gc
= data
;
577 struct sipe_account_data
*sip
= gc
->proto_data
;
581 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
583 if (max_write
== 0) {
584 if (sip
->tx_handler
!= 0){
585 purple_input_remove(sip
->tx_handler
);
591 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
593 if (written
< 0 && errno
== EAGAIN
)
595 else if (written
<= 0) {
596 /*TODO: do we really want to disconnect on a failure to write?*/
597 purple_connection_error(gc
, _("Could not write"));
601 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
604 static void sipe_canwrite_cb_ssl(gpointer data
,
605 SIPE_UNUSED_PARAMETER gint src
,
606 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
608 PurpleConnection
*gc
= data
;
609 struct sipe_account_data
*sip
= gc
->proto_data
;
613 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
615 if (max_write
== 0) {
616 if (sip
->tx_handler
!= 0) {
617 purple_input_remove(sip
->tx_handler
);
623 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
625 if (written
< 0 && errno
== EAGAIN
)
627 else if (written
<= 0) {
628 /*TODO: do we really want to disconnect on a failure to write?*/
629 purple_connection_error(gc
, _("Could not write"));
633 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
636 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
638 static void send_later_cb(gpointer data
, gint source
,
639 SIPE_UNUSED_PARAMETER
const gchar
*error
)
641 PurpleConnection
*gc
= data
;
642 struct sipe_account_data
*sip
;
643 struct sip_connection
*conn
;
645 if (!PURPLE_CONNECTION_IS_VALID(gc
))
653 purple_connection_error(gc
, _("Could not connect"));
657 sip
= gc
->proto_data
;
659 sip
->connecting
= FALSE
;
660 sip
->last_keepalive
= time(NULL
);
662 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
664 /* If there is more to write now, we need to register a handler */
665 if (sip
->txbuf
->bufused
> 0)
666 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
668 conn
= connection_create(sip
, source
);
669 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
672 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
674 struct sipe_account_data
*sip
;
676 if (!PURPLE_CONNECTION_IS_VALID(gc
))
678 if (gsc
) purple_ssl_close(gsc
);
682 sip
= gc
->proto_data
;
685 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
686 sip
->connecting
= FALSE
;
687 sip
->last_keepalive
= time(NULL
);
689 connection_create(sip
, gsc
->fd
);
691 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
696 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
697 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
699 PurpleConnection
*gc
= data
;
700 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
701 if (sip
== NULL
) return;
703 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
705 /* If there is more to write now */
706 if (sip
->txbuf
->bufused
> 0) {
707 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
712 static void sendlater(PurpleConnection
*gc
, const char *buf
)
714 struct sipe_account_data
*sip
= gc
->proto_data
;
716 if (!sip
->connecting
) {
717 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
718 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
719 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
721 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
722 purple_connection_error(gc
, _("Could not create socket"));
725 sip
->connecting
= TRUE
;
728 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
729 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
731 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
734 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
736 struct sipe_account_data
*sip
= gc
->proto_data
;
737 time_t currtime
= time(NULL
);
738 int writelen
= strlen(buf
);
741 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sending - %s######\n%s######\n", ctime(&currtime
), tmp
= fix_newlines(buf
));
743 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
744 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
745 purple_debug_info("sipe", "could not send packet\n");
754 if (sip
->tx_handler
) {
759 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
761 ret
= write(sip
->fd
, buf
, writelen
);
765 if (ret
< 0 && errno
== EAGAIN
)
767 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
772 if (ret
< writelen
) {
773 if (!sip
->tx_handler
){
775 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
778 sip
->tx_handler
= purple_input_add(sip
->fd
,
779 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
784 /* XXX: is it OK to do this? You might get part of a request sent
785 with part of another. */
786 if (sip
->txbuf
->bufused
> 0)
787 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
789 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
795 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
797 sendout_pkt(gc
, buf
);
801 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
803 GSList
*tmp
= msg
->headers
;
806 GString
*outstr
= g_string_new("");
807 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
809 name
= ((struct sipnameval
*) (tmp
->data
))->name
;
810 value
= ((struct sipnameval
*) (tmp
->data
))->value
;
811 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
812 tmp
= g_slist_next(tmp
);
814 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
815 sendout_pkt(sip
->gc
, outstr
->str
);
816 g_string_free(outstr
, TRUE
);
820 sipe_make_signature(struct sipe_account_data
*sip
,
823 if (sip
->registrar
.gssapi_context
) {
824 struct sipmsg_breakdown msgbd
;
825 gchar
*signature_input_str
;
827 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
828 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
829 sip
->registrar
.ntlm_num
++;
830 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
831 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
832 if (signature_input_str
!= NULL
) {
833 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
834 msg
->signature
= signature_hex
;
835 msg
->rand
= g_strdup(msgbd
.rand
);
836 msg
->num
= g_strdup(msgbd
.num
);
837 g_free(signature_input_str
);
839 sipmsg_breakdown_free(&msgbd
);
843 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
847 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
851 sipe_make_signature(sip
, msg
);
853 if (sip
->registrar
.type
&& sipe_strequal(method
, "REGISTER")) {
854 buf
= auth_header(sip
, &sip
->registrar
, msg
);
856 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
859 } 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")) {
860 sip
->registrar
.nc
= 3;
861 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
863 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
864 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
869 buf
= auth_header(sip
, &sip
->registrar
, msg
);
870 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
873 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
877 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
878 const char *text
, const char *body
)
882 GString
*outstr
= g_string_new("");
883 struct sipe_account_data
*sip
= gc
->proto_data
;
886 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
888 /* Can return NULL! */
889 contact
= get_contact(sip
);
891 sipmsg_add_header(msg
, "Contact", contact
);
896 gchar
*len
= g_strdup_printf("%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
897 sipmsg_add_header(msg
, "Content-Length", len
);
900 sipmsg_add_header(msg
, "Content-Length", "0");
903 msg
->response
= code
;
905 sipmsg_strip_headers(msg
, keepers
);
906 sipmsg_merge_new_headers(msg
);
907 sign_outgoing_message(msg
, sip
, msg
->method
);
909 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
912 name
= ((struct sipnameval
*) (tmp
->data
))->name
;
913 value
= ((struct sipnameval
*) (tmp
->data
))->value
;
915 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
916 tmp
= g_slist_next(tmp
);
918 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
919 sendout_pkt(gc
, outstr
->str
);
920 g_string_free(outstr
, TRUE
);
923 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
925 if (sip
->transactions
) {
926 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
927 purple_debug_info("sipe", "sip->transactions count:%d after removal\n", g_slist_length(sip
->transactions
));
929 if (trans
->msg
) sipmsg_free(trans
->msg
);
930 if (trans
->payload
) {
931 (*trans
->payload
->destroy
)(trans
->payload
->data
);
932 g_free(trans
->payload
);
939 static struct transaction
*
940 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
942 const gchar
*call_id
;
944 struct transaction
*trans
= g_new0(struct transaction
, 1);
946 trans
->time
= time(NULL
);
947 trans
->msg
= (struct sipmsg
*)msg
;
948 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
949 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
950 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
951 trans
->callback
= callback
;
952 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
953 purple_debug_info("sipe", "sip->transactions count:%d after addition\n", g_slist_length(sip
->transactions
));
957 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
959 struct transaction
*trans
;
960 GSList
*transactions
= sip
->transactions
;
961 const gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
962 const gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
965 if (!call_id
|| !cseq
) {
966 purple_debug(PURPLE_DEBUG_ERROR
, "sipe", "transaction_find: no Call-ID or CSeq!\n");
970 key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
971 while (transactions
) {
972 trans
= transactions
->data
;
973 if (!g_strcasecmp(trans
->key
, key
)) {
977 transactions
= transactions
->next
;
985 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
986 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
987 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
989 struct sipe_account_data
*sip
= gc
->proto_data
;
990 const char *addh
= "";
993 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
994 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
995 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
996 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
997 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
998 gchar
*route
= g_strdup("");
999 gchar
*epid
= get_epid(sip
);
1000 int cseq
= dialog
? ++dialog
->cseq
: 1 /* as Call-Id is new in this case */;
1001 struct transaction
*trans
= NULL
;
1003 if (dialog
&& dialog
->routes
)
1005 GSList
*iter
= dialog
->routes
;
1010 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
1012 iter
= g_slist_next(iter
);
1016 if (!ourtag
&& !dialog
) {
1020 if (sipe_strequal(method
, "REGISTER")) {
1021 if (sip
->regcallid
) {
1023 callid
= g_strdup(sip
->regcallid
);
1025 sip
->regcallid
= g_strdup(callid
);
1030 if (addheaders
) addh
= addheaders
;
1032 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
1033 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
1034 "From: <sip:%s>%s%s;epid=%s\r\n"
1035 "To: <%s>%s%s%s%s\r\n"
1036 "Max-Forwards: 70\r\n"
1038 "User-Agent: %s\r\n"
1041 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
1043 dialog
&& dialog
->request
? dialog
->request
: url
,
1044 TRANSPORT_DESCRIPTOR
,
1045 purple_network_get_my_ip(-1),
1047 branch
? ";branch=" : "",
1048 branch
? branch
: "",
1050 ourtag
? ";tag=" : "",
1051 ourtag
? ourtag
: "",
1054 theirtag
? ";tag=" : "",
1055 theirtag
? theirtag
: "",
1056 theirepid
? ";epid=" : "",
1057 theirepid
? theirepid
: "",
1060 sipe_get_useragent(sip
),
1064 body
? (gsize
) strlen(body
) : 0,
1068 //printf ("parsing msg buf:\n%s\n\n", buf);
1069 msg
= sipmsg_parse_msg(buf
);
1080 sign_outgoing_message (msg
, sip
, method
);
1082 buf
= sipmsg_to_string (msg
);
1084 /* add to ongoing transactions */
1085 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
1086 if (!sipe_strequal(method
, "ACK")) {
1087 trans
= transactions_add_buf(sip
, msg
, tc
);
1091 sendout_pkt(gc
, buf
);
1098 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
1101 send_soap_request_with_cb(struct sipe_account_data
*sip
,
1104 TransCallback callback
,
1105 struct transaction_payload
*payload
)
1107 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sip
);
1108 gchar
*contact
= get_contact(sip
);
1109 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1110 "Content-Type: application/SOAP+xml\r\n",contact
);
1112 struct transaction
*trans
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1113 trans
->payload
= payload
;
1120 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1122 send_soap_request_with_cb(sip
, NULL
, body
, NULL
, NULL
);
1125 static char *get_contact_register(struct sipe_account_data
*sip
)
1127 char *epid
= get_epid(sip
);
1128 char *uuid
= generateUUIDfromEPID(epid
);
1129 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
);
1135 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1143 if (!sip
->sipdomain
) return;
1145 uri
= sip_uri_from_name(sip
->sipdomain
);
1146 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1147 to
= sip_uri_self(sip
);
1148 contact
= get_contact_register(sip
);
1149 hdr
= g_strdup_printf("Contact: %s\r\n"
1150 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1151 "Event: registration\r\n"
1152 "Allow-Events: presence\r\n"
1153 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1154 "%s", contact
, expires
);
1158 sip
->registerstatus
= 1;
1160 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1161 process_register_response
);
1168 static void do_register_cb(struct sipe_account_data
*sip
,
1169 SIPE_UNUSED_PARAMETER
void *unused
)
1171 do_register_exp(sip
, -1);
1172 sip
->reregister_set
= FALSE
;
1175 static void do_register(struct sipe_account_data
*sip
)
1177 do_register_exp(sip
, -1);
1181 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1183 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1184 send_soap_request(sip
, body
);
1189 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1192 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1194 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1197 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1201 void sipe_auth_user_cb(void * data
)
1203 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1206 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1211 void sipe_deny_user_cb(void * data
)
1213 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1216 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1221 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1223 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1224 sipe_contact_allow_deny(sip
, name
, TRUE
);
1228 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1230 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1231 sipe_contact_allow_deny(sip
, name
, FALSE
);
1235 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1237 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1238 sipe_contact_set_acl(sip, name, "");
1242 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1246 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1247 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1249 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1251 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1252 if (!watchers
) return;
1254 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1255 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1256 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1257 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1259 // TODO pull out optional displayName to pass as alias
1261 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1262 job
->who
= remote_user
;
1264 purple_account_request_authorization(
1278 xmlnode_free(watchers
);
1283 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1285 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1286 if (!purple_group
) {
1287 purple_group
= purple_group_new(group
->name
);
1288 purple_blist_add_group(purple_group
, NULL
);
1292 group
->purple_group
= purple_group
;
1293 sip
->groups
= g_slist_append(sip
->groups
, group
);
1294 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1296 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1300 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1302 struct sipe_group
*group
;
1308 entry
= sip
->groups
;
1310 group
= entry
->data
;
1311 if (group
->id
== id
) {
1314 entry
= entry
->next
;
1319 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1321 struct sipe_group
*group
;
1323 if (!sip
|| !name
) {
1327 entry
= sip
->groups
;
1329 group
= entry
->data
;
1330 if (sipe_strequal(group
->name
, name
)) {
1333 entry
= entry
->next
;
1339 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1342 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1343 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1344 send_soap_request(sip
, body
);
1346 g_free(group
->name
);
1347 group
->name
= g_strdup(name
);
1351 * Only appends if no such value already stored.
1354 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1355 GSList
* res
= list
;
1356 if (!g_slist_find_custom(list
, data
, func
)) {
1357 res
= g_slist_insert_sorted(list
, data
, func
);
1363 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1364 return group1
->id
- group2
->id
;
1368 * Returns string like "2 4 7 8" - group ids buddy belong to.
1371 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1374 //creating array from GList, converting int to gchar*
1375 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1376 GSList
*entry
= buddy
->groups
;
1378 if (!ids_arr
) return NULL
;
1381 struct sipe_group
* group
= entry
->data
;
1382 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1383 entry
= entry
->next
;
1387 res
= g_strjoinv(" ", ids_arr
);
1388 g_strfreev(ids_arr
);
1393 * Sends buddy update to server
1396 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1398 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1399 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1401 if (buddy
&& purple_buddy
) {
1402 const char *alias
= purple_buddy_get_alias(purple_buddy
);
1403 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1406 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1408 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1409 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1411 send_soap_request(sip
, body
);
1418 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1420 if (msg
->response
== 200) {
1421 struct sipe_group
*group
;
1422 struct group_user_context
*ctx
= trans
->payload
->data
;
1426 struct sipe_buddy
*buddy
;
1428 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1433 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1439 group_id
= xmlnode_get_data(node
);
1445 group
= g_new0(struct sipe_group
, 1);
1446 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1448 group
->name
= g_strdup(ctx
->group_name
);
1450 sipe_group_add(sip
, group
);
1452 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1454 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1457 sipe_group_set_user(sip
, ctx
->user_name
);
1465 static void sipe_group_context_destroy(gpointer data
)
1467 struct group_user_context
*ctx
= data
;
1468 g_free(ctx
->group_name
);
1469 g_free(ctx
->user_name
);
1473 static void sipe_group_create (struct sipe_account_data
*sip
, const gchar
*name
, const gchar
* who
)
1475 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
1476 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
1478 ctx
->group_name
= g_strdup(name
);
1479 ctx
->user_name
= g_strdup(who
);
1480 payload
->destroy
= sipe_group_context_destroy
;
1481 payload
->data
= ctx
;
1483 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1484 send_soap_request_with_cb(sip
, NULL
, body
, process_add_group_response
, payload
);
1489 * Data structure for scheduled actions
1492 struct scheduled_action
{
1495 * Format is <Event>[<Data>...]
1496 * Example: <presence><sip:user@domain.com> or <registration>
1499 guint timeout_handler
;
1500 gboolean repetitive
;
1502 GDestroyNotify destroy
;
1503 struct sipe_account_data
*sip
;
1509 * Should return FALSE if repetitive action is not needed
1511 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1514 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1515 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1516 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1517 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1518 ret
= sched_action
->repetitive
;
1519 if (sched_action
->destroy
) {
1520 (*sched_action
->destroy
)(sched_action
->payload
);
1522 g_free(sched_action
->name
);
1523 g_free(sched_action
);
1528 * Kills action timer effectively cancelling
1531 * @param name of action
1533 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1537 if (!sip
->timeouts
|| !name
) return;
1539 entry
= sip
->timeouts
;
1541 struct scheduled_action
*sched_action
= entry
->data
;
1542 if(sipe_strequal(sched_action
->name
, name
)) {
1543 GSList
*to_delete
= entry
;
1544 entry
= entry
->next
;
1545 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1546 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1547 purple_timeout_remove(sched_action
->timeout_handler
);
1548 if (sched_action
->destroy
) {
1549 (*sched_action
->destroy
)(sched_action
->payload
);
1551 g_free(sched_action
->name
);
1552 g_free(sched_action
);
1554 entry
= entry
->next
;
1560 sipe_schedule_action0(const gchar
*name
,
1564 GDestroyNotify destroy
,
1565 struct sipe_account_data
*sip
,
1568 struct scheduled_action
*sched_action
;
1570 /* Make sure each action only exists once */
1571 sipe_cancel_scheduled_action(sip
, name
);
1573 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1574 sched_action
= g_new0(struct scheduled_action
, 1);
1575 sched_action
->repetitive
= FALSE
;
1576 sched_action
->name
= g_strdup(name
);
1577 sched_action
->action
= action
;
1578 sched_action
->destroy
= destroy
;
1579 sched_action
->sip
= sip
;
1580 sched_action
->payload
= payload
;
1581 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1582 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1583 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1584 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1588 sipe_schedule_action(const gchar
*name
,
1591 GDestroyNotify destroy
,
1592 struct sipe_account_data
*sip
,
1595 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1599 * Same as sipe_schedule_action() but timeout is in milliseconds.
1602 sipe_schedule_action_msec(const gchar
*name
,
1605 GDestroyNotify destroy
,
1606 struct sipe_account_data
*sip
,
1609 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1613 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1614 time_t calculate_from
);
1617 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
1620 sipe_get_status_by_availability(int avail
,
1624 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
1625 const char *status_id
,
1626 const char *message
,
1627 time_t do_not_publish
[]);
1630 sipe_apply_calendar_status(struct sipe_account_data
*sip
,
1631 struct sipe_buddy
*sbuddy
,
1632 const char *status_id
)
1634 time_t cal_avail_since
;
1635 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
1639 if (!sbuddy
) return;
1641 if (cal_status
< SIPE_CAL_NO_DATA
) {
1642 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_status : %d for %s\n", cal_status
, sbuddy
->name
);
1643 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
1646 /* scheduled Cal update call */
1648 status_id
= sbuddy
->last_non_cal_status_id
;
1649 g_free(sbuddy
->activity
);
1650 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
1654 purple_debug_info("sipe", "sipe_apply_calendar_status: status_id is NULL for %s, exiting.\n",
1655 sbuddy
->name
? sbuddy
->name
: "" );
1659 /* adjust to calendar status */
1660 if (cal_status
!= SIPE_CAL_NO_DATA
) {
1661 purple_debug_info("sipe", "sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
1663 if (cal_status
== SIPE_CAL_BUSY
1664 && cal_avail_since
> sbuddy
->user_avail_since
1665 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
1667 status_id
= SIPE_STATUS_ID_BUSY
;
1668 g_free(sbuddy
->activity
);
1669 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
1671 avail
= sipe_get_availability_by_status(status_id
, NULL
);
1673 purple_debug_info("sipe", "sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
1674 if (cal_avail_since
> sbuddy
->activity_since
) {
1675 if (cal_status
== SIPE_CAL_OOF
1676 && avail
>= 15000) /* 12000 in 2007 */
1678 g_free(sbuddy
->activity
);
1679 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
1684 /* then set status_id actually */
1685 purple_debug_info("sipe", "sipe_apply_calendar_status: to %s for %s\n", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
1686 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, status_id
, NULL
);
1688 /* set our account state to the one in roaming (including calendar info) */
1689 self_uri
= sip_uri_self(sip
);
1690 if (sip
->initial_state_published
&& sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
1691 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
1692 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
1695 purple_debug_info("sipe", "sipe_apply_calendar_status: switch to '%s' for the account\n", sip
->status
);
1696 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
1702 sipe_got_user_status(struct sipe_account_data
*sip
,
1704 const char *status_id
)
1706 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
1708 if (!sbuddy
) return;
1710 /* Check if on 2005 system contact's calendar,
1711 * then set/preserve it.
1713 if (!sip
->ocs2007
) {
1714 sipe_apply_calendar_status(sip
, sbuddy
, status_id
);
1716 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
1721 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
1722 struct sipe_buddy
*sbuddy
,
1723 struct sipe_account_data
*sip
)
1725 sipe_apply_calendar_status(sip
, sbuddy
, NULL
);
1729 * Updates contact's status
1730 * based on their calendar information.
1732 * Applicability: 2005 systems
1735 update_calendar_status(struct sipe_account_data
*sip
)
1737 purple_debug_info("sipe", "update_calendar_status() started.\n");
1738 g_hash_table_foreach(sip
->buddies
, (GHFunc
)update_calendar_status_cb
, (gpointer
)sip
);
1740 /* repeat scheduling */
1741 sipe_sched_calendar_status_update(sip
, time(NULL
) + 3*60 /* 3 min */);
1745 * Schedules process of contacts' status update
1746 * based on their calendar information.
1747 * Should be scheduled to the beginning of every
1748 * 15 min interval, like:
1749 * 13:00, 13:15, 13:30, 13:45, etc.
1751 * Applicability: 2005 systems
1754 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1755 time_t calculate_from
)
1757 int interval
= 15*60;
1758 /** start of the beginning of closest 15 min interval. */
1759 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1761 purple_debug_info("sipe", "sipe_sched_calendar_status_update: calculate_from time: %s",
1762 asctime(localtime(&calculate_from
)));
1763 purple_debug_info("sipe", "sipe_sched_calendar_status_update: next start time : %s",
1764 asctime(localtime(&next_start
)));
1766 sipe_schedule_action("<+2005-cal-status>",
1767 (int)(next_start
- time(NULL
)),
1768 (Action
)update_calendar_status
,
1775 * Schedules process of self status publish
1776 * based on own calendar information.
1777 * Should be scheduled to the beginning of every
1778 * 15 min interval, like:
1779 * 13:00, 13:15, 13:30, 13:45, etc.
1781 * Applicability: 2007+ systems
1784 sipe_sched_calendar_status_self_publish(struct sipe_account_data
*sip
,
1785 time_t calculate_from
)
1787 int interval
= 5*60;
1788 /** start of the beginning of closest 5 min interval. */
1789 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1791 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: calculate_from time: %s",
1792 asctime(localtime(&calculate_from
)));
1793 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: next start time : %s",
1794 asctime(localtime(&next_start
)));
1796 sipe_schedule_action("<+2007-cal-status>",
1797 (int)(next_start
- time(NULL
)),
1798 (Action
)publish_calendar_status_self
,
1804 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1806 /** Should be g_free()'d
1809 sipe_get_subscription_key(const gchar
*event
,
1814 if (is_empty(event
)) return NULL
;
1816 if (event
&& sipe_strcase_equal(event
, "presence")) {
1817 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1818 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, with
);
1820 /* @TODO drop participated buddies' just_added flag */
1822 /* Subscription is identified by <event> key */
1823 key
= g_strdup_printf("<%s>", event
);
1829 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1830 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1832 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
1833 const gchar
*event
= sipmsg_find_header(msg
, "Event");
1836 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
1838 struct sipmsg
*request_msg
= trans
->msg
;
1839 event
= sipmsg_find_header(request_msg
, "Event");
1842 key
= sipe_get_subscription_key(event
, with
);
1844 /* 200 OK; 481 Call Leg Does Not Exist */
1845 if (key
&& (msg
->response
== 200 || msg
->response
== 481)) {
1846 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
1847 g_hash_table_remove(sip
->subscriptions
, key
);
1848 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
1852 /* create/store subscription dialog if not yet */
1853 if (msg
->response
== 200) {
1854 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1855 gchar
*cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
1858 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
1859 g_hash_table_insert(sip
->subscriptions
, g_strdup(key
), subscription
);
1861 subscription
->dialog
.callid
= g_strdup(callid
);
1862 subscription
->dialog
.cseq
= atoi(cseq
);
1863 subscription
->dialog
.with
= g_strdup(with
);
1864 subscription
->event
= g_strdup(event
);
1865 sipe_dialog_parse(&subscription
->dialog
, msg
, TRUE
);
1867 purple_debug_info("sipe", "process_subscribe_response: subscription dialog added for: %s\n", key
);
1876 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1878 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1883 static void sipe_subscribe_resource_uri(const char *name
,
1884 SIPE_UNUSED_PARAMETER gpointer value
,
1885 gchar
**resources_uri
)
1887 gchar
*tmp
= *resources_uri
;
1888 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1892 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1894 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1895 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1896 gchar
*tmp
= *resources_uri
;
1898 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
1900 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
1905 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1906 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1907 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1908 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1909 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1912 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1915 gchar
*contact
= get_contact(sip
);
1918 gchar
*require
= "";
1920 gchar
*autoextend
= "";
1921 gchar
*content_type
;
1922 struct sip_dialog
*dialog
;
1925 require
= ", categoryList";
1926 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1927 content_type
= "application/msrtc-adrl-categorylist+xml";
1928 content
= g_strdup_printf(
1929 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1930 "<action name=\"subscribe\" id=\"63792024\">\n"
1931 "<adhocList>\n%s</adhocList>\n"
1932 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1933 "<category name=\"calendarData\"/>\n"
1934 "<category name=\"contactCard\"/>\n"
1935 "<category name=\"note\"/>\n"
1936 "<category name=\"state\"/>\n"
1939 "</batchSub>", sip
->username
, resources_uri
);
1941 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1942 content_type
= "application/adrl+xml";
1943 content
= g_strdup_printf(
1944 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1945 "<create xmlns=\"\">\n%s</create>\n"
1946 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1948 g_free(resources_uri
);
1950 request
= g_strdup_printf(
1951 "Require: adhoclist%s\r\n"
1952 "Supported: eventlist\r\n"
1953 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1954 "Supported: ms-piggyback-first-notify\r\n"
1955 "%sSupported: ms-benotify\r\n"
1956 "Proxy-Require: ms-benotify\r\n"
1957 "Event: presence\r\n"
1958 "Content-Type: %s\r\n"
1959 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1962 /* subscribe to buddy presence */
1963 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1964 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1965 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1966 purple_debug_info("sipe", "sipe_subscribe_presence_batched_to: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1968 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1976 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1977 SIPE_UNUSED_PARAMETER
void *unused
)
1979 gchar
*to
= sip_uri_self(sip
);
1980 gchar
*resources_uri
= g_strdup("");
1982 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1984 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1987 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1990 struct presence_batched_routed
{
1995 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1997 struct presence_batched_routed
*data
= payload
;
1998 GSList
*buddies
= data
->buddies
;
2000 g_free(buddies
->data
);
2001 buddies
= buddies
->next
;
2003 g_slist_free(data
->buddies
);
2008 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
2010 struct presence_batched_routed
*data
= payload
;
2011 GSList
*buddies
= data
->buddies
;
2012 gchar
*resources_uri
= g_strdup("");
2014 gchar
*tmp
= resources_uri
;
2015 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
2017 buddies
= buddies
->next
;
2019 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
2020 g_strdup(data
->host
));
2024 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
2025 * The user sends a single SUBSCRIBE request to the subscribed contact.
2026 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
2030 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
2034 gchar
*to
= sip_uri((char *)buddy_name
);
2035 gchar
*tmp
= get_contact(sip
);
2037 gchar
*content
= NULL
;
2038 gchar
*autoextend
= "";
2039 gchar
*content_type
= "";
2040 struct sip_dialog
*dialog
;
2041 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, to
);
2042 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
2044 if (sbuddy
) sbuddy
->just_added
= FALSE
;
2047 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
2049 autoextend
= "Supported: com.microsoft.autoextend\r\n";
2052 request
= g_strdup_printf(
2053 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
2054 "Supported: ms-piggyback-first-notify\r\n"
2055 "%s%sSupported: ms-benotify\r\n"
2056 "Proxy-Require: ms-benotify\r\n"
2057 "Event: presence\r\n"
2058 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
2061 content
= g_strdup_printf(
2062 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
2063 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
2064 "<resource uri=\"%s\"%s\n"
2066 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
2067 "<category name=\"calendarData\"/>\n"
2068 "<category name=\"contactCard\"/>\n"
2069 "<category name=\"note\"/>\n"
2070 "<category name=\"state\"/>\n"
2073 "</batchSub>", sip
->username
, to
, context
);
2078 /* subscribe to buddy presence */
2079 /* Subscription is identified by ACTION_NAME_PRESENCE key */
2080 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
2081 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2082 purple_debug_info("sipe", "sipe_subscribe_presence_single: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2084 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
2092 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
2094 purple_debug_info("sipe", "sipe_set_status: status=%s\n", purple_status_get_id(status
));
2096 if (!purple_status_is_active(status
))
2100 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
2105 time_t now
= time(NULL
);
2106 const char *status_id
= purple_status_get_id(status
);
2107 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
2108 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2109 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
2111 /* when other point of presence clears note, but we are keeping
2112 * state if OOF note.
2114 if (do_not_publish
&& !note
&& sip
->ews
&& sip
->ews
->oof_note
) {
2115 purple_debug_info("sipe", "sipe_set_status: enabling publication as OOF note keepers.\n");
2116 do_not_publish
= FALSE
;
2119 purple_debug_info("sipe", "sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d\n",
2120 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
2122 sip
->do_not_publish
[activity
] = 0;
2123 purple_debug_info("sipe", "sipe_set_status: set: sip->do_not_publish[%s]=%d [0]\n",
2124 status_id
, (int)sip
->do_not_publish
[activity
]);
2128 purple_debug_info("sipe", "sipe_set_status: publication was switched off, exiting.\n");
2132 g_free(sip
->status
);
2133 sip
->status
= g_strdup(status_id
);
2135 /* hack to escape apostrof before comparison */
2136 tmp
= note
? purple_strreplace(note
, "'", "'") : NULL
;
2138 /* this will preserve OOF flag as well */
2139 if (!sipe_strequal(tmp
, sip
->note
)) {
2140 sip
->is_oof_note
= FALSE
;
2142 sip
->note
= g_strdup(note
);
2143 sip
->note_since
= time(NULL
);
2147 /* schedule 2 sec to capture idle flag */
2148 action_name
= g_strdup_printf("<%s>", "+set-status");
2149 sipe_schedule_action(action_name
, SIPE_IDLE_SET_DELAY
, (Action
)send_presence_status
, NULL
, sip
, NULL
);
2150 g_free(action_name
);
2155 sipe_set_idle(PurpleConnection
* gc
,
2158 purple_debug_info("sipe", "sipe_set_idle: interval=%d\n", interval
);
2161 struct sipe_account_data
*sip
= gc
->proto_data
;
2164 sip
->idle_switch
= time(NULL
);
2165 purple_debug_info("sipe", "sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
2171 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
2172 SIPE_UNUSED_PARAMETER
const char *alias
)
2174 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2175 sipe_group_set_user(sip
, name
);
2179 sipe_group_buddy(PurpleConnection
*gc
,
2181 const char *old_group_name
,
2182 const char *new_group_name
)
2184 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2185 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
2186 struct sipe_group
* old_group
= NULL
;
2187 struct sipe_group
* new_group
;
2189 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
2190 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
2192 if(!buddy
) { // buddy not in roaming list
2196 if (old_group_name
) {
2197 old_group
= sipe_group_find_by_name(sip
, old_group_name
);
2199 new_group
= sipe_group_find_by_name(sip
, new_group_name
);
2202 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
2203 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
2207 sipe_group_create(sip
, new_group_name
, who
);
2209 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
2210 sipe_group_set_user(sip
, who
);
2214 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2216 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2218 /* libpurple can call us with undefined buddy or group */
2219 if (buddy
&& group
) {
2220 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2222 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2223 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
2224 purple_blist_rename_buddy(buddy
, buddy_name
);
2227 /* Prepend sip: if needed */
2228 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
2229 gchar
*buf
= sip_uri_from_name(buddy
->name
);
2230 purple_blist_rename_buddy(buddy
, buf
);
2234 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
2235 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
2236 purple_debug_info("sipe", "sipe_add_buddy: adding %s\n", buddy
->name
);
2237 b
->name
= g_strdup(buddy
->name
);
2238 b
->just_added
= TRUE
;
2239 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
2240 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
2241 /* @TODO should go to callback */
2242 sipe_subscribe_presence_single(sip
, b
->name
);
2244 purple_debug_info("sipe", "sipe_add_buddy: buddy %s already in internal list\n", buddy
->name
);
2249 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
2253 * We are calling g_hash_table_foreach_steal(). That means that no
2254 * key/value deallocation functions are called. Therefore the glib
2255 * hash code does not touch the key (buddy->name) or value (buddy)
2256 * of the to-be-deleted hash node at all. It follows that we
2258 * - MUST free the memory for the key ourselves and
2259 * - ARE allowed to do it in this function
2261 * Conclusion: glib must be broken on the Windows platform if sipe
2262 * crashes with SIGTRAP when closing. You'll have to live
2263 * with the memory leak until this is fixed.
2265 g_free(buddy
->name
);
2267 g_free(buddy
->activity
);
2268 g_free(buddy
->meeting_subject
);
2269 g_free(buddy
->meeting_location
);
2270 g_free(buddy
->note
);
2272 g_free(buddy
->cal_start_time
);
2273 g_free(buddy
->cal_free_busy_base64
);
2274 g_free(buddy
->cal_free_busy
);
2275 g_free(buddy
->last_non_cal_activity
);
2277 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
2279 g_free(buddy
->device_name
);
2280 g_slist_free(buddy
->groups
);
2285 * Unassociates buddy from group first.
2286 * Then see if no groups left, removes buddy completely.
2287 * Otherwise updates buddy groups on server.
2289 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2291 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2292 struct sipe_buddy
*b
;
2293 struct sipe_group
*g
= NULL
;
2295 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2298 b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
2302 g
= sipe_group_find_by_name(sip
, group
->name
);
2306 b
->groups
= g_slist_remove(b
->groups
, g
);
2307 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
2310 if (g_slist_length(b
->groups
) < 1) {
2311 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2312 sipe_cancel_scheduled_action(sip
, action_name
);
2313 g_free(action_name
);
2315 g_hash_table_remove(sip
->buddies
, buddy
->name
);
2318 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
2319 send_soap_request(sip
, body
);
2325 //updates groups on server
2326 sipe_group_set_user(sip
, b
->name
);
2332 sipe_rename_group(PurpleConnection
*gc
,
2333 const char *old_name
,
2335 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
2337 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2338 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, old_name
);
2340 sipe_group_rename(sip
, s_group
, group
->name
);
2342 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
2347 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
2349 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2350 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
2353 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
2354 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
2355 send_soap_request(sip
, body
);
2358 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
2359 g_free(s_group
->name
);
2362 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
2366 /** All statuses need message attribute to pass Note */
2367 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
2369 PurpleStatusType
*type
;
2370 GList
*types
= NULL
;
2372 /* Macros to reduce code repetition.
2373 Translators: noun */
2374 #define SIPE_ADD_STATUS(prim,id,name,user) type = purple_status_type_new_with_attrs( \
2376 TRUE, user, FALSE, \
2377 SIPE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
2379 types = g_list_append(types, type);
2382 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2388 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2389 sipe_activity_map
[SIPE_ACTIVITY_BUSY
].status_id
,
2390 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSY
),
2393 /* Do Not Disturb */
2394 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2395 sipe_activity_map
[SIPE_ACTIVITY_DND
].status_id
,
2400 /* Goes first in the list as
2401 * purple picks the first status with the AWAY type
2404 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2410 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2411 sipe_activity_map
[SIPE_ACTIVITY_BRB
].status_id
,
2412 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BRB
),
2415 /* Appear Offline */
2416 SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE
,
2422 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
,
2426 types
= g_list_append(types
, type
);
2432 * A callback for g_hash_table_foreach
2435 sipe_buddy_subscribe_cb(char *buddy_name
,
2436 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
2437 struct sipe_account_data
*sip
)
2439 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy_name
);
2440 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
2441 guint time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
2442 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
2444 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy_name
));
2445 g_free(action_name
);
2449 * Removes entries from purple buddy list
2450 * that does not correspond ones in the roaming contact list.
2452 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
2453 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
2454 GSList
*entry
= buddies
;
2455 struct sipe_buddy
*buddy
;
2459 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
2460 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
2463 g
= purple_buddy_get_group(b
);
2464 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
2466 gboolean in_sipe_groups
= FALSE
;
2467 GSList
*entry2
= buddy
->groups
;
2469 struct sipe_group
*group
= entry2
->data
;
2470 if (sipe_strequal(group
->name
, g
->name
)) {
2471 in_sipe_groups
= TRUE
;
2474 entry2
= entry2
->next
;
2476 if(!in_sipe_groups
) {
2477 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
2478 purple_blist_remove_buddy(b
);
2481 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
2482 purple_blist_remove_buddy(b
);
2484 entry
= entry
->next
;
2486 g_slist_free(buddies
);
2489 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2491 int len
= msg
->bodylen
;
2493 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
2496 const gchar
*contacts_delta
;
2497 xmlnode
*group_node
;
2498 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
2502 /* Convert the contact from XML to Purple Buddies */
2503 isc
= xmlnode_from_str(msg
->body
, len
);
2508 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
2509 if (contacts_delta
) {
2510 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2513 if (sipe_strequal(isc
->name
, "contactList")) {
2516 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
2517 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2518 const char *name
= xmlnode_get_attrib(group_node
, "name");
2520 if (g_str_has_prefix(name
, "~")) {
2521 name
= _("Other Contacts");
2523 group
->name
= g_strdup(name
);
2524 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2526 sipe_group_add(sip
, group
);
2529 // Make sure we have at least one group
2530 if (g_slist_length(sip
->groups
) == 0) {
2531 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2532 PurpleGroup
*purple_group
;
2533 group
->name
= g_strdup(_("Other Contacts"));
2535 purple_group
= purple_group_new(group
->name
);
2536 purple_blist_add_group(purple_group
, NULL
);
2537 sip
->groups
= g_slist_append(sip
->groups
, group
);
2540 /* Parse contacts */
2541 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2542 const gchar
*uri
= xmlnode_get_attrib(item
, "uri");
2543 const gchar
*name
= xmlnode_get_attrib(item
, "name");
2545 struct sipe_buddy
*buddy
= NULL
;
2547 gchar
**item_groups
;
2550 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2551 tmp
= sip_uri_from_name(uri
);
2552 buddy_name
= g_ascii_strdown(tmp
, -1);
2555 /* assign to group Other Contacts if nothing else received */
2556 tmp
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2558 struct sipe_group
*group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
2560 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
2562 item_groups
= g_strsplit(tmp
, " ", 0);
2565 while (item_groups
[i
]) {
2566 struct sipe_group
*group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2568 // If couldn't find the right group for this contact, just put them in the first group we have
2569 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2570 group
= sip
->groups
->data
;
2573 if (group
!= NULL
) {
2574 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2576 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2577 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2579 purple_debug_info("sipe", "Created new buddy %s with alias %s\n", buddy_name
, uri
);
2582 if (sipe_strcase_equal(uri
, purple_buddy_get_alias(b
))) {
2583 if (name
!= NULL
&& strlen(name
) != 0) {
2584 purple_blist_alias_buddy(b
, name
);
2586 purple_debug_info("sipe", "Replaced buddy %s alias with %s\n", buddy_name
, name
);
2591 buddy
= g_new0(struct sipe_buddy
, 1);
2592 buddy
->name
= g_strdup(b
->name
);
2593 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2596 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2598 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2600 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2605 } // while, contact groups
2606 g_strfreev(item_groups
);
2611 sipe_cleanup_local_blist(sip
);
2613 /* Add self-contact if not there yet. 2005 systems. */
2614 /* This will resemble subscription to roaming_self in 2007 systems */
2615 if (!sip
->ocs2007
) {
2616 gchar
*self_uri
= sip_uri_self(sip
);
2617 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, self_uri
);
2620 buddy
= g_new0(struct sipe_buddy
, 1);
2621 buddy
->name
= g_strdup(self_uri
);
2622 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2629 /* subscribe to buddies */
2630 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2631 if (sip
->batched_support
) {
2632 sipe_subscribe_presence_batched(sip
, NULL
);
2634 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2636 sip
->subscribed_buddies
= TRUE
;
2638 /* for 2005 systems schedule contacts' status update
2639 * based on their calendar information
2641 if (!sip
->ocs2007
) {
2642 sipe_sched_calendar_status_update(sip
, time(NULL
));
2649 * Subscribe roaming contacts
2651 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2653 gchar
*to
= sip_uri_self(sip
);
2654 gchar
*tmp
= get_contact(sip
);
2655 gchar
*hdr
= g_strdup_printf(
2656 "Event: vnd-microsoft-roaming-contacts\r\n"
2657 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2658 "Supported: com.microsoft.autoextend\r\n"
2659 "Supported: ms-benotify\r\n"
2660 "Proxy-Require: ms-benotify\r\n"
2661 "Supported: ms-piggyback-first-notify\r\n"
2662 "Contact: %s\r\n", tmp
);
2665 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2670 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2671 SIPE_UNUSED_PARAMETER
void *unused
)
2674 struct sip_dialog
*dialog
;
2675 gchar
*to
= sip_uri_self(sip
);
2676 gchar
*tmp
= get_contact(sip
);
2677 gchar
*hdr
= g_strdup_printf(
2678 "Event: presence.wpending\r\n"
2679 "Accept: text/xml+msrtc.wpending\r\n"
2680 "Supported: com.microsoft.autoextend\r\n"
2681 "Supported: ms-benotify\r\n"
2682 "Proxy-Require: ms-benotify\r\n"
2683 "Supported: ms-piggyback-first-notify\r\n"
2684 "Contact: %s\r\n", tmp
);
2687 /* Subscription is identified by <event> key */
2688 key
= g_strdup_printf("<%s>", "presence.wpending");
2689 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2690 purple_debug_info("sipe", "sipe_subscribe_presence_wpending: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2692 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", dialog
, process_subscribe_response
);
2700 * Fires on deregistration event initiated by server.
2701 * [MS-SIPREGE] SIP extension.
2706 // Content-Type: text/registration-event
2707 // subscription-state: terminated;expires=0
2708 // ms-diagnostics-public: 4141;reason="User disabled"
2710 // deregistered;event=rejected
2712 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2714 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2715 gchar
*event
= NULL
;
2716 gchar
*reason
= NULL
;
2717 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
2720 diagnostics
= diagnostics
? diagnostics
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2721 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2723 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2724 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2725 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2726 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2728 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2732 if (diagnostics
!= NULL
) {
2733 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
2734 } else { // for LCS2005
2736 if (event
&& sipe_strcase_equal(event
, "unregistered")) {
2737 error_id
= 4140; // [MS-SIPREGE]
2738 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2739 reason
= g_strdup(_("you are already signed in at another location"));
2740 } else if (event
&& sipe_strcase_equal(event
, "rejected")) {
2742 reason
= g_strdup(_("user disabled")); // [MS-OCER]
2743 } else if (event
&& sipe_strcase_equal(event
, "deactivated")) {
2745 reason
= g_strdup(_("user moved")); // [MS-OCER]
2749 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
2752 sip
->gc
->wants_to_die
= TRUE
;
2753 purple_connection_error(sip
->gc
, warning
);
2758 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2760 xmlnode
*xn_provision_group_list
;
2763 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2765 /* provisionGroup */
2766 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2767 if (sipe_strequal("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2768 g_free(sip
->focus_factory_uri
);
2769 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2770 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2771 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2775 xmlnode_free(xn_provision_group_list
);
2778 /** for 2005 system */
2780 sipe_process_provisioning(struct sipe_account_data
*sip
,
2783 xmlnode
*xn_provision
;
2786 xn_provision
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2787 if ((node
= xmlnode_get_child(xn_provision
, "user"))) {
2788 purple_debug_info("sipe", "sipe_process_provisioning: uri=%s\n", xmlnode_get_attrib(node
, "uri"));
2789 if ((node
= xmlnode_get_child(node
, "line"))) {
2790 const gchar
*line_uri
= xmlnode_get_attrib(node
, "uri");
2791 const gchar
*server
= xmlnode_get_attrib(node
, "server");
2792 purple_debug_info("sipe", "sipe_process_provisioning: line_uri=%s server=%s\n", line_uri
, server
);
2793 sip_csta_open(sip
, line_uri
, server
);
2796 xmlnode_free(xn_provision
);
2799 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2801 const gchar
*contacts_delta
;
2804 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2810 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2813 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2820 free_container(struct sipe_container
*container
)
2824 if (!container
) return;
2826 entry
= container
->members
;
2828 void *data
= entry
->data
;
2829 entry
= g_slist_remove(entry
, data
);
2836 * Finds locally stored MS-PRES container member
2838 static struct sipe_container_member
*
2839 sipe_find_container_member(struct sipe_container
*container
,
2843 struct sipe_container_member
*member
;
2846 if (container
== NULL
|| type
== NULL
) {
2850 entry
= container
->members
;
2852 member
= entry
->data
;
2853 if (!g_strcasecmp(member
->type
, type
)
2854 && ((!member
->value
&& !value
)
2855 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2859 entry
= entry
->next
;
2865 * Finds locally stored MS-PRES container by id
2867 static struct sipe_container
*
2868 sipe_find_container(struct sipe_account_data
*sip
,
2871 struct sipe_container
*container
;
2878 entry
= sip
->containers
;
2880 container
= entry
->data
;
2881 if (id
== container
->id
) {
2884 entry
= entry
->next
;
2898 sipe_find_access_level(struct sipe_account_data
*sip
,
2902 guint containers
[] = {32000, 400, 300, 200, 100};
2905 for (i
= 0; i
< 5; i
++) {
2906 struct sipe_container_member
*member
;
2907 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2908 if (!container
) continue;
2910 member
= sipe_find_container_member(container
, type
, value
);
2912 return containers
[i
];
2920 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2922 guint container_version
,
2923 const gchar
* action
,
2927 gchar
*self
= sip_uri_self(sip
);
2928 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2931 gchar
*body
= g_strdup_printf(
2932 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2933 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2934 "</setContainerMembers>",
2942 contact
= get_contact(sip
);
2943 hdr
= g_strdup_printf("Contact: %s\r\n"
2944 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2947 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2955 free_publication(struct sipe_publication
*publication
)
2957 g_free(publication
->category
);
2958 g_free(publication
->cal_event_hash
);
2959 g_free(publication
->note
);
2961 g_free(publication
->working_hours_xml_str
);
2962 g_free(publication
->fb_start_str
);
2963 g_free(publication
->free_busy_base64
);
2965 g_free(publication
);
2968 /* key is <category><instance><container> */
2970 sipe_is_our_publication(struct sipe_account_data
*sip
,
2975 /* filling keys for our publications if not yet cached */
2976 if (!sip
->our_publication_keys
) {
2977 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2978 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2979 guint user_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
);
2980 guint calendar_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
2981 guint cal_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
);
2982 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
2983 guint note_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
);
2985 purple_debug_info("sipe", "* Our Publication Instances *\n");
2986 purple_debug_info("sipe", "\tDevice : %u\t0x%08X\n", device_instance
, device_instance
);
2987 purple_debug_info("sipe", "\tMachine State : %u\t0x%08X\n", machine_instance
, machine_instance
);
2988 purple_debug_info("sipe", "\tUser Stare : %u\t0x%08X\n", user_instance
, user_instance
);
2989 purple_debug_info("sipe", "\tCalendar State : %u\t0x%08X\n", calendar_instance
, calendar_instance
);
2990 purple_debug_info("sipe", "\tCalendar OOF State : %u\t0x%08X\n", cal_oof_instance
, cal_oof_instance
);
2991 purple_debug_info("sipe", "\tCalendar FreeBusy : %u\t0x%08X\n", cal_data_instance
, cal_data_instance
);
2992 purple_debug_info("sipe", "\tOOF Note : %u\t0x%08X\n", note_oof_instance
, note_oof_instance
);
2993 purple_debug_info("sipe", "\tNote : %u\n", 0);
2994 purple_debug_info("sipe", "\tCalendar WorkingHours: %u\n", 0);
2997 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2998 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
3000 /* state:machineState */
3001 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3002 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
3003 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3004 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
3006 /* state:userState */
3007 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3008 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
3009 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3010 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
3012 /* state:calendarState */
3013 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3014 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
3015 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3016 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
3018 /* state:calendarState OOF */
3019 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3020 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
3021 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3022 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
3025 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3026 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
3027 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3028 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
3029 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3030 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
3033 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3034 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
3035 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3036 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
3037 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3038 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
3040 /* calendarData:WorkingHours */
3041 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3042 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
3043 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3044 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
3045 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3046 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
3047 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3048 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
3049 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3050 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
3051 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3052 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
3054 /* calendarData:FreeBusy */
3055 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3056 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
3057 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3058 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
3059 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3060 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
3061 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3062 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
3063 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3064 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
3065 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3066 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
3068 //purple_debug_info("sipe", "sipe_is_our_publication: sip->our_publication_keys length=%d\n",
3069 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
3072 //purple_debug_info("sipe", "sipe_is_our_publication: key=%s\n", key);
3074 entry
= sip
->our_publication_keys
;
3076 //purple_debug_info("sipe", " sipe_is_our_publication: entry->data=%s\n", entry->data);
3077 if (sipe_strequal(entry
->data
, key
)) {
3080 entry
= entry
->next
;
3085 /** Property names to store in blist.xml */
3086 #define ALIAS_PROP "alias"
3087 #define EMAIL_PROP "email"
3088 #define PHONE_PROP "phone"
3089 #define PHONE_DISPLAY_PROP "phone-display"
3090 #define PHONE_MOBILE_PROP "phone-mobile"
3091 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
3092 #define PHONE_HOME_PROP "phone-home"
3093 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
3094 #define PHONE_OTHER_PROP "phone-other"
3095 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
3096 #define PHONE_CUSTOM1_PROP "phone-custom1"
3097 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
3098 #define SITE_PROP "site"
3099 #define COMPANY_PROP "company"
3100 #define DEPARTMENT_PROP "department"
3101 #define TITLE_PROP "title"
3102 #define OFFICE_PROP "office"
3103 /** implies work address */
3104 #define ADDRESS_STREET_PROP "address-street"
3105 #define ADDRESS_CITY_PROP "address-city"
3106 #define ADDRESS_STATE_PROP "address-state"
3107 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
3108 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
3111 * Tries to figure out user first and last name
3112 * based on Display Name and email properties.
3114 * Allocates memory - must be g_free()'d
3116 * Examples to parse:
3118 * First Last - Company Name
3121 * Last, First (C)(STP) (Company)
3122 * first.last@company.com (preprocessed as "first last")
3123 * first.last.company.com@reuters.net (preprocessed as "first last company com")
3125 * Unusable examples:
3126 * user@company.com (preprocessed as "user")
3127 * first.m.last@company.com (preprocessed as "first m last")
3128 * user.company.com@reuters.net (preprocessed as "user company com")
3131 sipe_get_first_last_names(struct sipe_account_data
*sip
,
3136 PurpleBuddy
*p_buddy
;
3139 const char *first
, *last
;
3142 gboolean has_comma
= FALSE
;
3144 if (!sip
|| !uri
) return;
3146 p_buddy
= purple_find_buddy(sip
->account
, uri
);
3148 if (!p_buddy
) return;
3150 display_name
= g_strdup(purple_buddy_get_alias(p_buddy
));
3151 email
= purple_blist_node_get_string(&p_buddy
->node
, EMAIL_PROP
);
3153 if (!display_name
&& !email
) return;
3155 /* if no display name, make "first last anything_else" out of email */
3156 if (email
&& !display_name
) {
3157 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
3158 display_name
= purple_strreplace((tmp
= display_name
), ".", " ");
3163 has_comma
= (strstr(display_name
, ",") != NULL
);
3164 display_name
= purple_strreplace((tmp
= display_name
), ", ", " ");
3166 display_name
= purple_strreplace((tmp
= display_name
), ",", " ");
3170 parts
= g_strsplit(display_name
, " ", 0);
3172 if (!parts
[0] || !parts
[1]) {
3173 g_free(display_name
);
3187 *first_name
= g_strstrip(g_strdup(first
));
3191 *last_name
= g_strstrip(g_strdup(last
));
3194 g_free(display_name
);
3199 * Update user information
3201 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3202 * @param property_name
3203 * @param property_value may be modified to strip white space
3206 sipe_update_user_info(struct sipe_account_data
*sip
,
3208 const char *property_name
,
3209 char *property_value
)
3211 GSList
*buddies
, *entry
;
3213 if (!property_name
|| strlen(property_name
) == 0) return;
3216 property_value
= g_strstrip(property_value
);
3218 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
3220 const char *prop_str
;
3221 const char *server_alias
;
3222 PurpleBuddy
*p_buddy
= entry
->data
;
3224 /* for Display Name */
3225 if (sipe_strequal(property_name
, ALIAS_PROP
)) {
3226 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
3227 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, property_value
);
3228 purple_blist_alias_buddy(p_buddy
, property_value
);
3231 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3232 if (!is_empty(property_value
) &&
3233 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
3235 purple_blist_server_alias_buddy(p_buddy
, property_value
);
3238 /* for other properties */
3240 if (!is_empty(property_value
)) {
3241 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
3242 if (!prop_str
|| !sipe_strcase_equal(prop_str
, property_value
)) {
3243 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
3248 entry
= entry
->next
;
3250 g_slist_free(buddies
);
3255 * Suitable for both 2005 and 2007 systems.
3257 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3259 * @param phone may be modified to strip white space
3260 * @param phone_display_string may be modified to strip white space
3263 sipe_update_user_phone(struct sipe_account_data
*sip
,
3265 const gchar
*phone_type
,
3267 gchar
*phone_display_string
)
3269 const char *phone_node
= PHONE_PROP
; /* work phone by default */
3270 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
3272 if(!phone
|| strlen(phone
) == 0) return;
3274 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
3275 phone_node
= PHONE_MOBILE_PROP
;
3276 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
3277 } else if (sipe_strequal(phone_type
, "home")) {
3278 phone_node
= PHONE_HOME_PROP
;
3279 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
3280 } else if (sipe_strequal(phone_type
, "other")) {
3281 phone_node
= PHONE_OTHER_PROP
;
3282 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
3283 } else if (sipe_strequal(phone_type
, "custom1")) {
3284 phone_node
= PHONE_CUSTOM1_PROP
;
3285 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
3288 sipe_update_user_info(sip
, uri
, phone_node
, phone
);
3289 if (phone_display_string
) {
3290 sipe_update_user_info(sip
, uri
, phone_display_node
, phone_display_string
);
3295 sipe_update_calendar(struct sipe_account_data
*sip
)
3297 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
3299 purple_debug_info("sipe", "sipe_update_calendar: started.\n");
3301 if (sipe_strequal(calendar
, "EXCH")) {
3302 sipe_ews_update_calendar(sip
);
3305 /* schedule repeat */
3306 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_INTERVAL
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3308 purple_debug_info("sipe", "sipe_update_calendar: finished.\n");
3312 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
3313 * by using standard Purple's means of signals and saved statuses.
3315 * Thus all UI elements get updated: Status Button with Note, docklet.
3316 * This is ablolutely important as both our status and note can come
3317 * inbound (roaming) or be updated programmatically (e.g. based on our
3321 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
3322 const char *status_id
,
3323 const char *message
,
3324 time_t do_not_publish
[])
3326 PurpleStatus
*status
= purple_account_get_active_status(account
);
3327 gboolean changed
= TRUE
;
3329 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
3330 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
3335 if (purple_savedstatus_is_idleaway()) {
3340 PurpleSavedStatus
*saved_status
;
3341 const PurpleStatusType
*acct_status_type
=
3342 purple_status_type_find_with_id(account
->status_types
, status_id
);
3343 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
3344 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
3346 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
3348 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
3351 /* If this type+message is unique then create a new transient saved status
3352 * Ref: gtkstatusbox.c
3354 if (!saved_status
) {
3356 GList
*active_accts
= purple_accounts_get_all_active();
3358 saved_status
= purple_savedstatus_new(NULL
, primitive
);
3359 purple_savedstatus_set_message(saved_status
, message
);
3361 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
3362 purple_savedstatus_set_substatus(saved_status
,
3363 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
3365 g_list_free(active_accts
);
3368 do_not_publish
[activity
] = time(NULL
);
3369 purple_debug_info("sipe", "sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]\n",
3370 status_id
, (int)do_not_publish
[activity
]);
3372 /* Set the status for each account */
3373 purple_savedstatus_activate(saved_status
);
3377 struct hash_table_delete_payload
{
3378 GHashTable
*hash_table
;
3383 sipe_remove_category_container_publications_cb(const char *name
,
3384 struct sipe_publication
*publication
,
3385 struct hash_table_delete_payload
*payload
)
3387 if (publication
->container
== payload
->container
) {
3388 g_hash_table_remove(payload
->hash_table
, name
);
3392 sipe_remove_category_container_publications(GHashTable
*our_publications
,
3393 const char *category
,
3396 struct hash_table_delete_payload payload
;
3397 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
3399 if (!payload
.hash_table
) return;
3401 payload
.container
= container
;
3402 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
3406 send_publish_category_initial(struct sipe_account_data
*sip
);
3409 * When we receive some self (BE) NOTIFY with a new subscriber
3410 * we sends a setSubscribers request to him [SIP-PRES] 4.8
3413 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3420 char *display_name
= NULL
;
3422 GSList
*category_names
= NULL
;
3423 int aggreg_avail
= 0;
3424 static sipe_activity aggreg_activity
= SIPE_ACTIVITY_UNSET
;
3425 gboolean do_update_status
= FALSE
;
3426 gboolean has_note_cleaned
= FALSE
;
3428 purple_debug_info("sipe", "sipe_process_roaming_self\n");
3430 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3433 contact
= get_contact(sip
);
3434 to
= sip_uri_self(sip
);
3438 /* set list of categories participating in this XML */
3439 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3440 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3441 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
3443 purple_debug_info("sipe", "sipe_process_roaming_self: category_names length=%d\n",
3444 category_names
? (int) g_slist_length(category_names
) : -1);
3445 /* drop category information */
3446 if (category_names
) {
3447 GSList
*entry
= category_names
;
3449 GHashTable
*cat_publications
;
3450 const gchar
*category
= entry
->data
;
3451 entry
= entry
->next
;
3452 purple_debug_info("sipe", "sipe_process_roaming_self: dropping category: %s\n", category
);
3453 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
3454 if (cat_publications
) {
3455 g_hash_table_remove(sip
->our_publications
, category
);
3456 purple_debug_info("sipe", " sipe_process_roaming_self: dropped category: %s\n", category
);
3460 g_slist_free(category_names
);
3461 /* filling our categories reflected in roaming data */
3462 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3464 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3465 guint container
= xmlnode_get_int_attrib(node
, "container", -1);
3466 guint instance
= xmlnode_get_int_attrib(node
, "instance", -1);
3467 guint version
= xmlnode_get_int_attrib(node
, "version", 0);
3468 time_t publish_time
= (tmp
= xmlnode_get_attrib(node
, "publishTime")) ?
3469 sipe_utils_str_to_time(tmp
) : 0;
3471 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
3473 /* Ex. clear note: <category name="note"/> */
3474 if (container
== (guint
)-1) {
3477 do_update_status
= TRUE
;
3481 /* Ex. clear note: <category name="note" container="200"/> */
3482 if (instance
== (guint
)-1) {
3483 if (container
== 200) {
3486 do_update_status
= TRUE
;
3488 purple_debug_info("sipe", "sipe_process_roaming_self: removing publications for: %s/%u\n", name
, container
);
3489 sipe_remove_category_container_publications(
3490 sip
->our_publications
, name
, container
);
3494 /* key is <category><instance><container> */
3495 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
3496 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version
);
3498 /* capture all userState publication for later clean up if required */
3499 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
3500 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3502 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "userState")) {
3503 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3504 publication
->category
= g_strdup(name
);
3505 publication
->instance
= instance
;
3506 publication
->container
= container
;
3507 publication
->version
= version
;
3509 if (!sip
->user_state_publications
) {
3510 sip
->user_state_publications
= g_hash_table_new_full(
3511 g_str_hash
, g_str_equal
,
3512 g_free
, (GDestroyNotify
)free_publication
);
3514 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
3515 purple_debug_info("sipe", "sipe_process_roaming_self: added to user_state_publications key=%s version=%d\n",
3520 if (sipe_is_our_publication(sip
, key
)) {
3521 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3523 publication
->category
= g_strdup(name
);
3524 publication
->instance
= instance
;
3525 publication
->container
= container
;
3526 publication
->version
= version
;
3528 /* filling publication->availability */
3529 if (sipe_strequal(name
, "state")) {
3530 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3531 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3534 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3536 publication
->availability
= atoi(avail_str
);
3540 /* for calendarState */
3541 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "calendarState")) {
3542 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3543 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
3545 event
->start_time
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "startTime"));
3547 if (sipe_strequal(xmlnode_get_attrib(xn_activity
, "token"),
3548 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
3550 event
->is_meeting
= TRUE
;
3553 event
->subject
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingSubject"));
3554 event
->location
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingLocation"));
3556 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
3557 purple_debug_info("sipe", "sipe_process_roaming_self: hash=%s\n",
3558 publication
->cal_event_hash
);
3559 sipe_cal_event_free(event
);
3562 /* filling publication->note */
3563 if (sipe_strequal(name
, "note")) {
3564 xmlnode
*xn_body
= xmlnode_get_descendant(node
, "note", "body", NULL
);
3566 if (!has_note_cleaned
) {
3567 has_note_cleaned
= TRUE
;
3571 sip
->note_since
= publish_time
;
3573 do_update_status
= TRUE
;
3576 g_free(publication
->note
);
3577 publication
->note
= NULL
;
3581 publication
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_body
)), -1);
3583 if (publish_time
>= sip
->note_since
) {
3585 sip
->note
= g_strdup(publication
->note
);
3586 sip
->note_since
= publish_time
;
3587 sip
->is_oof_note
= sipe_strequal(xmlnode_get_attrib(xn_body
, "type"), "OOF");
3589 do_update_status
= TRUE
;
3594 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
3595 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
3596 xmlnode
*xn_free_busy
= xmlnode_get_descendant(node
, "calendarData", "freeBusy", NULL
);
3597 xmlnode
*xn_working_hours
= xmlnode_get_descendant(node
, "calendarData", "WorkingHours", NULL
);
3599 publication
->fb_start_str
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
3600 publication
->free_busy_base64
= xmlnode_get_data(xn_free_busy
);
3602 if (xn_working_hours
) {
3603 publication
->working_hours_xml_str
= xmlnode_to_str(xn_working_hours
, NULL
);
3607 if (!cat_publications
) {
3608 cat_publications
= g_hash_table_new_full(
3609 g_str_hash
, g_str_equal
,
3610 g_free
, (GDestroyNotify
)free_publication
);
3611 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
3612 purple_debug_info("sipe", "sipe_process_roaming_self: added GHashTable cat=%s\n", name
);
3614 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
3615 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version
);
3619 /* aggregateState (not an our publication) from 2-nd container */
3620 if (sipe_strequal(name
, "state") && container
== 2) {
3621 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3623 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "aggregateState")) {
3624 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3625 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3628 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3630 aggreg_avail
= atoi(avail_str
);
3636 const char *activity_token
= xmlnode_get_attrib(xn_activity
, "token");
3638 aggreg_activity
= sipe_get_activity_by_token(activity_token
);
3641 do_update_status
= TRUE
;
3645 /* userProperties published by server from AD */
3646 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
3648 /* line, for Remote Call Control (RCC) */
3649 for (line
= xmlnode_get_descendant(node
, "userProperties", "lines", "line", NULL
); line
; line
= xmlnode_get_next_twin(line
)) {
3650 const gchar
*line_server
= xmlnode_get_attrib(line
, "lineServer");
3651 const gchar
*line_type
= xmlnode_get_attrib(line
, "lineType");
3654 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
3656 line_uri
= xmlnode_get_data(line
);
3658 purple_debug_info("sipe", "sipe_process_roaming_self: line_uri=%s server=%s\n", line_uri
, line_server
);
3659 sip_csta_open(sip
, line_uri
, line_server
);
3667 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
3668 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
3671 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3672 guint id
= xmlnode_get_int_attrib(node
, "id", 0);
3673 struct sipe_container
*container
= sipe_find_container(sip
, id
);
3676 sip
->containers
= g_slist_remove(sip
->containers
, container
);
3677 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
3678 free_container(container
);
3680 container
= g_new0(struct sipe_container
, 1);
3682 container
->version
= xmlnode_get_int_attrib(node
, "version", 0);
3683 sip
->containers
= g_slist_append(sip
->containers
, container
);
3684 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
3686 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
3687 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
3688 member
->type
= xmlnode_get_attrib(node2
, "type");
3689 member
->value
= xmlnode_get_attrib(node2
, "value");
3690 container
->members
= g_slist_append(container
->members
, member
);
3691 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
3692 member
->type
, member
->value
? member
->value
: "");
3696 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
3697 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
3698 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
3699 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
3700 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
3701 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
3702 /* initial set-up to let counterparties see your status */
3703 if (sameEnterpriseAL
< 0) {
3704 struct sipe_container
*container
= sipe_find_container(sip
, 200);
3705 guint version
= container
? container
->version
: 0;
3706 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
3708 if (federatedAL
< 0) {
3709 struct sipe_container
*container
= sipe_find_container(sip
, 100);
3710 guint version
= container
? container
->version
: 0;
3711 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
3713 sip
->access_level_set
= TRUE
;
3717 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3719 const char *acknowledged
;
3723 user
= xmlnode_get_attrib(node
, "user"); /* without 'sip:' prefix */
3724 if (!user
) continue;
3725 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
3726 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
3727 uri
= sip_uri_from_name(user
);
3729 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
3731 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
3732 if(sipe_strcase_equal(acknowledged
,"false")){
3733 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
3734 if (!purple_find_buddy(sip
->account
, uri
)) {
3735 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
3738 hdr
= g_strdup_printf(
3740 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
3742 body
= g_strdup_printf(
3743 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
3744 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
3745 "</setSubscribers>", user
);
3747 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
3751 g_free(display_name
);
3758 /* Publish initial state if not yet.
3759 * Assuming this happens on initial responce to subscription to roaming-self
3760 * so we've already updated our roaming data in full.
3763 if (!sip
->initial_state_published
) {
3764 send_publish_category_initial(sip
);
3765 sip
->initial_state_published
= TRUE
;
3767 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3768 do_update_status
= FALSE
;
3769 } else if (aggreg_avail
) {
3771 g_free(sip
->status
);
3772 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
3773 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
3775 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
3779 if (do_update_status
) {
3780 purple_debug_info("sipe", "sipe_process_roaming_self: switch to '%s' for the account\n", sip
->status
);
3781 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
3787 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
3789 gchar
*to
= sip_uri_self(sip
);
3790 gchar
*tmp
= get_contact(sip
);
3791 gchar
*hdr
= g_strdup_printf(
3792 "Event: vnd-microsoft-roaming-ACL\r\n"
3793 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
3794 "Supported: com.microsoft.autoextend\r\n"
3795 "Supported: ms-benotify\r\n"
3796 "Proxy-Require: ms-benotify\r\n"
3797 "Supported: ms-piggyback-first-notify\r\n"
3798 "Contact: %s\r\n", tmp
);
3801 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
3807 * To request for presence information about the user, access level settings that have already been configured by the user
3808 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
3809 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
3812 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
3814 gchar
*to
= sip_uri_self(sip
);
3815 gchar
*tmp
= get_contact(sip
);
3816 gchar
*hdr
= g_strdup_printf(
3817 "Event: vnd-microsoft-roaming-self\r\n"
3818 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
3819 "Supported: ms-benotify\r\n"
3820 "Proxy-Require: ms-benotify\r\n"
3821 "Supported: ms-piggyback-first-notify\r\n"
3823 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
3825 gchar
*body
=g_strdup(
3826 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
3827 "<roaming type=\"categories\"/>"
3828 "<roaming type=\"containers\"/>"
3829 "<roaming type=\"subscribers\"/></roamingList>");
3832 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3841 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
3843 gchar
*to
= sip_uri_self(sip
);
3844 gchar
*tmp
= get_contact(sip
);
3845 gchar
*hdr
= g_strdup_printf(
3846 "Event: vnd-microsoft-provisioning\r\n"
3847 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
3848 "Supported: com.microsoft.autoextend\r\n"
3849 "Supported: ms-benotify\r\n"
3850 "Proxy-Require: ms-benotify\r\n"
3851 "Supported: ms-piggyback-first-notify\r\n"
3853 "Contact: %s\r\n", tmp
);
3856 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
3861 /** Subscription for provisioning information to help with initial
3862 * configuration. This subscription is a one-time query (denoted by the Expires header,
3863 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
3864 * configuration, meeting policies, and policy settings that Communicator must enforce.
3865 * TODO: for what we need this information.
3868 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
3870 gchar
*to
= sip_uri_self(sip
);
3871 gchar
*tmp
= get_contact(sip
);
3872 gchar
*hdr
= g_strdup_printf(
3873 "Event: vnd-microsoft-provisioning-v2\r\n"
3874 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
3875 "Supported: com.microsoft.autoextend\r\n"
3876 "Supported: ms-benotify\r\n"
3877 "Proxy-Require: ms-benotify\r\n"
3878 "Supported: ms-piggyback-first-notify\r\n"
3881 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
3882 gchar
*body
= g_strdup(
3883 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
3884 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
3885 "<provisioningGroup name=\"ucPolicy\"/>"
3886 "</provisioningGroupList>");
3889 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3896 sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
3897 gpointer value
, gpointer user_data
)
3899 struct sip_subscription
*subscription
= value
;
3900 struct sip_dialog
*dialog
= &subscription
->dialog
;
3901 struct sipe_account_data
*sip
= user_data
;
3902 gchar
*tmp
= get_contact(sip
);
3903 gchar
*hdr
= g_strdup_printf(
3906 "Contact: %s\r\n", subscription
->event
, tmp
);
3909 /* Rate limit to max. 25 requests per seconds */
3910 g_usleep(1000000 / 25);
3912 send_sip_request(sip
->gc
, "SUBSCRIBE", dialog
->with
, dialog
->with
, hdr
, NULL
, dialog
, NULL
);
3916 /* IM Session (INVITE and MESSAGE methods) */
3918 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
3920 get_end_points (struct sipe_account_data
*sip
,
3921 struct sip_session
*session
)
3925 if (session
== NULL
) {
3929 res
= g_strdup_printf("<sip:%s>", sip
->username
);
3931 SIPE_DIALOG_FOREACH
{
3933 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
3936 if (dialog
->theirepid
) {
3938 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
3941 } SIPE_DIALOG_FOREACH_END
;
3947 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
3949 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3951 gboolean ret
= TRUE
;
3953 if (msg
->response
!= 200) {
3954 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
3958 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
3964 * Asks UA/proxy about its capabilities.
3966 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
3968 gchar
*to
= sip_uri(who
);
3969 gchar
*contact
= get_contact(sip
);
3970 gchar
*request
= g_strdup_printf(
3971 "Accept: application/sdp\r\n"
3972 "Contact: %s\r\n", contact
);
3975 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
3982 sipe_notify_user(struct sipe_account_data
*sip
,
3983 struct sip_session
*session
,
3984 PurpleMessageFlags flags
,
3985 const gchar
*message
)
3987 PurpleConversation
*conv
;
3989 if (!session
->conv
) {
3990 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
3992 conv
= session
->conv
;
3994 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
3998 sipe_present_info(struct sipe_account_data
*sip
,
3999 struct sip_session
*session
,
4000 const gchar
*message
)
4002 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
4006 sipe_present_err(struct sipe_account_data
*sip
,
4007 struct sip_session
*session
,
4008 const gchar
*message
)
4010 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
4014 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
4015 struct sip_session
*session
,
4019 const gchar
*message
)
4021 char *msg
, *msg_tmp
, *msg_tmp2
;
4024 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
4025 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
4027 /* Service unavailable; Server Internal Error; Server Time-out */
4028 if (sip_error
== 606 && sip_warning
== 309) { /* Not acceptable all. */ /* Message contents not allowed by policy */
4029 label
= _("Your message or invitation was not delivered, possibly because it contains a hyperlink or other content that the system administrator has blocked.");
4032 } else if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
4033 label
= _("This message was not delivered to %s because the service is not available");
4034 } else if (sip_error
== 486) { /* Busy Here */
4035 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
4036 } else if (sip_error
== 415) { /* Unsupported media type */
4037 label
= _("This message was not delivered to %s because one or more recipients don't support this type of message");
4039 label
= _("This message was not delivered to %s because one or more recipients are offline");
4042 msg_tmp
= g_strdup_printf( "%s%s\n%s" ,
4043 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""),
4046 sipe_present_err(sip
, session
, msg_tmp
);
4054 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4055 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4057 gboolean ret
= TRUE
;
4058 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4059 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
4060 struct sip_dialog
*dialog
;
4063 struct queued_message
*message
;
4066 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
4071 dialog
= sipe_dialog_find(session
, with
);
4073 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
4078 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4079 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
4081 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4083 if (msg
->response
>= 400) {
4084 PurpleBuddy
*pbuddy
;
4085 const char *alias
= with
;
4086 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
4089 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
4092 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
4094 warning
= atoi(parts
[0]);
4099 /* cancel file transfer as rejected by server */
4100 if (msg
->response
== 606 && /* Not acceptable all. */
4101 warning
== 309 && /* Message contents not allowed by policy */
4102 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
4104 GSList
*parsed_body
= sipe_ft_parse_msg_body(msg
->body
);
4105 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4106 sipe_utils_nameval_free(parsed_body
);
4109 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4110 alias
= purple_buddy_get_alias(pbuddy
);
4113 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, warning
, alias
, (message
? message
->body
: NULL
));
4115 /* drop dangling IM sessions: assume that BYE from remote never reached us */
4116 if (msg
->response
== 408 || /* Request timeout */
4117 msg
->response
== 480 || /* Temporarily Unavailable */
4118 msg
->response
== 481) { /* Call/Transaction Does Not Exist */
4119 purple_debug_info("sipe", "process_message_response: assuming dangling IM session, dropping it.\n");
4120 send_sip_request(sip
->gc
, "BYE", with
, with
, NULL
, NULL
, dialog
, NULL
);
4125 const gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
4127 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
->body
));
4128 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
4129 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
4132 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4133 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
4134 key
, g_hash_table_size(session
->unconfirmed_messages
));
4140 if (ret
) sipe_im_process_queue(sip
, session
);
4145 sipe_is_election_finished(struct sip_session
*session
);
4148 sipe_election_result(struct sipe_account_data
*sip
,
4152 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4153 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4155 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4156 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4157 struct sip_dialog
*dialog
;
4158 struct sip_session
*session
;
4160 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4162 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
4166 if (msg
->response
== 200 && g_str_has_prefix(contenttype
, "application/x-ms-mim")) {
4167 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4168 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
4169 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
4171 if (xn_request_rm_response
) {
4172 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
4173 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
4175 dialog
= sipe_dialog_find(session
, with
);
4177 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
4178 xmlnode_free(xn_action
);
4182 if (allow
&& !g_strcasecmp(allow
, "true")) {
4183 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
4184 dialog
->election_vote
= 1;
4185 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
4186 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
4187 dialog
->election_vote
= -1;
4190 if (sipe_is_election_finished(session
)) {
4191 sipe_election_result(sip
, session
);
4194 } else if (xn_set_rm_response
) {
4197 xmlnode_free(xn_action
);
4204 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
, const char *content_type
)
4208 char *msgtext
= NULL
;
4209 const gchar
*msgr
= "";
4212 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
4216 sipe_parse_html(msg
, &msgformat
, &msgtext
);
4217 purple_debug_info("sipe", "sipe_send_message: msgformat=%s\n", msgformat
);
4219 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4222 msgr
= tmp2
= g_strdup_printf(";msgr=%s", msgr_value
);
4226 msgtext
= g_strdup(msg
);
4229 tmp
= get_contact(sip
);
4230 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
4231 //hdr = g_strdup("Content-Type: text/rtf\r\n");
4232 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
4233 if (content_type
== NULL
)
4234 content_type
= "text/plain";
4236 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
4240 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
4247 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
4249 GSList
*entry2
= session
->outgoing_message_queue
;
4251 struct queued_message
*msg
= entry2
->data
;
4253 /* for multiparty chat or conference */
4254 if (session
->is_multiparty
|| session
->focus_uri
) {
4255 gchar
*who
= sip_uri_self(sip
);
4256 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
4257 PURPLE_MESSAGE_SEND
, msg
->body
, time(NULL
));
4261 SIPE_DIALOG_FOREACH
{
4263 struct queued_message
*message
;
4265 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
4267 message
= g_new0(struct queued_message
,1);
4268 message
->body
= g_strdup(msg
->body
);
4269 if (msg
->content_type
!= NULL
)
4270 message
->content_type
= g_strdup(msg
->content_type
);
4272 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
4273 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
4274 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
4275 key
, g_hash_table_size(session
->unconfirmed_messages
));
4278 sipe_send_message(sip
, dialog
, msg
->body
, msg
->content_type
);
4279 } SIPE_DIALOG_FOREACH_END
;
4281 entry2
= sipe_session_dequeue_message(session
);
4286 sipe_refer_notify(struct sipe_account_data
*sip
,
4287 struct sip_session
*session
,
4294 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4296 hdr
= g_strdup_printf(
4298 "Subscription-State: %s\r\n"
4299 "Content-Type: message/sipfrag\r\n",
4300 status
>= 200 ? "terminated" : "active");
4302 body
= g_strdup_printf(
4303 "SIP/2.0 %d %s\r\n",
4306 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
4313 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
4315 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4316 struct sip_session
*session
;
4317 struct sip_dialog
*dialog
;
4320 struct queued_message
*message
;
4321 struct sipmsg
*request_msg
= trans
->msg
;
4323 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4326 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4328 session
= sipe_session_find_im(sip
, with
);
4331 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
4336 dialog
= sipe_dialog_find(session
, with
);
4338 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
4343 sipe_dialog_parse(dialog
, msg
, TRUE
);
4345 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4346 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
4348 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4350 if (msg
->response
!= 200) {
4351 PurpleBuddy
*pbuddy
;
4352 const char *alias
= with
;
4353 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
4356 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
4359 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
4361 warning
= atoi(parts
[0]);
4366 /* cancel file transfer as rejected by server */
4367 if (msg
->response
== 606 && /* Not acceptable all. */
4368 warning
== 309 && /* Message contents not allowed by policy */
4369 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
4371 GSList
*parsed_body
= sipe_ft_parse_msg_body(message
->body
);
4372 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4373 sipe_utils_nameval_free(parsed_body
);
4376 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4377 alias
= purple_buddy_get_alias(pbuddy
);
4381 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, warning
, alias
, message
->body
);
4383 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
4384 sipe_present_err(sip
, session
, tmp_msg
);
4388 sipe_dialog_remove(session
, with
);
4396 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4397 dialog
->outgoing_invite
= NULL
;
4398 dialog
->is_established
= TRUE
;
4400 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
4402 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
4403 g_free(referred_by
);
4406 /* add user to chat if it is a multiparty session */
4407 if (session
->is_multiparty
) {
4408 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4410 PURPLE_CBFLAGS_NONE
, TRUE
);
4413 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
4414 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
4415 sipe_session_dequeue_message(session
);
4418 sipe_im_process_queue(sip
, session
);
4420 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4421 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
4422 key
, g_hash_table_size(session
->unconfirmed_messages
));
4431 sipe_invite(struct sipe_account_data
*sip
,
4432 struct sip_session
*session
,
4434 const gchar
*msg_body
,
4435 const gchar
*msg_content_type
,
4436 const gchar
*referred_by
,
4437 const gboolean is_triggered
)
4444 char *ms_text_format
= NULL
;
4445 gchar
*roster_manager
;
4447 gchar
*referred_by_str
;
4448 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4450 if (dialog
&& dialog
->is_established
) {
4451 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
4456 dialog
= sipe_dialog_add(session
);
4457 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
4458 dialog
->with
= g_strdup(who
);
4461 if (!(dialog
->ourtag
)) {
4462 dialog
->ourtag
= gentag();
4468 char *msgtext
= NULL
;
4470 const gchar
*msgr
= "";
4472 struct queued_message
*message
;
4475 if (!g_str_has_prefix(msg_content_type
, "text/x-msmsgsinvite")) {
4479 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
4480 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
4482 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4485 msgr
= tmp
= g_strdup_printf(";msgr=%s", msgr_value
);
4489 msgtext
= g_strdup(msg_body
);
4492 base64_msg
= g_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
4493 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
,
4494 msg_content_type
? msg_content_type
: "text/plain",
4501 message
= g_new0(struct queued_message
,1);
4502 message
->body
= g_strdup(msg_body
);
4503 if (msg_content_type
!= NULL
)
4504 message
->content_type
= g_strdup(msg_content_type
);
4506 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
4507 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
4508 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
4509 key
, g_hash_table_size(session
->unconfirmed_messages
));
4513 contact
= get_contact(sip
);
4514 end_points
= get_end_points(sip
, session
);
4515 self
= sip_uri_self(sip
);
4516 roster_manager
= g_strdup_printf(
4517 "Roster-Manager: %s\r\n"
4518 "EndPoints: %s\r\n",
4521 referred_by_str
= referred_by
?
4523 "Referred-By: %s\r\n",
4526 hdr
= g_strdup_printf(
4527 "Supported: ms-sender\r\n"
4533 "Content-Type: application/sdp\r\n",
4534 sipe_strcase_equal(session
->roster_manager
, self
) ? roster_manager
: "",
4536 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
4537 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
4539 ms_text_format
? ms_text_format
: "");
4540 g_free(ms_text_format
);
4543 body
= g_strdup_printf(
4545 "o=- 0 0 IN IP4 %s\r\n"
4549 "m=%s %d sip null\r\n"
4550 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
4551 purple_network_get_my_ip(-1),
4552 purple_network_get_my_ip(-1),
4553 sip
->ocs2007
? "message" : "x-ms-message",
4556 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
4557 to
, to
, hdr
, body
, dialog
, process_invite_response
);
4560 g_free(roster_manager
);
4562 g_free(referred_by_str
);
4569 sipe_refer(struct sipe_account_data
*sip
,
4570 struct sip_session
*session
,
4575 gchar
*epid
= get_epid(sip
);
4576 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
4577 session
->roster_manager
);
4578 const char *ourtag
= dialog
&& dialog
->ourtag
? dialog
->ourtag
: NULL
;
4580 contact
= get_contact(sip
);
4581 hdr
= g_strdup_printf(
4583 "Refer-to: <%s>\r\n"
4584 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
4585 "Require: com.microsoft.rtc-multiparty\r\n",
4589 ourtag
? ";tag=" : "",
4590 ourtag
? ourtag
: "",
4594 send_sip_request(sip
->gc
, "REFER",
4595 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
4602 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
4603 struct sip_dialog
*dialog
,
4606 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4608 gchar
*body
= g_strdup_printf(
4609 "<?xml version=\"1.0\"?>\r\n"
4610 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4611 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
4612 sip
->username
, bid
);
4614 send_sip_request(sip
->gc
, "INFO",
4615 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4621 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
4622 struct sip_dialog
*dialog
)
4624 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4626 gchar
*body
= g_strdup_printf(
4627 "<?xml version=\"1.0\"?>\r\n"
4628 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4629 "<SetRM uri=\"sip:%s\"/></action>\r\n",
4632 send_sip_request(sip
->gc
, "INFO",
4633 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4639 sipe_session_close(struct sipe_account_data
*sip
,
4640 struct sip_session
* session
)
4642 if (session
&& session
->focus_uri
) {
4643 sipe_conf_immcu_closed(sip
, session
);
4644 conf_session_close(sip
, session
);
4648 SIPE_DIALOG_FOREACH
{
4649 /* @TODO slow down BYE message sending rate */
4650 /* @see single subscription code */
4651 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4652 } SIPE_DIALOG_FOREACH_END
;
4654 sipe_session_remove(sip
, session
);
4659 sipe_session_close_all(struct sipe_account_data
*sip
)
4662 while ((entry
= sip
->sessions
) != NULL
) {
4663 sipe_session_close(sip
, entry
->data
);
4668 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
4670 struct sipe_account_data
*sip
= gc
->proto_data
;
4672 purple_debug_info("sipe", "conversation with %s closed\n", who
);
4673 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
4677 sipe_chat_invite(PurpleConnection
*gc
, int id
,
4678 SIPE_UNUSED_PARAMETER
const char *message
,
4681 sipe_chat_create(gc
->proto_data
, id
, name
);
4685 sipe_chat_leave (PurpleConnection
*gc
, int id
)
4687 struct sipe_account_data
*sip
= gc
->proto_data
;
4688 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
4690 sipe_session_close(sip
, session
);
4693 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
4694 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4696 struct sipe_account_data
*sip
= gc
->proto_data
;
4697 struct sip_session
*session
;
4698 struct sip_dialog
*dialog
;
4699 gchar
*uri
= sip_uri(who
);
4701 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
4703 session
= sipe_session_find_or_add_im(sip
, uri
);
4704 dialog
= sipe_dialog_find(session
, uri
);
4706 // Queue the message
4707 sipe_session_enqueue_message(session
, what
, NULL
);
4709 if (dialog
&& !dialog
->outgoing_invite
) {
4710 sipe_im_process_queue(sip
, session
);
4711 } else if (!dialog
|| !dialog
->outgoing_invite
) {
4712 // Need to send the INVITE to get the outgoing dialog setup
4713 sipe_invite(sip
, session
, uri
, what
, NULL
, NULL
, FALSE
);
4720 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
4721 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4723 struct sipe_account_data
*sip
= gc
->proto_data
;
4724 struct sip_session
*session
;
4726 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
4728 session
= sipe_session_find_chat_by_id(sip
, id
);
4730 // Queue the message
4731 if (session
&& session
->dialogs
) {
4732 sipe_session_enqueue_message(session
,what
,NULL
);
4733 sipe_im_process_queue(sip
, session
);
4735 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
4736 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
4738 purple_debug_info("sipe", "sipe_chat_send: chat_name='%s'\n", chat_name
? chat_name
: "NULL");
4739 purple_debug_info("sipe", "sipe_chat_send: proto_chat_id='%s'\n", proto_chat_id
? proto_chat_id
: "NULL");
4742 struct sip_session
*session
= sipe_session_add_chat(sip
);
4744 session
->is_multiparty
= FALSE
;
4745 session
->focus_uri
= g_strdup(proto_chat_id
);
4746 sipe_session_enqueue_message(session
, what
, NULL
);
4747 sipe_invite_conf_focus(sip
, session
);
4754 /* End IM Session (INVITE and MESSAGE methods) */
4756 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4758 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4759 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4761 struct sip_session
*session
;
4763 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
4765 /* Call Control protocol */
4766 if (g_str_has_prefix(contenttype
, "application/csta+xml"))
4768 process_incoming_info_csta(sip
, msg
);
4772 from
= parse_from(sipmsg_find_header(msg
, "From"));
4773 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4775 session
= sipe_session_find_im(sip
, from
);
4782 if (g_str_has_prefix(contenttype
, "application/x-ms-mim"))
4784 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4785 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
4786 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
4788 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
4790 if (xn_request_rm
) {
4791 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
4792 int bid
= xmlnode_get_int_attrib(xn_request_rm
, "bid", 0);
4793 gchar
*body
= g_strdup_printf(
4794 "<?xml version=\"1.0\"?>\r\n"
4795 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4796 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
4798 session
->bid
< bid
? "true" : "false");
4799 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4801 } else if (xn_set_rm
) {
4803 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
4804 g_free(session
->roster_manager
);
4805 session
->roster_manager
= g_strdup(rm
);
4807 body
= g_strdup_printf(
4808 "<?xml version=\"1.0\"?>\r\n"
4809 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4810 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
4812 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4815 xmlnode_free(xn_action
);
4820 /* looks like purple lacks typing notification for chat */
4821 if (!session
->is_multiparty
&& !session
->focus_uri
) {
4822 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4823 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
4825 if (sipe_strequal(status
, "type")) {
4826 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4827 } else if (sipe_strequal(status
, "idle")) {
4828 serv_got_typing_stopped(sip
->gc
, from
);
4830 xmlnode_free(xn_keyboard_activity
);
4833 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4838 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4840 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4841 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4842 struct sip_session
*session
;
4843 struct sip_dialog
*dialog
;
4845 /* collect dialog identification
4846 * we need callid, ourtag and theirtag to unambiguously identify dialog
4848 /* take data before 'msg' will be modified by send_sip_response */
4849 dialog
= g_new0(struct sip_dialog
, 1);
4850 dialog
->callid
= g_strdup(callid
);
4851 dialog
->cseq
= parse_cseq(sipmsg_find_header(msg
, "CSeq"));
4852 dialog
->with
= g_strdup(from
);
4853 sipe_dialog_parse(dialog
, msg
, FALSE
);
4855 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4857 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4859 session
= sipe_session_find_im(sip
, from
);
4862 sipe_dialog_free(dialog
);
4867 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
4868 g_free(session
->roster_manager
);
4869 session
->roster_manager
= NULL
;
4872 /* This what BYE is essentially for - terminating dialog */
4873 sipe_dialog_remove_3(session
, dialog
);
4874 sipe_dialog_free(dialog
);
4875 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
4876 sipe_conf_immcu_closed(sip
, session
);
4877 } else if (session
->is_multiparty
) {
4878 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
4884 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4886 gchar
*self
= sip_uri_self(sip
);
4887 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4888 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4889 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
4890 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
4891 struct sip_session
*session
;
4892 struct sip_dialog
*dialog
;
4894 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4895 dialog
= sipe_dialog_find(session
, from
);
4897 if (!session
|| !dialog
|| !session
->roster_manager
|| !sipe_strcase_equal(session
->roster_manager
, self
)) {
4898 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
4900 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
4902 sipe_invite(sip
, session
, refer_to
, NULL
, NULL
, referred_by
, FALSE
);
4908 g_free(referred_by
);
4912 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
4914 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
4915 struct sip_session
*session
;
4916 struct sip_dialog
*dialog
;
4918 if (state
== PURPLE_NOT_TYPING
)
4921 session
= sipe_session_find_im(sip
, who
);
4922 dialog
= sipe_dialog_find(session
, who
);
4924 if (session
&& dialog
&& dialog
->is_established
) {
4925 send_sip_request(gc
, "INFO", who
, who
,
4926 "Content-Type: application/xml\r\n",
4927 SIPE_SEND_TYPING
, dialog
, NULL
);
4929 return SIPE_TYPING_SEND_TIMEOUT
;
4932 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
4934 GSList
*tmp
= sip
->transactions
;
4935 time_t currtime
= time(NULL
);
4937 struct transaction
*trans
= tmp
->data
;
4939 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
4940 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
4943 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
4945 sendout_sipmsg(sip
, trans
->msg
);
4952 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
4953 SIPE_UNUSED_PARAMETER
void *unused
)
4955 /* register again when security token expires */
4956 /* we have to start a new authentication as the security token
4957 * is almost expired by sending a not signed REGISTER message */
4958 purple_debug_info("sipe", "do a full reauthentication\n");
4959 sipe_auth_free(&sip
->registrar
);
4960 sipe_auth_free(&sip
->proxy
);
4961 sip
->registerstatus
= 0;
4963 sip
->reauthenticate_set
= FALSE
;
4967 sipe_process_incoming_x_msmsgsinvite(struct sipe_account_data
*sip
,
4969 GSList
*parsed_body
)
4971 gboolean found
= FALSE
;
4974 const gchar
*invitation_command
= sipe_utils_nameval_find(parsed_body
, "Invitation-Command");
4976 if (sipe_strequal(invitation_command
, "INVITE")) {
4977 sipe_ft_incoming_transfer(sip
->gc
->account
, msg
, parsed_body
);
4979 } else if (sipe_strequal(invitation_command
, "CANCEL")) {
4980 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4982 } else if (sipe_strequal(invitation_command
, "ACCEPT")) {
4983 sipe_ft_incoming_accept(sip
->gc
->account
, parsed_body
);
4990 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4993 const gchar
*contenttype
;
4994 gboolean found
= FALSE
;
4996 from
= parse_from(sipmsg_find_header(msg
, "From"));
5000 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
5002 contenttype
= sipmsg_find_header(msg
, "Content-Type");
5003 if (g_str_has_prefix(contenttype
, "text/plain")
5004 || g_str_has_prefix(contenttype
, "text/html")
5005 || g_str_has_prefix(contenttype
, "multipart/related")
5006 || g_str_has_prefix(contenttype
, "multipart/alternative"))
5008 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5009 gchar
*html
= get_html_message(contenttype
, msg
->body
);
5011 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
5013 session
= sipe_session_find_im(sip
, from
);
5016 if (session
&& session
->focus_uri
) { /* a conference */
5017 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
5018 gchar
*sender
= parse_from(tmp
);
5020 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
5021 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5023 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
5024 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5025 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5027 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5030 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5033 } else if (g_str_has_prefix(contenttype
, "application/im-iscomposing+xml")) {
5034 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5039 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
5044 state
= xmlnode_get_child(isc
, "state");
5047 purple_debug_info("sipe", "process_incoming_message: no state found\n");
5053 statedata
= xmlnode_get_data(state
);
5055 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
5056 else serv_got_typing_stopped(sip
->gc
, from
);
5061 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5063 } else if (g_str_has_prefix(contenttype
, "text/x-msmsgsinvite")) {
5064 GSList
*body
= sipe_ft_parse_msg_body(msg
->body
);
5065 found
= sipe_process_incoming_x_msmsgsinvite(sip
, msg
, body
);
5066 sipe_utils_nameval_free(body
);
5068 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5072 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5073 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
5075 session
= sipe_session_find_im(sip
, from
);
5078 gchar
*errmsg
= g_strdup_printf(_("Received a message with unrecognized contents from %s"),
5080 sipe_present_err(sip
, session
, errmsg
);
5084 purple_debug_info("sipe", "got unknown mime-type '%s'\n", contenttype
);
5085 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
5090 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5094 const gchar
*oldHeader
;
5096 gboolean is_multiparty
= FALSE
;
5097 gboolean is_triggered
= FALSE
;
5098 gboolean was_multiparty
= TRUE
;
5099 gboolean just_joined
= FALSE
;
5101 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5102 const gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
5103 const gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
5104 const gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
5105 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
5106 GSList
*end_points
= NULL
;
5108 struct sip_session
*session
;
5109 const gchar
*ms_text_format
;
5111 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? tmp
= fix_newlines(msg
->body
) : "");
5114 /* Invitation to join conference */
5115 if (g_str_has_prefix(content_type
, "application/ms-conf-invite+xml")) {
5116 process_incoming_invite_conf(sip
, msg
);
5120 /* Only accept text invitations */
5121 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
5122 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
5126 // TODO There *must* be a better way to clean up the To header to add a tag...
5127 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
5128 oldHeader
= sipmsg_find_header(msg
, "To");
5130 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
5131 sipmsg_remove_header_now(msg
, "To");
5132 sipmsg_add_header_now(msg
, "To", newHeader
);
5135 if (end_points_hdr
) {
5136 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
5138 if (g_slist_length(end_points
) > 2) {
5139 is_multiparty
= TRUE
;
5142 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
5143 is_triggered
= TRUE
;
5144 is_multiparty
= TRUE
;
5147 session
= sipe_session_find_chat_by_callid(sip
, callid
);
5148 /* Convert to multiparty */
5149 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
5150 g_free(session
->with
);
5151 session
->with
= NULL
;
5152 was_multiparty
= FALSE
;
5153 session
->is_multiparty
= TRUE
;
5154 session
->chat_id
= rand();
5157 if (!session
&& is_multiparty
) {
5158 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
5161 from
= parse_from(sipmsg_find_header(msg
, "From"));
5163 session
= sipe_session_find_or_add_im(sip
, from
);
5167 g_free(session
->callid
);
5168 session
->callid
= g_strdup(callid
);
5170 session
->is_multiparty
= is_multiparty
;
5171 if (roster_manager
) {
5172 session
->roster_manager
= g_strdup(roster_manager
);
5176 if (is_multiparty
&& end_points
) {
5177 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
5178 GSList
*entry
= end_points
;
5180 struct sip_dialog
*dialog
;
5181 struct sipendpoint
*end_point
= entry
->data
;
5182 entry
= entry
->next
;
5184 if (!g_strcasecmp(from
, end_point
->contact
) ||
5185 !g_strcasecmp(to
, end_point
->contact
))
5188 dialog
= sipe_dialog_find(session
, end_point
->contact
);
5190 g_free(dialog
->theirepid
);
5191 dialog
->theirepid
= end_point
->epid
;
5192 end_point
->epid
= NULL
;
5194 dialog
= sipe_dialog_add(session
);
5196 dialog
->callid
= g_strdup(session
->callid
);
5197 dialog
->with
= end_point
->contact
;
5198 end_point
->contact
= NULL
;
5199 dialog
->theirepid
= end_point
->epid
;
5200 end_point
->epid
= NULL
;
5204 /* send triggered INVITE */
5205 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, NULL
, TRUE
);
5212 GSList
*entry
= end_points
;
5214 struct sipendpoint
*end_point
= entry
->data
;
5215 entry
= entry
->next
;
5216 g_free(end_point
->contact
);
5217 g_free(end_point
->epid
);
5220 g_slist_free(end_points
);
5224 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
5226 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
5227 sipe_dialog_parse_routes(dialog
, msg
, FALSE
);
5229 dialog
= sipe_dialog_add(session
);
5231 dialog
->callid
= g_strdup(session
->callid
);
5232 dialog
->with
= g_strdup(from
);
5233 sipe_dialog_parse(dialog
, msg
, FALSE
);
5235 if (!dialog
->ourtag
) {
5236 dialog
->ourtag
= newTag
;
5243 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
5247 if (is_multiparty
&& !session
->conv
) {
5248 gchar
*chat_title
= sipe_chat_get_name(callid
);
5249 gchar
*self
= sip_uri_self(sip
);
5250 /* create prpl chat */
5251 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_title
);
5252 session
->chat_title
= g_strdup(chat_title
);
5253 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
5255 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5257 PURPLE_CBFLAGS_NONE
, FALSE
);
5262 if (is_multiparty
&& !was_multiparty
) {
5263 /* add current IM counterparty to chat */
5264 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5265 sipe_dialog_first(session
)->with
, NULL
,
5266 PURPLE_CBFLAGS_NONE
, FALSE
);
5269 /* add inviting party to chat */
5270 if (just_joined
&& session
->conv
) {
5271 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5273 PURPLE_CBFLAGS_NONE
, TRUE
);
5276 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
5278 /* This used only in 2005 official client, not 2007 or Reuters.
5279 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
5280 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
5282 /* also enabled for 2005 file transfer. Didn't work otherwise. */
5283 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
5284 if (is_multiparty
||
5285 (ms_text_format
&& g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite")) )
5287 if (ms_text_format
) {
5288 if (g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite"))
5290 gchar
*tmp
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
5293 gchar
*body
= (gchar
*) g_base64_decode(tmp
, &len
);
5295 GSList
*parsed_body
= sipe_ft_parse_msg_body(body
);
5297 sipe_process_incoming_x_msmsgsinvite(sip
, msg
, parsed_body
);
5298 sipe_utils_nameval_free(parsed_body
);
5299 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5303 else if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html"))
5305 /* please do not optimize logic inside as this code may be re-enabled for other cases */
5306 gchar
*html
= get_html_message(ms_text_format
, NULL
);
5308 if (is_multiparty
) {
5309 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5310 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5312 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5315 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5323 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
5324 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5325 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5327 body
= g_strdup_printf(
5329 "o=- 0 0 IN IP4 %s\r\n"
5333 "m=%s %d sip sip:%s\r\n"
5334 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5335 purple_network_get_my_ip(-1),
5336 purple_network_get_my_ip(-1),
5337 sip
->ocs2007
? "message" : "x-ms-message",
5340 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5344 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5348 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
5349 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5350 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5352 body
= g_strdup_printf(
5354 "o=- 0 0 IN IP4 0.0.0.0\r\n"
5356 "c=IN IP4 0.0.0.0\r\n"
5358 "m=%s %d sip sip:%s\r\n"
5359 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5360 sip
->ocs2007
? "message" : "x-ms-message",
5363 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5368 sipe_get_auth_scheme_name(struct sipe_account_data
*sip
)
5370 const char *res
= "NTLM";
5371 #ifdef HAVE_KERBEROS
5372 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5376 (void) sip
; /* make compiler happy */
5381 static void sipe_connection_cleanup(struct sipe_account_data
*);
5382 static void create_connection(struct sipe_account_data
*, gchar
*, int);
5384 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5385 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5388 const gchar
*expires_header
;
5390 GSList
*hdr
= msg
->headers
;
5391 struct sipnameval
*elem
;
5393 expires_header
= sipmsg_find_header(msg
, "Expires");
5394 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
5395 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
5397 switch (msg
->response
) {
5400 sip
->registerstatus
= 0;
5402 const gchar
*contact_hdr
;
5407 const gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
5408 const char *auth_scheme
;
5410 if (!sip
->reregister_set
) {
5411 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
5412 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
5413 g_free(action_name
);
5414 sip
->reregister_set
= TRUE
;
5417 sip
->registerstatus
= 3;
5419 if (server_hdr
&& !sip
->server_version
) {
5420 sip
->server_version
= g_strdup(server_hdr
);
5425 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5426 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5429 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5430 fill_auth(tmp
, &sip
->registrar
);
5433 if (!sip
->reauthenticate_set
) {
5434 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
5435 guint reauth_timeout
;
5436 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
5437 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
5438 reauth_timeout
= sip
->registrar
.expires
- 300;
5440 /* NTLM: we have to reauthenticate as our security token expires
5441 after eight hours (be five minutes early) */
5442 reauth_timeout
= (8 * 3600) - 300;
5444 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
5445 g_free(action_name
);
5446 sip
->reauthenticate_set
= TRUE
;
5449 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
5451 epid
= get_epid(sip
);
5452 uuid
= generateUUIDfromEPID(epid
);
5455 // There can be multiple Contact headers (one per location where the user is logged in) so
5456 // make sure to only get the one for this uuid
5457 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
5458 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
5459 if (valid_contact
) {
5460 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
5461 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
5462 g_free(valid_contact
);
5465 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
5470 g_free(sip
->contact
);
5472 sip
->contact
= g_strdup_printf("<%s>", gruu
);
5475 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
5476 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
);
5478 sip
->ocs2007
= FALSE
;
5479 sip
->batched_support
= FALSE
;
5484 if (sipe_strcase_equal(elem
->name
, "Supported")) {
5485 if (sipe_strcase_equal(elem
->value
, "msrtc-event-categories")) {
5486 /* We interpret this as OCS2007+ indicator */
5487 sip
->ocs2007
= TRUE
;
5488 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
5490 if (sipe_strcase_equal(elem
->value
, "adhoclist")) {
5491 sip
->batched_support
= TRUE
;
5492 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
5495 if (sipe_strcase_equal(elem
->name
, "Allow-Events")){
5496 gchar
**caps
= g_strsplit(elem
->value
,",",0);
5499 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
5500 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
5505 hdr
= g_slist_next(hdr
);
5508 /* rejoin open chats to be able to use them by continue to send messages */
5509 purple_conversation_foreach(sipe_rejoin_chat
);
5512 if (!sip
->subscribed
) { //do it just once, not every re-register
5514 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
5515 (GCompareFunc
)g_ascii_strcasecmp
)) {
5516 sipe_subscribe_roaming_contacts(sip
);
5519 /* For 2007+ it does not make sence to subscribe to:
5520 * vnd-microsoft-roaming-ACL
5521 * vnd-microsoft-provisioning (not v2)
5523 * These are for backward compatibility.
5527 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
5528 (GCompareFunc
)g_ascii_strcasecmp
)) {
5529 sipe_subscribe_roaming_self(sip
);
5531 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
5532 (GCompareFunc
)g_ascii_strcasecmp
)) {
5533 sipe_subscribe_roaming_provisioning_v2(sip
);
5536 /* For 2005- servers */
5539 //sipe_options_request(sip, sip->sipdomain);
5541 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
5542 (GCompareFunc
)g_ascii_strcasecmp
)) {
5543 sipe_subscribe_roaming_acl(sip
);
5545 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
5546 (GCompareFunc
)g_ascii_strcasecmp
)) {
5547 sipe_subscribe_roaming_provisioning(sip
);
5549 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
5550 (GCompareFunc
)g_ascii_strcasecmp
)) {
5551 sipe_subscribe_presence_wpending(sip
, msg
);
5554 /* For 2007+ we publish our initial statuses and calendar data only after
5555 * received our existing publications in sipe_process_roaming_self()
5556 * Only in this case we know versions of current publications made
5559 /* For 2005- we publish our initial statuses only after
5560 * received our existing UserInfo data in response to
5561 * self subscription.
5562 * Only in this case we won't override existing UserInfo data
5563 * set earlier or by other client on our behalf.
5567 sip
->subscribed
= TRUE
;
5570 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
5571 "timeout=", ";", NULL
);
5572 if (timeout
!= NULL
) {
5573 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
5574 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
5575 sip
->keepalive_timeout
);
5579 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
5584 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
5586 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
5587 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
5591 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
5594 tmp
= g_strsplit(parts
[0], ":", 0);
5595 hostname
= g_strdup(tmp
[0]);
5596 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
5600 tmp
= g_strsplit(parts
[i
], "=", 0);
5602 if (g_strcasecmp("transport", tmp
[0]) == 0) {
5603 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
5604 transport
= SIPE_TRANSPORT_TCP
;
5605 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
5606 transport
= SIPE_TRANSPORT_UDP
;
5615 /* Close old connection */
5616 sipe_connection_cleanup(sip
);
5618 /* Create new connection */
5619 sip
->transport
= transport
;
5620 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
5621 hostname
, port
, TRANSPORT_DESCRIPTOR
);
5622 create_connection(sip
, hostname
, port
);
5628 if (sip
->registerstatus
!= 2) {
5629 const char *auth_scheme
;
5630 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
5631 if (sip
->registrar
.retries
> 3) {
5632 sip
->gc
->wants_to_die
= TRUE
;
5633 purple_connection_error(sip
->gc
, _("Authentication failed"));
5637 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5638 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5640 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
? tmp
: "");
5642 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
5643 sip
->gc
->wants_to_die
= TRUE
;
5644 purple_connection_error(sip
->gc
, tmp2
);
5648 fill_auth(tmp
, &sip
->registrar
);
5649 sip
->registerstatus
= 2;
5650 if (sip
->account
->disconnecting
) {
5651 do_register_exp(sip
, 0);
5659 const gchar
*diagnostics
= sipmsg_find_header(msg
, "Warning");
5660 gchar
**reason
= NULL
;
5662 if (diagnostics
!= NULL
) {
5664 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
5666 reason
= g_strsplit(diagnostics
, "\"", 0);
5668 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
5669 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
5672 sip
->gc
->wants_to_die
= TRUE
;
5673 purple_connection_error(sip
->gc
, warning
);
5680 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5681 gchar
*reason
= NULL
;
5683 if (diagnostics
!= NULL
) {
5684 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5686 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
5687 diagnostics
? (reason
? reason
: _("no reason given")) :
5688 _("SIP is either not enabled for the destination URI or it does not exist"));
5691 sip
->gc
->wants_to_die
= TRUE
;
5692 purple_connection_error(sip
->gc
, warning
);
5698 case 504: /* Server time-out */
5700 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5701 gchar
*reason
= NULL
;
5703 if (diagnostics
!= NULL
) {
5704 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5706 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
5709 sip
->gc
->wants_to_die
= TRUE
;
5710 purple_connection_error(sip
->gc
, warning
);
5720 * Returns 2005-style activity and Availability.
5722 * @param status Sipe statis id.
5725 sipe_get_act_avail_by_status_2005(const char *status
,
5729 int avail
= 300; /* online */
5730 int act
= 400; /* Available */
5732 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
5734 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
5736 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
5738 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
5740 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
5742 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
5743 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
5745 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
5746 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
5747 avail
= 0; /* offline */
5750 act
= 400; /* Available */
5753 if (activity
) *activity
= act
;
5754 if (availability
) *availability
= avail
;
5760 * @param activity 2005 aggregated activity. Ex.: 600
5761 * @param availablity 2005 aggregated availablity. Ex.: 300
5764 sipe_get_status_by_act_avail_2005(const int activity
,
5765 const int availablity
,
5766 char **activity_desc
)
5768 const char *status_id
= NULL
;
5769 const char *act
= NULL
;
5771 if (activity
< 150) {
5772 status_id
= SIPE_STATUS_ID_AWAY
;
5773 } else if (activity
< 200) {
5774 //status_id = SIPE_STATUS_ID_LUNCH;
5775 status_id
= SIPE_STATUS_ID_AWAY
;
5776 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
5777 } else if (activity
< 300) {
5778 //status_id = SIPE_STATUS_ID_IDLE;
5779 status_id
= SIPE_STATUS_ID_AWAY
;
5780 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5781 } else if (activity
< 400) {
5782 status_id
= SIPE_STATUS_ID_BRB
;
5783 } else if (activity
< 500) {
5784 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5785 } else if (activity
< 600) {
5786 //status_id = SIPE_STATUS_ID_ON_PHONE;
5787 status_id
= SIPE_STATUS_ID_BUSY
;
5788 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
5789 } else if (activity
< 700) {
5790 status_id
= SIPE_STATUS_ID_BUSY
;
5791 } else if (activity
< 800) {
5792 status_id
= SIPE_STATUS_ID_AWAY
;
5794 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5797 if (availablity
< 100)
5798 status_id
= SIPE_STATUS_ID_OFFLINE
;
5800 if (activity_desc
&& act
) {
5801 g_free(*activity_desc
);
5802 *activity_desc
= g_strdup(act
);
5809 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
5812 sipe_get_status_by_availability(int avail
,
5813 char** activity_desc
)
5816 const char *act
= NULL
;
5819 status
= SIPE_STATUS_ID_OFFLINE
;
5820 } else if (avail
< 4500) {
5821 status
= SIPE_STATUS_ID_AVAILABLE
;
5822 } else if (avail
< 6000) {
5823 //status = SIPE_STATUS_ID_IDLE;
5824 status
= SIPE_STATUS_ID_AVAILABLE
;
5825 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5826 } else if (avail
< 7500) {
5827 status
= SIPE_STATUS_ID_BUSY
;
5828 } else if (avail
< 9000) {
5829 //status = SIPE_STATUS_ID_BUSYIDLE;
5830 status
= SIPE_STATUS_ID_BUSY
;
5831 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
5832 } else if (avail
< 12000) {
5833 status
= SIPE_STATUS_ID_DND
;
5834 } else if (avail
< 15000) {
5835 status
= SIPE_STATUS_ID_BRB
;
5836 } else if (avail
< 18000) {
5837 status
= SIPE_STATUS_ID_AWAY
;
5839 status
= SIPE_STATUS_ID_OFFLINE
;
5842 if (activity_desc
&& act
) {
5843 g_free(*activity_desc
);
5844 *activity_desc
= g_strdup(act
);
5851 * Returns 2007-style availability value
5853 * @param sipe_status_id (in)
5854 * @param activity_token (out) Must be g_free()'d after use if consumed.
5857 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
5860 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
5862 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
5863 availability
= 15500;
5864 if (!activity_token
|| !(*activity_token
)) {
5865 activity
= SIPE_ACTIVITY_AWAY
;
5867 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
5868 availability
= 12500;
5869 activity
= SIPE_ACTIVITY_BRB
;
5870 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
5871 availability
= 9500;
5872 activity
= SIPE_ACTIVITY_DND
;
5873 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
5874 availability
= 6500;
5875 if (!activity_token
|| !(*activity_token
)) {
5876 activity
= SIPE_ACTIVITY_BUSY
;
5878 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
5879 availability
= 3500;
5880 activity
= SIPE_ACTIVITY_ONLINE
;
5881 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
5884 // Offline or invisible
5885 availability
= 18500;
5886 activity
= SIPE_ACTIVITY_OFFLINE
;
5889 if (activity_token
) {
5890 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
5892 return availability
;
5895 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5898 sipe_xml
*xn_categories
;
5899 const sipe_xml
*xn_category
;
5900 const char *status
= NULL
;
5901 gboolean do_update_status
= FALSE
;
5902 gboolean has_note_cleaned
= FALSE
;
5903 gboolean has_free_busy_cleaned
= FALSE
;
5905 xn_categories
= sipe_xml_parse(data
, len
);
5906 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
5908 for (xn_category
= sipe_xml_child(xn_categories
, "category");
5910 xn_category
= sipe_xml_twin(xn_category
) )
5912 const sipe_xml
*xn_node
;
5914 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
5915 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
5916 sipe_utils_str_to_time(tmp
) : 0;
5919 if (sipe_strequal(attrVar
, "contactCard"))
5921 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
5924 const sipe_xml
*node
;
5925 /* identity - Display Name and email */
5926 node
= sipe_xml_child(card
, "identity");
5928 char* display_name
= sipe_xml_data(
5929 sipe_xml_child(node
, "name/displayName"));
5930 char* email
= sipe_xml_data(
5931 sipe_xml_child(node
, "email"));
5933 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5934 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5936 g_free(display_name
);
5940 node
= sipe_xml_child(card
, "company");
5942 char* company
= sipe_xml_data(node
);
5943 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
5947 node
= sipe_xml_child(card
, "department");
5949 char* department
= sipe_xml_data(node
);
5950 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
5954 node
= sipe_xml_child(card
, "title");
5956 char* title
= sipe_xml_data(node
);
5957 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
5961 node
= sipe_xml_child(card
, "office");
5963 char* office
= sipe_xml_data(node
);
5964 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
5968 node
= sipe_xml_child(card
, "url");
5970 char* site
= sipe_xml_data(node
);
5971 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
5975 for (node
= sipe_xml_child(card
, "phone");
5977 node
= sipe_xml_twin(node
))
5979 const char *phone_type
= sipe_xml_attribute(node
, "type");
5980 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
5981 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
5983 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
5986 g_free(phone_display_string
);
5989 for (node
= sipe_xml_child(card
, "address");
5991 node
= sipe_xml_twin(node
))
5993 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
5994 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
5995 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
5996 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
5997 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
5998 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
6000 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
6001 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
6002 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
6003 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
6004 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
6010 g_free(country_code
);
6018 else if (sipe_strequal(attrVar
, "note"))
6021 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6023 if (!has_note_cleaned
) {
6024 has_note_cleaned
= TRUE
;
6026 g_free(sbuddy
->note
);
6027 sbuddy
->note
= NULL
;
6028 sbuddy
->is_oof_note
= FALSE
;
6029 sbuddy
->note_since
= publish_time
;
6031 do_update_status
= TRUE
;
6033 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
6034 /* clean up in case no 'note' element is supplied
6035 * which indicate note removal in client
6037 g_free(sbuddy
->note
);
6038 sbuddy
->note
= NULL
;
6039 sbuddy
->is_oof_note
= FALSE
;
6040 sbuddy
->note_since
= publish_time
;
6042 xn_node
= sipe_xml_child(xn_category
, "note/body");
6045 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
6047 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
6048 sbuddy
->note_since
= publish_time
;
6050 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s), note(%s)\n",
6051 uri
, sbuddy
->note
? sbuddy
->note
: "");
6053 /* to trigger UI refresh in case no status info is supplied in this update */
6054 do_update_status
= TRUE
;
6059 else if(sipe_strequal(attrVar
, "state"))
6063 const sipe_xml
*xn_availability
;
6064 const sipe_xml
*xn_activity
;
6065 const sipe_xml
*xn_meeting_subject
;
6066 const sipe_xml
*xn_meeting_location
;
6067 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6069 xn_node
= sipe_xml_child(xn_category
, "state");
6070 if (!xn_node
) continue;
6071 xn_availability
= sipe_xml_child(xn_node
, "availability");
6072 if (!xn_availability
) continue;
6073 xn_activity
= sipe_xml_child(xn_node
, "activity");
6074 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
6075 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
6077 tmp
= sipe_xml_data(xn_availability
);
6078 availability
= atoi(tmp
);
6081 /* activity, meeting_subject, meeting_location */
6086 g_free(sbuddy
->activity
);
6087 sbuddy
->activity
= NULL
;
6089 const char *token
= sipe_xml_attribute(xn_activity
, "token");
6090 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
6093 if (!is_empty(token
)) {
6094 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
6096 /* from custom element */
6098 char *custom
= sipe_xml_data(xn_custom
);
6100 if (!is_empty(custom
)) {
6101 sbuddy
->activity
= custom
;
6107 /* meeting_subject */
6108 g_free(sbuddy
->meeting_subject
);
6109 sbuddy
->meeting_subject
= NULL
;
6110 if (xn_meeting_subject
) {
6111 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
6113 if (!is_empty(meeting_subject
)) {
6114 sbuddy
->meeting_subject
= meeting_subject
;
6115 meeting_subject
= NULL
;
6117 g_free(meeting_subject
);
6119 /* meeting_location */
6120 g_free(sbuddy
->meeting_location
);
6121 sbuddy
->meeting_location
= NULL
;
6122 if (xn_meeting_location
) {
6123 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
6125 if (!is_empty(meeting_location
)) {
6126 sbuddy
->meeting_location
= meeting_location
;
6127 meeting_location
= NULL
;
6129 g_free(meeting_location
);
6132 status
= sipe_get_status_by_availability(availability
, &tmp
);
6133 if (sbuddy
->activity
&& tmp
) {
6134 char *tmp2
= sbuddy
->activity
;
6136 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
6140 sbuddy
->activity
= tmp
;
6144 do_update_status
= TRUE
;
6147 else if(sipe_strequal(attrVar
, "calendarData"))
6149 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6150 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
6151 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
6153 if (sbuddy
&& xn_free_busy
) {
6154 if (!has_free_busy_cleaned
) {
6155 has_free_busy_cleaned
= TRUE
;
6157 g_free(sbuddy
->cal_start_time
);
6158 sbuddy
->cal_start_time
= NULL
;
6160 g_free(sbuddy
->cal_free_busy_base64
);
6161 sbuddy
->cal_free_busy_base64
= NULL
;
6163 g_free(sbuddy
->cal_free_busy
);
6164 sbuddy
->cal_free_busy
= NULL
;
6166 sbuddy
->cal_free_busy_published
= publish_time
;
6169 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
6170 g_free(sbuddy
->cal_start_time
);
6171 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
6173 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
6176 g_free(sbuddy
->cal_free_busy_base64
);
6177 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
6179 g_free(sbuddy
->cal_free_busy
);
6180 sbuddy
->cal_free_busy
= NULL
;
6182 sbuddy
->cal_free_busy_published
= publish_time
;
6184 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
);
6188 if (sbuddy
&& xn_working_hours
) {
6189 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
6194 if (do_update_status
) {
6195 if (!status
) { /* no status category in this update, using contact's current status */
6196 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6197 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
6198 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
6199 status
= purple_status_get_id(pstatus
);
6202 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
6203 sipe_got_user_status(sip
, uri
, status
);
6206 sipe_xml_free(xn_categories
);
6209 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
6211 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6212 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
6213 payload
->host
= g_strdup(host
);
6214 payload
->buddies
= server
;
6215 sipe_subscribe_presence_batched_routed(sip
, payload
);
6216 sipe_subscribe_presence_batched_routed_free(payload
);
6219 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6222 xmlnode
*xn_resource
;
6223 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
6228 xn_list
= xmlnode_from_str(data
, len
);
6230 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
6232 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
6234 const char *uri
, *state
;
6235 xmlnode
*xn_instance
;
6237 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
6238 if (!xn_instance
) continue;
6240 uri
= xmlnode_get_attrib(xn_resource
, "uri");
6241 state
= xmlnode_get_attrib(xn_instance
, "state");
6242 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
6244 if (strstr(state
, "resubscribe")) {
6245 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
6247 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
6248 gchar
*user
= g_strdup(uri
);
6249 host
= g_strdup(poolFqdn
);
6250 server
= g_hash_table_lookup(servers
, host
);
6251 server
= g_slist_append(server
, user
);
6252 g_hash_table_insert(servers
, host
, server
);
6254 sipe_subscribe_presence_single(sip
, (void *) uri
);
6259 /* Send out any deferred poolFqdn subscriptions */
6260 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
6261 g_hash_table_destroy(servers
);
6263 xmlnode_free(xn_list
);
6266 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6270 gchar
*activity
= NULL
;
6272 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
6273 gboolean isonline
= FALSE
;
6274 xmlnode
*display_name_node
;
6276 pidf
= xmlnode_from_str(data
, len
);
6278 purple_debug_info("sipe", "process_incoming_notify_pidf: no parseable pidf:%s\n",data
);
6282 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
6284 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6285 basicstatus
= xmlnode_get_child(status
, "basic");
6290 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic found\n");
6295 getbasic
= xmlnode_get_data(basicstatus
);
6297 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic data found\n");
6302 purple_debug_info("sipe", "process_incoming_notify_pidf: basic-status(%s)\n", getbasic
);
6303 if (strstr(getbasic
, "open")) {
6308 uri
= sip_uri(xmlnode_get_attrib(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
6310 display_name_node
= xmlnode_get_child(pidf
, "display-name");
6311 if (display_name_node
) {
6312 char * display_name
= xmlnode_get_data(display_name_node
);
6314 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6315 g_free(display_name
);
6318 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
6319 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6320 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
6321 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
6322 activity
= xmlnode_get_data(basicstatus
);
6323 purple_debug_info("sipe", "process_incoming_notify_pidf: activity(%s)\n", activity
);
6330 const gchar
* status_id
= NULL
;
6332 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
6333 status_id
= SIPE_STATUS_ID_BUSY
;
6334 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
6335 status_id
= SIPE_STATUS_ID_AWAY
;
6340 status_id
= SIPE_STATUS_ID_AVAILABLE
;
6343 purple_debug_info("sipe", "process_incoming_notify_pidf: status_id(%s)\n", status_id
);
6344 sipe_got_user_status(sip
, uri
, status_id
);
6346 sipe_got_user_status(sip
, uri
, SIPE_STATUS_ID_OFFLINE
);
6356 sipe_user_info_has_updated(struct sipe_account_data
*sip
,
6357 xmlnode
*xn_userinfo
)
6361 g_free(sip
->user_states
);
6362 sip
->user_states
= NULL
;
6363 if ((xn_states
= xmlnode_get_child(xn_userinfo
, "states")) != NULL
) {
6364 sip
->user_states
= xmlnode_to_str(xn_states
, NULL
);
6365 /* this is a hack-around to remove added newline after inner element,
6366 * state in this case, where it shouldn't be.
6367 * After several use of xmlnode_to_str, amount of added newlines
6368 * grows significantly.
6370 purple_str_strip_char(sip
->user_states
, '\n');
6371 //purple_str_strip_char(sip->user_states, '\r');
6374 /* Publish initial state if not yet.
6375 * Assuming this happens on initial responce to self subscription
6376 * so we've already updated our UserInfo.
6378 if (!sip
->initial_state_published
) {
6379 send_presence_soap(sip
, FALSE
);
6381 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
6385 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6387 char *activity
= NULL
;
6389 const char *status_id
= NULL
;
6392 char *self_uri
= sip_uri_self(sip
);
6395 const char *device_name
= NULL
;
6396 const char *cal_start_time
= NULL
;
6397 const char *cal_granularity
= NULL
;
6398 char *cal_free_busy_base64
= NULL
;
6399 struct sipe_buddy
*sbuddy
;
6401 xmlnode
*xn_presentity
;
6402 xmlnode
*xn_availability
;
6403 xmlnode
*xn_activity
;
6404 xmlnode
*xn_display_name
;
6406 xmlnode
*xn_phone_number
;
6407 xmlnode
*xn_userinfo
;
6411 xmlnode
*xn_contact
;
6413 char *free_activity
;
6415 const char *user_avail_nil
;
6417 time_t user_avail_since
= 0;
6418 time_t activity_since
= 0;
6420 /* fix for Reuters environment on Linux */
6421 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
6423 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
6424 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
6427 xn_presentity
= xmlnode_from_str(data
, len
);
6430 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
6431 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
6432 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
6433 xn_email
= xmlnode_get_child(xn_presentity
, "email");
6434 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
6435 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
6436 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
6437 xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
): NULL
;
6438 user_avail
= xn_state
? xmlnode_get_int_attrib(xn_state
, "avail", 0) : 0;
6439 user_avail_since
= xn_state
? sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since")) : 0;
6440 user_avail_nil
= xn_state
? xmlnode_get_attrib(xn_state
, "nil") : NULL
;
6441 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
6442 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
6443 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
6445 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
6447 user_avail_since
= 0;
6450 free_activity
= NULL
;
6452 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
6453 uri
= sip_uri_from_name(name
);
6454 avl
= xmlnode_get_int_attrib(xn_availability
, "aggregate", 0);
6455 epid
= xmlnode_get_attrib(xn_availability
, "epid");
6456 act
= xmlnode_get_int_attrib(xn_activity
, "aggregate", 0);
6458 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
6459 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
6460 if (user_avail
> res_avail
) {
6461 res_avail
= user_avail
;
6462 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
6465 if (xn_display_name
) {
6466 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
6467 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
6468 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
6469 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
6470 char *tel_uri
= sip_to_tel_uri(phone_number
);
6472 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6473 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
6474 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
6475 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
6478 g_free(phone_label
);
6479 g_free(phone_number
);
6481 g_free(display_name
);
6486 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
6488 /* Ex.: <tel type="work">tel:+3222220000</tel> */
6489 const char *phone_type
= xmlnode_get_attrib(node
, "type");
6490 char* phone
= xmlnode_get_data(node
);
6492 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
6498 /* devicePresence */
6499 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
6500 xmlnode
*xn_device_name
;
6501 xmlnode
*xn_calendar_info
;
6506 if (sipe_strequal(xmlnode_get_attrib(node
, "epid"), epid
)) {
6507 xn_device_name
= xmlnode_get_child(node
, "deviceName");
6508 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
6512 xn_calendar_info
= xmlnode_get_child(node
, "calendarInfo");
6513 if (xn_calendar_info
) {
6514 const char *cal_start_time_tmp
= xmlnode_get_attrib(xn_calendar_info
, "startTime");
6516 if (cal_start_time
) {
6517 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
6518 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
6520 if (cal_start_time_t_tmp
> cal_start_time_t
) {
6521 cal_start_time
= cal_start_time_tmp
;
6522 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6523 g_free(cal_free_busy_base64
);
6524 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6526 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
);
6529 cal_start_time
= cal_start_time_tmp
;
6530 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6531 g_free(cal_free_busy_base64
);
6532 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6534 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
);
6539 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
6541 int dev_avail
= xmlnode_get_int_attrib(xn_state
, "avail", 0);
6542 time_t dev_avail_since
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since"));
6544 state
= xmlnode_get_data(xn_state
);
6545 if (dev_avail_since
> user_avail_since
&&
6546 dev_avail
>= res_avail
)
6548 res_avail
= dev_avail
;
6549 if (!is_empty(state
))
6551 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
6553 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
6554 } else if (sipe_strequal(state
, "presenting")) {
6556 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
6561 activity_since
= dev_avail_since
;
6563 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
6570 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
6572 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
6576 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6579 g_free(sbuddy
->activity
);
6580 sbuddy
->activity
= activity
;
6583 sbuddy
->activity_since
= activity_since
;
6585 sbuddy
->user_avail
= user_avail
;
6586 sbuddy
->user_avail_since
= user_avail_since
;
6588 g_free(sbuddy
->note
);
6589 sbuddy
->note
= NULL
;
6590 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
6592 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
6594 g_free(sbuddy
->device_name
);
6595 sbuddy
->device_name
= NULL
;
6596 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
6598 if (!is_empty(cal_free_busy_base64
)) {
6599 g_free(sbuddy
->cal_start_time
);
6600 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
6602 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
6604 g_free(sbuddy
->cal_free_busy_base64
);
6605 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
6606 cal_free_busy_base64
= NULL
;
6608 g_free(sbuddy
->cal_free_busy
);
6609 sbuddy
->cal_free_busy
= NULL
;
6612 sbuddy
->last_non_cal_status_id
= status_id
;
6613 g_free(sbuddy
->last_non_cal_activity
);
6614 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
6616 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
6617 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
6619 sip
->is_oof_note
= sbuddy
->is_oof_note
;
6622 sip
->note
= g_strdup(sbuddy
->note
);
6624 sip
->note_since
= time(NULL
);
6627 g_free(sip
->status
);
6628 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
6631 g_free(cal_free_busy_base64
);
6634 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
6635 sipe_got_user_status(sip
, uri
, status_id
);
6637 if (!sip
->ocs2007
&& sipe_strcase_equal(self_uri
, uri
)) {
6638 sipe_user_info_has_updated(sip
, xn_userinfo
);
6642 xmlnode_free(xn_presentity
);
6647 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
6649 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6651 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
6653 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
6654 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
6656 const char *content
= msg
->body
;
6657 unsigned length
= msg
->bodylen
;
6658 PurpleMimeDocument
*mime
= NULL
;
6660 if (strstr(ctype
, "multipart"))
6662 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6663 const char *content_type
;
6665 mime
= purple_mime_document_parse(doc
);
6666 parts
= purple_mime_document_get_parts(mime
);
6668 content
= purple_mime_part_get_data(parts
->data
);
6669 length
= purple_mime_part_get_length(parts
->data
);
6670 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
6671 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
6673 process_incoming_notify_rlmi_resub(sip
, content
, length
);
6675 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
6677 process_incoming_notify_msrtc(sip
, content
, length
);
6681 process_incoming_notify_rlmi(sip
, content
, length
);
6683 parts
= parts
->next
;
6689 purple_mime_document_free(mime
);
6692 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
6694 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
6696 else if(strstr(ctype
, "application/rlmi+xml"))
6698 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
6701 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
6703 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
6707 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
6711 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
6713 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6714 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6716 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
6719 strstr(ctype
, "multipart") &&
6720 (strstr(ctype
, "application/rlmi+xml") ||
6721 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
6722 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6723 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
6724 GList
*parts
= purple_mime_document_get_parts(mime
);
6725 GSList
*buddies
= NULL
;
6726 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6729 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
6730 purple_mime_part_get_length(parts
->data
));
6732 if (xml
&& !sipe_strequal(xml
->name
, "list")) {
6733 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
6735 buddies
= g_slist_append(buddies
, uri
);
6739 parts
= parts
->next
;
6742 if (mime
) purple_mime_document_free(mime
);
6744 payload
->host
= g_strdup(who
);
6745 payload
->buddies
= buddies
;
6746 sipe_schedule_action(action_name
, timeout
,
6747 sipe_subscribe_presence_batched_routed
,
6748 sipe_subscribe_presence_batched_routed_free
,
6750 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
6753 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6754 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
6756 g_free(action_name
);
6760 * Dispatcher for all incoming subscription information
6761 * whether it comes from NOTIFY, BENOTIFY requests or
6762 * piggy-backed to subscription's OK responce.
6764 * @param request whether initiated from BE/NOTIFY request or OK-response message.
6765 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
6767 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
6769 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
6770 const gchar
*event
= sipmsg_find_header(msg
, "Event");
6771 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
6774 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
6776 tmp
= fix_newlines(msg
->body
));
6778 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
6780 /* implicit subscriptions */
6781 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
6782 sipe_process_imdn(sip
, msg
);
6786 /* for one off subscriptions (send with Expire: 0) */
6787 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
6789 sipe_process_provisioning_v2(sip
, msg
);
6791 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
6793 sipe_process_provisioning(sip
, msg
);
6795 else if (sipe_strcase_equal(event
, "presence"))
6797 sipe_process_presence(sip
, msg
);
6799 else if (sipe_strcase_equal(event
, "registration-notify"))
6801 sipe_process_registration_notify(sip
, msg
);
6804 if (!subscription_state
|| strstr(subscription_state
, "active"))
6806 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
6808 sipe_process_roaming_contacts(sip
, msg
);
6810 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
6812 sipe_process_roaming_self(sip
, msg
);
6814 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
6816 sipe_process_roaming_acl(sip
, msg
);
6818 else if (sipe_strcase_equal(event
, "presence.wpending"))
6820 sipe_process_presence_wpending(sip
, msg
);
6822 else if (sipe_strcase_equal(event
, "conference"))
6824 sipe_process_conference(sip
, msg
);
6829 /* The server sends status 'terminated' */
6830 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
6831 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6832 gchar
*key
= sipe_get_subscription_key(event
, who
);
6834 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
6837 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
6838 g_hash_table_remove(sip
->subscriptions
, key
);
6839 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
6845 if (!request
&& event
) {
6846 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
6847 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
6848 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
6851 /* 2 min ahead of expiration */
6852 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
6854 if (sipe_strcase_equal(event
, "presence.wpending") &&
6855 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
6857 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
6858 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
6859 g_free(action_name
);
6861 else if (sipe_strcase_equal(event
, "presence") &&
6862 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
6864 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
6865 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6867 if (sip
->batched_support
) {
6868 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
6871 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6872 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
6874 g_free(action_name
);
6880 /* The client responses on received a NOTIFY message */
6881 if (request
&& !benotify
)
6883 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
6888 * Whether user manually changed status or
6889 * it was changed automatically due to user
6890 * became inactive/active again
6893 sipe_is_user_state(struct sipe_account_data
*sip
)
6896 time_t now
= time(NULL
);
6898 purple_debug_info("sipe", "sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
6899 purple_debug_info("sipe", "sipe_is_user_state: now : %s", asctime(localtime(&now
)));
6901 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
6903 purple_debug_info("sipe", "sipe_is_user_state: res = %s\n", res
? "USER" : "MACHINE");
6908 send_presence_soap0(struct sipe_account_data
*sip
,
6909 gboolean do_publish_calendar
,
6910 gboolean do_reset_status
)
6912 struct sipe_ews
* ews
= sip
->ews
;
6913 int availability
= 0;
6918 gchar
*res_note
= NULL
;
6919 gchar
*res_oof
= NULL
;
6920 const gchar
*note_pub
= NULL
;
6921 gchar
*states
= NULL
;
6922 gchar
*calendar_data
= NULL
;
6923 gchar
*epid
= get_epid(sip
);
6924 time_t now
= time(NULL
);
6925 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
6926 const gchar
*oof_note
= ews
? sipe_ews_get_oof_note(ews
) : NULL
;
6927 const char *user_input
;
6928 gboolean pub_oof
= ews
&& oof_note
&& (!sip
->note
|| ews
->updated
> sip
->note_since
);
6930 if (oof_note
&& sip
->note
) {
6931 purple_debug_info("sipe", "ews->oof_start : %s", asctime(localtime(&(ews
->oof_start
))));
6932 purple_debug_info("sipe", "sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
6935 purple_debug_info("sipe", "sip->note : %s", sip
->note
? sip
->note
: "");
6937 if (!sip
->initial_state_published
||
6940 g_free(sip
->status
);
6941 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
6944 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
6948 note_pub
= oof_note
;
6949 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
6950 ews
->published
= TRUE
;
6951 } else if (sip
->note
) {
6952 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in ews already */
6955 sip
->is_oof_note
= FALSE
;
6956 sip
->note_since
= 0;
6958 note_pub
= sip
->note
;
6959 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
6965 /* to protocol internal plain text format */
6966 tmp
= purple_markup_strip_html(note_pub
);
6967 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
6972 if (!do_reset_status
) {
6973 if (sipe_is_user_state(sip
) && !do_publish_calendar
&& sip
->initial_state_published
)
6975 gchar
*activity_token
= NULL
;
6976 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
6978 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
6983 g_free(activity_token
);
6985 else /* preserve existing publication */
6987 if (sip
->user_states
) {
6988 states
= g_strdup(sip
->user_states
);
6992 /* do nothing - then User state will be erased */
6994 sip
->initial_state_published
= TRUE
;
6997 if (ews
&& (!is_empty(ews
->legacy_dn
) || !is_empty(ews
->email
)) && ews
->fb_start
&& !is_empty(ews
->free_busy
))
6999 char *fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
7000 char *free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
7001 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
7002 !is_empty(ews
->legacy_dn
) ? ews
->legacy_dn
: ews
->email
,
7005 g_free(fb_start_str
);
7006 g_free(free_busy_base64
);
7009 user_input
= !sipe_is_user_state(sip
) && sip
->status
!= SIPE_STATUS_ID_AVAILABLE
? "idle" : "active";
7011 /* forming resulting XML */
7012 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
7016 (tmp
= g_ascii_strup(g_get_host_name(), -1)),
7017 res_note
? res_note
: "",
7018 res_oof
? res_oof
: "",
7019 states
? states
: "",
7020 calendar_data
? calendar_data
: "",
7029 g_free(calendar_data
);
7031 send_soap_request(sip
, body
);
7034 g_free(since_time_str
);
7039 send_presence_soap(struct sipe_account_data
*sip
,
7040 gboolean do_publish_calendar
)
7042 return send_presence_soap0(sip
, do_publish_calendar
, FALSE
);
7047 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
7049 struct transaction
*trans
)
7051 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
7053 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
7059 gboolean has_device_publication
= FALSE
;
7061 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
7063 /* test if version mismatch fault */
7064 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
7065 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
7066 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
7073 /* accumulating information about faulty versions */
7074 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
7075 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
7077 node
= xmlnode_get_next_twin(node
))
7079 const gchar
*index
= xmlnode_get_attrib(node
, "index");
7080 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
7082 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
7083 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
7087 /* here we are parsing own request to figure out what publication
7088 * referensed here only by index went wrong
7090 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
7093 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
7094 index_our
= 1; /* starts with 1 - our first publication */
7096 node
= xmlnode_get_next_twin(node
), index_our
++)
7098 gchar
*idx
= g_strdup_printf("%d", index_our
);
7099 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
7100 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
7103 if (sipe_strequal("device", categoryName
)) {
7104 has_device_publication
= TRUE
;
7107 if (curVersion
) { /* fault exist on this index */
7108 const gchar
*container
= xmlnode_get_attrib(node
, "container");
7109 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
7110 /* key is <category><instance><container> */
7111 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
7112 struct sipe_publication
*publication
=
7113 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
7115 purple_debug_info("sipe", "key is %s\n", key
);
7118 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
7119 key
, curVersion
, publication
->version
);
7120 /* updating publication's version to the correct one */
7121 publication
->version
= atoi(curVersion
);
7127 g_hash_table_destroy(faults
);
7129 /* rebublishing with right versions */
7130 if (has_device_publication
) {
7131 send_publish_category_initial(sip
);
7133 send_presence_status(sip
);
7140 * Returns 'device' XML part for publication.
7141 * Must be g_free'd after use.
7144 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
7148 gchar
*epid
= get_epid(sip
);
7149 gchar
*uuid
= generateUUIDfromEPID(epid
);
7150 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
7151 /* key is <category><instance><container> */
7152 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
7153 struct sipe_publication
*publication
=
7154 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
7159 uri
= sip_uri_self(sip
);
7160 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
7162 publication
? publication
->version
: 0,
7165 "00:00:00+01:00", /* @TODO make timezone real*/
7176 * A service method - use
7177 * - send_publish_get_category_state_machine and
7178 * - send_publish_get_category_state_user instead.
7179 * Must be g_free'd after use.
7182 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
7183 gboolean is_user_state
)
7185 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
7186 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
7187 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
7188 /* key is <category><instance><container> */
7189 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7190 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7191 struct sipe_publication
*publication_2
=
7192 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7193 struct sipe_publication
*publication_3
=
7194 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7199 if (publication_2
&& (publication_2
->availability
== availability
))
7201 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
7202 return NULL
; /* nothing to update */
7205 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
7207 publication_2
? publication_2
->version
: 0,
7210 publication_3
? publication_3
->version
: 0,
7215 * Only Busy and OOF calendar event are published.
7216 * Different instances are used for that.
7218 * Must be g_free'd after use.
7221 sipe_publish_get_category_state_calendar(struct sipe_account_data
*sip
,
7222 struct sipe_cal_event
*event
,
7226 gchar
*start_time_str
;
7227 int availability
= 0;
7230 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
7231 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
) :
7232 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
7234 /* key is <category><instance><container> */
7235 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7236 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7237 struct sipe_publication
*publication_2
=
7238 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7239 struct sipe_publication
*publication_3
=
7240 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7245 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
7246 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7247 "Exiting as no publication and no event for cal_satus:%d\n", cal_satus
);
7253 (publication_3
->availability
== availability
) &&
7254 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
7257 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7258 "cal state has NOT changed for cal_satus:%d. Exiting.\n", cal_satus
);
7259 return NULL
; /* nothing to update */
7264 (event
->cal_status
== SIPE_CAL_BUSY
||
7265 event
->cal_status
== SIPE_CAL_OOF
))
7267 gchar
*availability_xml_str
= NULL
;
7268 gchar
*activity_xml_str
= NULL
;
7270 if (event
->cal_status
== SIPE_CAL_BUSY
) {
7271 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
7274 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
7275 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7276 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
7277 "minAvailability=\"6500\"",
7278 "maxAvailability=\"8999\"");
7279 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
7280 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7281 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
7282 "minAvailability=\"12000\"",
7285 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
7287 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
7289 publication_2
? publication_2
->version
: 0,
7292 availability_xml_str
? availability_xml_str
: "",
7293 activity_xml_str
? activity_xml_str
: "",
7294 event
->subject
? event
->subject
: "",
7295 event
->location
? event
->location
: "",
7298 publication_3
? publication_3
->version
: 0,
7301 availability_xml_str
? availability_xml_str
: "",
7302 activity_xml_str
? activity_xml_str
: "",
7303 event
->subject
? event
->subject
: "",
7304 event
->location
? event
->location
: ""
7306 g_free(start_time_str
);
7307 g_free(availability_xml_str
);
7308 g_free(activity_xml_str
);
7311 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
7313 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
7315 publication_2
? publication_2
->version
: 0,
7318 publication_3
? publication_3
->version
: 0
7326 * Returns 'machineState' XML part for publication.
7327 * Must be g_free'd after use.
7330 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
7332 return sipe_publish_get_category_state(sip
, FALSE
);
7336 * Returns 'userState' XML part for publication.
7337 * Must be g_free'd after use.
7340 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
7342 return sipe_publish_get_category_state(sip
, TRUE
);
7346 * Returns 'note' XML part for publication.
7347 * Must be g_free'd after use.
7349 * Protocol format for Note is plain text.
7351 * @param note a note in Sipe internal HTML format
7352 * @param note_type either personal or OOF
7355 sipe_publish_get_category_note(struct sipe_account_data
*sip
,
7356 const char *note
, /* html */
7357 const char *note_type
,
7361 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
) : 0;
7362 /* key is <category><instance><container> */
7363 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
7364 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
7365 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
7367 struct sipe_publication
*publication_note_200
=
7368 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
7369 struct sipe_publication
*publication_note_300
=
7370 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
7371 struct sipe_publication
*publication_note_400
=
7372 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
7374 char *tmp
= note
? purple_markup_strip_html(note
) : NULL
;
7375 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
7376 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
7377 char *res
, *tmp1
, *tmp2
, *tmp3
;
7378 char *start_time_attr
;
7379 char *end_time_attr
;
7383 g_free(key_note_200
);
7384 g_free(key_note_300
);
7385 g_free(key_note_400
);
7387 /* we even need to republish empty note */
7388 if (sipe_strequal(n1
, n2
))
7390 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
7392 return NULL
; /* nothing to update */
7395 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
7398 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
7402 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7405 publication_note_200
? publication_note_200
->version
: 0,
7407 start_time_attr
? start_time_attr
: "",
7408 end_time_attr
? end_time_attr
: "",
7411 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7414 publication_note_300
? publication_note_300
->version
: 0,
7416 start_time_attr
? start_time_attr
: "",
7417 end_time_attr
? end_time_attr
: "",
7420 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7423 publication_note_400
? publication_note_400
->version
: 0,
7425 start_time_attr
? start_time_attr
: "",
7426 end_time_attr
? end_time_attr
: "",
7429 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7433 publication_note_200
? publication_note_200
->version
: 0,
7435 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7439 publication_note_200
? publication_note_200
->version
: 0,
7441 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7445 publication_note_200
? publication_note_200
->version
: 0,
7448 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
7450 g_free(start_time_attr
);
7451 g_free(end_time_attr
);
7461 * Returns 'calendarData' XML part with WorkingHours for publication.
7462 * Must be g_free'd after use.
7465 sipe_publish_get_category_cal_working_hours(struct sipe_account_data
*sip
)
7467 struct sipe_ews
* ews
= sip
->ews
;
7469 /* key is <category><instance><container> */
7470 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
7471 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
7472 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
7473 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
7474 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
7475 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
7477 struct sipe_publication
*publication_cal_1
=
7478 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7479 struct sipe_publication
*publication_cal_100
=
7480 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7481 struct sipe_publication
*publication_cal_200
=
7482 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7483 struct sipe_publication
*publication_cal_300
=
7484 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7485 struct sipe_publication
*publication_cal_400
=
7486 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7487 struct sipe_publication
*publication_cal_32000
=
7488 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7490 const char *n1
= ews
? ews
->working_hours_xml_str
: NULL
;
7491 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
7494 g_free(key_cal_100
);
7495 g_free(key_cal_200
);
7496 g_free(key_cal_300
);
7497 g_free(key_cal_400
);
7498 g_free(key_cal_32000
);
7500 if (!ews
|| is_empty(ews
->email
) || is_empty(ews
->working_hours_xml_str
)) {
7501 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: no data to publish, exiting\n");
7505 if (sipe_strequal(n1
, n2
))
7507 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.\n");
7508 return NULL
; /* nothing to update */
7511 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
7513 publication_cal_1
? publication_cal_1
->version
: 0,
7515 ews
->working_hours_xml_str
,
7517 publication_cal_100
? publication_cal_100
->version
: 0,
7519 publication_cal_200
? publication_cal_200
->version
: 0,
7521 ews
->working_hours_xml_str
,
7523 publication_cal_300
? publication_cal_300
->version
: 0,
7525 ews
->working_hours_xml_str
,
7526 /* 400 - Personal */
7527 publication_cal_400
? publication_cal_400
->version
: 0,
7529 ews
->working_hours_xml_str
,
7530 /* 32000 - Blocked */
7531 publication_cal_32000
? publication_cal_32000
->version
: 0
7536 * Returns 'calendarData' XML part with FreeBusy for publication.
7537 * Must be g_free'd after use.
7540 sipe_publish_get_category_cal_free_busy(struct sipe_account_data
*sip
)
7542 struct sipe_ews
* ews
= sip
->ews
;
7543 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
7545 char *free_busy_base64
;
7550 /* key is <category><instance><container> */
7551 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
7552 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
7553 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
7554 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
7555 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
7556 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
7558 struct sipe_publication
*publication_cal_1
=
7559 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7560 struct sipe_publication
*publication_cal_100
=
7561 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7562 struct sipe_publication
*publication_cal_200
=
7563 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7564 struct sipe_publication
*publication_cal_300
=
7565 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7566 struct sipe_publication
*publication_cal_400
=
7567 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7568 struct sipe_publication
*publication_cal_32000
=
7569 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7572 g_free(key_cal_100
);
7573 g_free(key_cal_200
);
7574 g_free(key_cal_300
);
7575 g_free(key_cal_400
);
7576 g_free(key_cal_32000
);
7578 if (!ews
|| is_empty(ews
->email
) || !ews
->fb_start
|| is_empty(ews
->free_busy
)) {
7579 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: no data to publish, exiting\n");
7583 fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
7584 free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
7586 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
7587 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
7589 /* we will rebuplish the same data to refresh publication time,
7590 * so if data from multiple sources, most recent will be choosen
7592 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
7594 // purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.\n");
7595 // g_free(fb_start_str);
7596 // g_free(free_busy_base64);
7597 // return NULL; /* nothing to update */
7600 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
7603 publication_cal_1
? publication_cal_1
->version
: 0,
7606 publication_cal_100
? publication_cal_100
->version
: 0,
7609 publication_cal_200
? publication_cal_200
->version
: 0,
7615 publication_cal_300
? publication_cal_300
->version
: 0,
7619 /* 400 - Personal */
7621 publication_cal_400
? publication_cal_400
->version
: 0,
7625 /* 32000 - Blocked */
7627 publication_cal_32000
? publication_cal_32000
->version
: 0
7630 g_free(fb_start_str
);
7631 g_free(free_busy_base64
);
7635 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
7642 uri
= sip_uri_self(sip
);
7643 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
7647 tmp
= get_contact(sip
);
7648 hdr
= g_strdup_printf("Contact: %s\r\n"
7649 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
7651 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
7660 send_publish_category_initial(struct sipe_account_data
*sip
)
7662 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
7664 gchar
*publications
;
7666 g_free(sip
->status
);
7667 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
7669 pub_machine
= sipe_publish_get_category_state_machine(sip
);
7670 publications
= g_strdup_printf("%s%s",
7672 pub_machine
? pub_machine
: "");
7674 g_free(pub_machine
);
7676 send_presence_publish(sip
, publications
);
7677 g_free(publications
);
7681 send_presence_category_publish(struct sipe_account_data
*sip
)
7683 gchar
*pub_state
= sipe_is_user_state(sip
) ?
7684 sipe_publish_get_category_state_user(sip
) :
7685 sipe_publish_get_category_state_machine(sip
);
7686 gchar
*pub_note
= sipe_publish_get_category_note(sip
,
7688 sip
->is_oof_note
? "OOF" : "personal",
7691 gchar
*publications
;
7693 if (!pub_state
&& !pub_note
) {
7694 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
7698 publications
= g_strdup_printf("%s%s",
7699 pub_state
? pub_state
: "",
7700 pub_note
? pub_note
: "");
7705 send_presence_publish(sip
, publications
);
7706 g_free(publications
);
7710 * Publishes self status
7711 * based on own calendar information.
7716 publish_calendar_status_self(struct sipe_account_data
*sip
)
7718 struct sipe_cal_event
* event
= NULL
;
7719 gchar
*pub_cal_working_hours
= NULL
;
7720 gchar
*pub_cal_free_busy
= NULL
;
7721 gchar
*pub_calendar
= NULL
;
7722 gchar
*pub_calendar2
= NULL
;
7723 gchar
*pub_oof_note
= NULL
;
7724 const gchar
*oof_note
;
7725 time_t oof_start
= 0;
7729 purple_debug_info("sipe", "publish_calendar_status_self() no calendar data.\n");
7733 purple_debug_info("sipe", "publish_calendar_status_self() started.\n");
7734 if (sip
->ews
->cal_events
) {
7735 event
= sipe_cal_get_event(sip
->ews
->cal_events
, time(NULL
));
7739 purple_debug_info("sipe", "publish_calendar_status_self: current event is NULL\n");
7741 char *desc
= sipe_cal_event_describe(event
);
7742 purple_debug_info("sipe", "publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
7748 OOF publish, Busy clean
7750 OOF clean, Busy publish
7752 OOF clean, Busy clean
7754 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
7755 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_OOF
);
7756 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7757 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
7758 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7759 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7761 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7762 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7765 oof_note
= sipe_ews_get_oof_note(sip
->ews
);
7766 if (sipe_strequal("Scheduled", sip
->ews
->oof_state
)) {
7767 oof_start
= sip
->ews
->oof_start
;
7768 oof_end
= sip
->ews
->oof_end
;
7770 pub_oof_note
= sipe_publish_get_category_note(sip
, oof_note
, "OOF", oof_start
, oof_end
);
7772 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sip
);
7773 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sip
);
7775 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
7776 purple_debug_info("sipe", "publish_calendar_status_self: nothing has changed.\n");
7778 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
7779 pub_cal_working_hours
? pub_cal_working_hours
: "",
7780 pub_cal_free_busy
? pub_cal_free_busy
: "",
7781 pub_calendar
? pub_calendar
: "",
7782 pub_calendar2
? pub_calendar2
: "",
7783 pub_oof_note
? pub_oof_note
: "");
7785 send_presence_publish(sip
, publications
);
7786 g_free(publications
);
7789 g_free(pub_cal_working_hours
);
7790 g_free(pub_cal_free_busy
);
7791 g_free(pub_calendar
);
7792 g_free(pub_calendar2
);
7793 g_free(pub_oof_note
);
7795 /* repeat scheduling */
7796 sipe_sched_calendar_status_self_publish(sip
, time(NULL
));
7799 static void send_presence_status(struct sipe_account_data
*sip
)
7801 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
7803 if (!status
) return;
7805 purple_debug_info("sipe", "send_presence_status: status: %s (%s)\n",
7806 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
7807 sipe_is_user_state(sip
) ? "USER" : "MACHINE");
7810 send_presence_category_publish(sip
);
7812 send_presence_soap(sip
, FALSE
);
7816 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
7818 gboolean found
= FALSE
;
7819 const char *method
= msg
->method
? msg
->method
: "NOT FOUND";
7820 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,method
);
7821 if (msg
->response
== 0) { /* request */
7822 if (sipe_strequal(method
, "MESSAGE")) {
7823 process_incoming_message(sip
, msg
);
7825 } else if (sipe_strequal(method
, "NOTIFY")) {
7826 purple_debug_info("sipe","send->process_incoming_notify\n");
7827 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
7829 } else if (sipe_strequal(method
, "BENOTIFY")) {
7830 purple_debug_info("sipe","send->process_incoming_benotify\n");
7831 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
7833 } else if (sipe_strequal(method
, "INVITE")) {
7834 process_incoming_invite(sip
, msg
);
7836 } else if (sipe_strequal(method
, "REFER")) {
7837 process_incoming_refer(sip
, msg
);
7839 } else if (sipe_strequal(method
, "OPTIONS")) {
7840 process_incoming_options(sip
, msg
);
7842 } else if (sipe_strequal(method
, "INFO")) {
7843 process_incoming_info(sip
, msg
);
7845 } else if (sipe_strequal(method
, "ACK")) {
7846 // ACK's don't need any response
7848 } else if (sipe_strequal(method
, "SUBSCRIBE")) {
7849 // LCS 2005 sends us these - just respond 200 OK
7851 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
7852 } else if (sipe_strequal(method
, "BYE")) {
7853 process_incoming_bye(sip
, msg
);
7856 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
7858 } else { /* response */
7859 struct transaction
*trans
= transactions_find(sip
, msg
);
7861 if (msg
->response
== 407) {
7862 gchar
*resend
, *auth
;
7865 if (sip
->proxy
.retries
> 30) return;
7866 sip
->proxy
.retries
++;
7867 /* do proxy authentication */
7869 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
7871 fill_auth(ptmp
, &sip
->proxy
);
7872 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
7873 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7874 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7876 resend
= sipmsg_to_string(trans
->msg
);
7877 /* resend request */
7878 sendout_pkt(sip
->gc
, resend
);
7881 if (msg
->response
< 200) {
7882 /* ignore provisional response */
7883 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
7885 sip
->proxy
.retries
= 0;
7886 if (sipe_strequal(trans
->msg
->method
, "REGISTER")) {
7887 if (msg
->response
== 401)
7889 sip
->registrar
.retries
++;
7893 sip
->registrar
.retries
= 0;
7895 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
7897 if (msg
->response
== 401) {
7898 gchar
*resend
, *auth
, *ptmp
;
7899 const char* auth_scheme
;
7901 if (sip
->registrar
.retries
> 4) return;
7902 sip
->registrar
.retries
++;
7904 auth_scheme
= sipe_get_auth_scheme_name(sip
);
7905 ptmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
7907 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
? ptmp
: "");
7909 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
7910 sip
->gc
->wants_to_die
= TRUE
;
7911 purple_connection_error(sip
->gc
, tmp2
);
7916 fill_auth(ptmp
, &sip
->registrar
);
7917 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
7918 sipmsg_remove_header_now(trans
->msg
, "Authorization");
7919 sipmsg_add_header_now_pos(trans
->msg
, "Authorization", auth
, 5);
7921 resend
= sipmsg_to_string(trans
->msg
);
7922 /* resend request */
7923 sendout_pkt(sip
->gc
, resend
);
7928 if (trans
->callback
) {
7929 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
7930 /* call the callback to process response*/
7931 (trans
->callback
)(sip
, msg
, trans
);
7934 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
7935 transactions_remove(sip
, trans
);
7941 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
7945 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", method
, msg
->response
);
7949 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
7958 /* according to the RFC remove CRLF at the beginning */
7959 while (*cur
== '\r' || *cur
== '\n') {
7962 if (cur
!= conn
->inbuf
) {
7963 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
7964 conn
->inbufused
= strlen(conn
->inbuf
);
7967 /* Received a full Header? */
7968 sip
->processing_input
= TRUE
;
7969 while (sip
->processing_input
&&
7970 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
7971 time_t currtime
= time(NULL
);
7974 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
7976 msg
= sipmsg_parse_header(conn
->inbuf
);
7979 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
7980 if (msg
&& restlen
>= msg
->bodylen
) {
7981 dummy
= g_malloc(msg
->bodylen
+ 1);
7982 memcpy(dummy
, cur
, msg
->bodylen
);
7983 dummy
[msg
->bodylen
] = '\0';
7985 cur
+= msg
->bodylen
;
7986 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
7987 conn
->inbufused
= strlen(conn
->inbuf
);
7990 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
7997 purple_debug_info("sipe", "body:\n%s", msg->body);
8000 // Verify the signature before processing it
8001 if (sip
->registrar
.gssapi_context
) {
8002 struct sipmsg_breakdown msgbd
;
8003 gchar
*signature_input_str
;
8006 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
8007 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
8009 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
8011 if (rspauth
!= NULL
) {
8012 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
8013 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
8014 process_input_message(sip
, msg
);
8016 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
8017 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
8018 sip
->gc
->wants_to_die
= TRUE
;
8020 } else if (msg
->response
== 401) {
8021 purple_connection_error(sip
->gc
, _("Authentication failed"));
8022 sip
->gc
->wants_to_die
= TRUE
;
8024 g_free(signature_input_str
);
8027 sipmsg_breakdown_free(&msgbd
);
8029 process_input_message(sip
, msg
);
8036 static void sipe_udp_process(gpointer data
, gint source
,
8037 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
8039 PurpleConnection
*gc
= data
;
8040 struct sipe_account_data
*sip
= gc
->proto_data
;
8043 static char buffer
[65536];
8044 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
8045 time_t currtime
= time(NULL
);
8048 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
8049 msg
= sipmsg_parse_msg(buffer
);
8050 if (msg
) process_input_message(sip
, msg
);
8054 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
8056 struct sipe_account_data
*sip
= gc
->proto_data
;
8057 PurpleSslConnection
*gsc
= sip
->gsc
;
8059 purple_debug_error("sipe", "%s",debug
);
8060 purple_connection_error(gc
, msg
);
8062 /* Invalidate this connection. Next send will open a new one */
8064 connection_remove(sip
, gsc
->fd
);
8065 purple_ssl_close(gsc
);
8071 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8072 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8074 PurpleConnection
*gc
= data
;
8075 struct sipe_account_data
*sip
;
8076 struct sip_connection
*conn
;
8078 gboolean firstread
= TRUE
;
8080 /* NOTE: This check *IS* necessary */
8081 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
8082 purple_ssl_close(gsc
);
8086 sip
= gc
->proto_data
;
8087 conn
= connection_find(sip
, gsc
->fd
);
8089 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
8090 gc
->wants_to_die
= TRUE
;
8091 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
8095 /* Read all available data from the SSL connection */
8097 /* Increase input buffer size as needed */
8098 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8099 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8100 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8101 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
8104 /* Try to read as much as there is space left in the buffer */
8105 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
8106 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
8108 if (len
< 0 && errno
== EAGAIN
) {
8109 /* Try again later */
8111 } else if (len
< 0) {
8112 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
8114 } else if (firstread
&& (len
== 0)) {
8115 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
8119 conn
->inbufused
+= len
;
8122 /* Equivalence indicates that there is possibly more data to read */
8123 } while (len
== readlen
);
8125 conn
->inbuf
[conn
->inbufused
] = '\0';
8126 process_input(sip
, conn
);
8130 static void sipe_input_cb(gpointer data
, gint source
,
8131 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8133 PurpleConnection
*gc
= data
;
8134 struct sipe_account_data
*sip
= gc
->proto_data
;
8136 struct sip_connection
*conn
= connection_find(sip
, source
);
8138 purple_debug_error("sipe", "Connection not found!\n");
8142 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8143 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8144 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8147 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
8149 if (len
< 0 && errno
== EAGAIN
)
8151 else if (len
<= 0) {
8152 purple_debug_info("sipe", "sipe_input_cb: read error\n");
8153 connection_remove(sip
, source
);
8154 if (sip
->fd
== source
) sip
->fd
= -1;
8158 conn
->inbufused
+= len
;
8159 conn
->inbuf
[conn
->inbufused
] = '\0';
8161 process_input(sip
, conn
);
8164 /* Callback for new connections on incoming TCP port */
8165 static void sipe_newconn_cb(gpointer data
, gint source
,
8166 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8168 PurpleConnection
*gc
= data
;
8169 struct sipe_account_data
*sip
= gc
->proto_data
;
8170 struct sip_connection
*conn
;
8172 int newfd
= accept(source
, NULL
, NULL
);
8174 conn
= connection_create(sip
, newfd
);
8176 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8179 static void login_cb(gpointer data
, gint source
,
8180 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
8182 PurpleConnection
*gc
= data
;
8183 struct sipe_account_data
*sip
;
8184 struct sip_connection
*conn
;
8186 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8194 purple_connection_error(gc
, _("Could not connect"));
8198 sip
= gc
->proto_data
;
8200 sip
->last_keepalive
= time(NULL
);
8202 conn
= connection_create(sip
, source
);
8206 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8209 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8210 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8212 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
8213 if (sip
== NULL
) return;
8218 static guint
sipe_ht_hash_nick(const char *nick
)
8220 char *lc
= g_utf8_strdown(nick
, -1);
8221 guint bucket
= g_str_hash(lc
);
8227 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
8229 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
8232 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
8234 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8236 sip
->listen_data
= NULL
;
8238 if (listenfd
== -1) {
8239 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8245 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
8246 sip
->listenfd
= sip
->fd
;
8248 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
8250 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
8254 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
8255 SIPE_UNUSED_PARAMETER
const char *error_message
)
8257 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8259 sip
->query_data
= NULL
;
8261 if (!hosts
|| !hosts
->data
) {
8262 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
8266 hosts
= g_slist_remove(hosts
, hosts
->data
);
8267 g_free(sip
->serveraddr
);
8268 sip
->serveraddr
= hosts
->data
;
8269 hosts
= g_slist_remove(hosts
, hosts
->data
);
8271 void *tmp
= hosts
->data
;
8272 hosts
= g_slist_remove(hosts
, tmp
);
8273 hosts
= g_slist_remove(hosts
, tmp
);
8277 /* create socket for incoming connections */
8278 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
8279 sipe_udp_host_resolved_listen_cb
, sip
);
8280 if (sip
->listen_data
== NULL
) {
8281 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8286 static const struct sipe_service_data
*current_service
= NULL
;
8288 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
8289 PurpleSslErrorType error
,
8292 PurpleConnection
*gc
= data
;
8293 struct sipe_account_data
*sip
;
8295 /* If the connection is already disconnected, we don't need to do anything else */
8296 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8299 sip
= gc
->proto_data
;
8300 current_service
= sip
->service_data
;
8301 if (current_service
) {
8302 purple_debug_info("sipe", "current_service: transport '%s' service '%s'\n",
8303 current_service
->transport
? current_service
->transport
: "NULL",
8304 current_service
->service
? current_service
->service
: "NULL");
8311 case PURPLE_SSL_CONNECT_FAILED
:
8312 purple_connection_error(gc
, _("Connection failed"));
8314 case PURPLE_SSL_HANDSHAKE_FAILED
:
8315 purple_connection_error(gc
, _("SSL handshake failed"));
8317 case PURPLE_SSL_CERTIFICATE_INVALID
:
8318 purple_connection_error(gc
, _("SSL certificate invalid"));
8324 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
8326 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8327 PurpleProxyConnectData
*connect_data
;
8329 sip
->listen_data
= NULL
;
8331 sip
->listenfd
= listenfd
;
8332 if (sip
->listenfd
== -1) {
8333 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8337 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
8338 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
8339 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
8340 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
8341 sipe_newconn_cb
, sip
->gc
);
8342 purple_debug_info("sipe", "connecting to %s port %d\n",
8343 sip
->realhostname
, sip
->realport
);
8344 /* open tcp connection to the server */
8345 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
8346 sip
->realport
, login_cb
, sip
->gc
);
8348 if (connect_data
== NULL
) {
8349 purple_connection_error(sip
->gc
, _("Could not create socket"));
8353 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
8355 PurpleAccount
*account
= sip
->account
;
8356 PurpleConnection
*gc
= sip
->gc
;
8359 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
8362 sip
->realhostname
= hostname
;
8363 sip
->realport
= port
;
8365 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
8368 /* TODO: is there a good default grow size? */
8369 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
8370 sip
->txbuf
= purple_circ_buffer_new(0);
8372 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
8374 if (!purple_ssl_is_supported()) {
8375 gc
->wants_to_die
= TRUE
;
8376 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
8380 purple_debug_info("sipe", "using SSL\n");
8382 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
8383 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
8384 if (sip
->gsc
== NULL
) {
8385 purple_connection_error(gc
, _("Could not create SSL context"));
8388 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
8390 purple_debug_info("sipe", "using UDP\n");
8392 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
8393 if (sip
->query_data
== NULL
) {
8394 purple_connection_error(gc
, _("Could not resolve hostname"));
8398 purple_debug_info("sipe", "using TCP\n");
8399 /* create socket for incoming connections */
8400 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
8401 sipe_tcp_connect_listen_cb
, sip
);
8402 if (sip
->listen_data
== NULL
) {
8403 purple_connection_error(gc
, _("Could not create listen socket"));
8409 /* Service list for autodection */
8410 static const struct sipe_service_data service_autodetect
[] = {
8411 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8412 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8413 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8414 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8418 /* Service list for SSL/TLS */
8419 static const struct sipe_service_data service_tls
[] = {
8420 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8421 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8425 /* Service list for TCP */
8426 static const struct sipe_service_data service_tcp
[] = {
8427 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8428 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8432 /* Service list for UDP */
8433 static const struct sipe_service_data service_udp
[] = {
8434 { "sip", "udp", SIPE_TRANSPORT_UDP
},
8438 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
8439 static void resolve_next_service(struct sipe_account_data
*sip
,
8440 const struct sipe_service_data
*start
)
8443 sip
->service_data
= start
;
8445 sip
->service_data
++;
8446 if (sip
->service_data
->service
== NULL
) {
8448 /* Try connecting to the SIP hostname directly */
8449 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
8450 if (sip
->auto_transport
) {
8451 // If SSL is supported, default to using it; OCS servers aren't configured
8452 // by default to accept TCP
8453 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
8454 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8455 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
8458 hostname
= g_strdup(sip
->sipdomain
);
8459 create_connection(sip
, hostname
, 0);
8464 /* Try to resolve next service */
8465 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
8466 sip
->service_data
->transport
,
8471 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
8473 struct sipe_account_data
*sip
= data
;
8475 sip
->srv_query_data
= NULL
;
8477 /* find the host to connect to */
8479 gchar
*hostname
= g_strdup(resp
->hostname
);
8480 int port
= resp
->port
;
8481 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
8485 sip
->transport
= sip
->service_data
->type
;
8487 create_connection(sip
, hostname
, port
);
8489 resolve_next_service(sip
, NULL
);
8493 static void sipe_login(PurpleAccount
*account
)
8495 PurpleConnection
*gc
;
8496 struct sipe_account_data
*sip
;
8497 gchar
**signinname_login
, **userserver
;
8498 const char *transport
;
8501 const char *username
= purple_account_get_username(account
);
8502 gc
= purple_account_get_connection(account
);
8504 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
8506 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
8507 gc
->wants_to_die
= TRUE
;
8508 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
8512 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
8513 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
8514 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
8516 sip
->account
= account
;
8517 sip
->reregister_set
= FALSE
;
8518 sip
->reauthenticate_set
= FALSE
;
8519 sip
->subscribed
= FALSE
;
8520 sip
->subscribed_buddies
= FALSE
;
8521 sip
->initial_state_published
= FALSE
;
8523 /* username format: <username>,[<optional login>] */
8524 signinname_login
= g_strsplit(username
, ",", 2);
8525 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
8527 /* ensure that username format is name@domain */
8528 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
8529 g_strfreev(signinname_login
);
8530 gc
->wants_to_die
= TRUE
;
8531 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
8534 sip
->username
= g_strdup(signinname_login
[0]);
8536 /* ensure that email format is name@domain if provided */
8537 email
= purple_account_get_string(sip
->account
, "email", NULL
);
8538 if (!is_empty(email
) &&
8539 (!strchr(email
, '@') || g_str_has_prefix(email
, "@") || g_str_has_suffix(email
, "@")))
8541 gc
->wants_to_die
= TRUE
;
8542 purple_connection_error(gc
, _("Email address should be valid if provided\nExample: user@company.com"));
8545 sip
->email
= !is_empty(email
) ? g_strdup(email
) : g_strdup(sip
->username
);
8547 /* login name specified? */
8548 if (signinname_login
[1] && strlen(signinname_login
[1])) {
8549 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
8550 gboolean has_domain
= domain_user
[1] != NULL
;
8551 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
8552 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
8553 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
8554 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
8555 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
8556 g_strfreev(domain_user
);
8559 userserver
= g_strsplit(signinname_login
[0], "@", 2);
8560 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
8561 purple_connection_set_display_name(gc
, userserver
[0]);
8562 sip
->sipdomain
= g_strdup(userserver
[1]);
8563 g_strfreev(userserver
);
8564 g_strfreev(signinname_login
);
8566 if (strchr(sip
->username
, ' ') != NULL
) {
8567 gc
->wants_to_die
= TRUE
;
8568 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
8572 sip
->password
= g_strdup(purple_connection_get_password(gc
));
8574 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
8575 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8576 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
8577 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8578 g_free
, (GDestroyNotify
)sipe_subscription_free
);
8580 sip
->filetransfers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,g_free
,NULL
);
8582 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
8584 g_free(sip
->status
);
8585 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
8587 sip
->auto_transport
= FALSE
;
8588 transport
= purple_account_get_string(account
, "transport", "auto");
8589 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
8590 if (userserver
[0]) {
8591 /* Use user specified server[:port] */
8595 port
= atoi(userserver
[1]);
8597 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
8598 userserver
[0], port
);
8600 if (sipe_strequal(transport
, "auto")) {
8601 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8602 } else if (sipe_strequal(transport
, "tls")) {
8603 sip
->transport
= SIPE_TRANSPORT_TLS
;
8604 } else if (sipe_strequal(transport
, "tcp")) {
8605 sip
->transport
= SIPE_TRANSPORT_TCP
;
8607 sip
->transport
= SIPE_TRANSPORT_UDP
;
8610 create_connection(sip
, g_strdup(userserver
[0]), port
);
8612 /* Server auto-discovery */
8613 if (sipe_strequal(transport
, "auto")) {
8614 sip
->auto_transport
= TRUE
;
8615 if (current_service
&& current_service
->transport
!= NULL
&& current_service
->service
!= NULL
){
8617 resolve_next_service(sip
, current_service
);
8619 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
8621 } else if (sipe_strequal(transport
, "tls")) {
8622 resolve_next_service(sip
, service_tls
);
8623 } else if (sipe_strequal(transport
, "tcp")) {
8624 resolve_next_service(sip
, service_tcp
);
8626 resolve_next_service(sip
, service_udp
);
8629 g_strfreev(userserver
);
8632 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
8634 connection_free_all(sip
);
8639 if (sip
->query_data
!= NULL
)
8640 purple_dnsquery_destroy(sip
->query_data
);
8641 sip
->query_data
= NULL
;
8643 if (sip
->srv_query_data
!= NULL
)
8644 purple_srv_cancel(sip
->srv_query_data
);
8645 sip
->srv_query_data
= NULL
;
8647 if (sip
->listen_data
!= NULL
)
8648 purple_network_listen_cancel(sip
->listen_data
);
8649 sip
->listen_data
= NULL
;
8651 if (sip
->gsc
!= NULL
)
8652 purple_ssl_close(sip
->gsc
);
8655 sipe_auth_free(&sip
->registrar
);
8656 sipe_auth_free(&sip
->proxy
);
8659 purple_circ_buffer_destroy(sip
->txbuf
);
8662 g_free(sip
->realhostname
);
8663 sip
->realhostname
= NULL
;
8665 g_free(sip
->server_version
);
8666 sip
->server_version
= NULL
;
8669 purple_input_remove(sip
->listenpa
);
8671 if (sip
->tx_handler
)
8672 purple_input_remove(sip
->tx_handler
);
8673 sip
->tx_handler
= 0;
8674 if (sip
->resendtimeout
)
8675 purple_timeout_remove(sip
->resendtimeout
);
8676 sip
->resendtimeout
= 0;
8677 if (sip
->timeouts
) {
8678 GSList
*entry
= sip
->timeouts
;
8680 struct scheduled_action
*sched_action
= entry
->data
;
8681 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
8682 purple_timeout_remove(sched_action
->timeout_handler
);
8683 if (sched_action
->destroy
) {
8684 (*sched_action
->destroy
)(sched_action
->payload
);
8686 g_free(sched_action
->name
);
8687 g_free(sched_action
);
8688 entry
= entry
->next
;
8691 g_slist_free(sip
->timeouts
);
8693 if (sip
->allow_events
) {
8694 GSList
*entry
= sip
->allow_events
;
8696 g_free(entry
->data
);
8697 entry
= entry
->next
;
8700 g_slist_free(sip
->allow_events
);
8702 if (sip
->containers
) {
8703 GSList
*entry
= sip
->containers
;
8705 free_container((struct sipe_container
*)entry
->data
);
8706 entry
= entry
->next
;
8709 g_slist_free(sip
->containers
);
8712 g_free(sip
->contact
);
8713 sip
->contact
= NULL
;
8715 g_free(sip
->regcallid
);
8716 sip
->regcallid
= NULL
;
8718 if (sip
->serveraddr
)
8719 g_free(sip
->serveraddr
);
8720 sip
->serveraddr
= NULL
;
8722 if (sip
->focus_factory_uri
)
8723 g_free(sip
->focus_factory_uri
);
8724 sip
->focus_factory_uri
= NULL
;
8727 sip
->processing_input
= FALSE
;
8730 sipe_ews_free(sip
->ews
);
8736 * A callback for g_hash_table_foreach_remove
8738 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
8739 SIPE_UNUSED_PARAMETER gpointer user_data
)
8741 sipe_free_buddy((struct sipe_buddy
*) buddy
);
8743 /* We must return TRUE as the key/value have already been deleted */
8747 static void sipe_close(PurpleConnection
*gc
)
8749 struct sipe_account_data
*sip
= gc
->proto_data
;
8752 /* leave all conversations */
8753 sipe_session_close_all(sip
);
8754 sipe_session_remove_all(sip
);
8757 sip_csta_close(sip
);
8760 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
8761 /* unsubscribe all */
8762 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
8765 do_register_exp(sip
, 0);
8768 sipe_connection_cleanup(sip
);
8769 g_free(sip
->sipdomain
);
8770 g_free(sip
->username
);
8772 g_free(sip
->password
);
8773 g_free(sip
->authdomain
);
8774 g_free(sip
->authuser
);
8775 g_free(sip
->status
);
8777 g_free(sip
->user_states
);
8779 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
8780 g_hash_table_destroy(sip
->buddies
);
8781 g_hash_table_destroy(sip
->our_publications
);
8782 g_hash_table_destroy(sip
->user_state_publications
);
8783 g_hash_table_destroy(sip
->subscriptions
);
8784 g_hash_table_destroy(sip
->filetransfers
);
8787 GSList
*entry
= sip
->groups
;
8789 struct sipe_group
*group
= entry
->data
;
8790 g_free(group
->name
);
8792 entry
= entry
->next
;
8795 g_slist_free(sip
->groups
);
8797 if (sip
->our_publication_keys
) {
8798 GSList
*entry
= sip
->our_publication_keys
;
8800 g_free(entry
->data
);
8801 entry
= entry
->next
;
8804 g_slist_free(sip
->our_publication_keys
);
8806 while (sip
->transactions
)
8807 transactions_remove(sip
, sip
->transactions
->data
);
8809 g_free(gc
->proto_data
);
8810 gc
->proto_data
= NULL
;
8813 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
8814 SIPE_UNUSED_PARAMETER
void *user_data
)
8816 PurpleAccount
*acct
= purple_connection_get_account(gc
);
8817 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
8818 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
8820 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
8821 purple_conversation_present(conv
);
8825 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
8826 SIPE_UNUSED_PARAMETER
void *user_data
)
8829 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
8830 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
8833 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
8834 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
8836 PurpleNotifySearchResults
*results
;
8837 PurpleNotifySearchColumn
*column
;
8838 xmlnode
*searchResults
;
8840 int match_count
= 0;
8841 gboolean more
= FALSE
;
8844 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
8846 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
8847 if (!searchResults
) {
8848 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
8852 results
= purple_notify_searchresults_new();
8854 if (results
== NULL
) {
8855 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
8856 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
8858 xmlnode_free(searchResults
);
8862 column
= purple_notify_searchresults_column_new(_("User name"));
8863 purple_notify_searchresults_column_add(results
, column
);
8865 column
= purple_notify_searchresults_column_new(_("Name"));
8866 purple_notify_searchresults_column_add(results
, column
);
8868 column
= purple_notify_searchresults_column_new(_("Company"));
8869 purple_notify_searchresults_column_add(results
, column
);
8871 column
= purple_notify_searchresults_column_new(_("Country"));
8872 purple_notify_searchresults_column_add(results
, column
);
8874 column
= purple_notify_searchresults_column_new(_("Email"));
8875 purple_notify_searchresults_column_add(results
, column
);
8877 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
8880 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
8881 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
8882 g_strfreev(uri_parts
);
8884 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
8885 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
8886 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
8887 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
8889 purple_notify_searchresults_row_add(results
, row
);
8893 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
8894 char *data
= xmlnode_get_data(mrow
);
8895 more
= (g_strcasecmp(data
, "true") == 0);
8899 secondary
= g_strdup_printf(
8900 dngettext(PACKAGE_NAME
,
8901 "Found %d contact%s:",
8902 "Found %d contacts%s:", match_count
),
8903 match_count
, more
? _(" (more matched your query)") : "");
8905 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
8906 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
8907 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
8910 xmlnode_free(searchResults
);
8914 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
8916 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
8917 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
8923 PurpleRequestField
*field
= entries
->data
;
8924 const char *id
= purple_request_field_get_id(field
);
8925 const char *value
= purple_request_field_string_get_value(field
);
8927 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
8929 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
8930 } while ((entries
= g_list_next(entries
)) != NULL
);
8934 struct sipe_account_data
*sip
= gc
->proto_data
;
8935 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
8936 gchar
*query
= g_strjoinv(NULL
, attrs
);
8937 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
8938 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
8939 send_soap_request_with_cb(sip
, domain_uri
, body
,
8940 (TransCallback
) process_search_contact_response
, NULL
);
8949 static void sipe_show_find_contact(PurplePluginAction
*action
)
8951 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8952 PurpleRequestFields
*fields
;
8953 PurpleRequestFieldGroup
*group
;
8954 PurpleRequestField
*field
;
8956 fields
= purple_request_fields_new();
8957 group
= purple_request_field_group_new(NULL
);
8958 purple_request_fields_add_group(fields
, group
);
8960 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
8961 purple_request_field_group_add_field(group
, field
);
8962 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
8963 purple_request_field_group_add_field(group
, field
);
8964 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
8965 purple_request_field_group_add_field(group
, field
);
8966 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
8967 purple_request_field_group_add_field(group
, field
);
8969 purple_request_fields(gc
,
8971 _("Search for a contact"),
8972 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
8974 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
8976 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
8979 static void sipe_show_about_plugin(PurplePluginAction
*action
)
8981 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8982 char *tmp
= g_strdup_printf(
8984 * Non-translatable parts, like markup, are hard-coded
8985 * into the format string. This requires more translatable
8986 * texts but it makes the translations less error prone.
8988 "<b><font size=\"+1\">SIPE " PACKAGE_VERSION
" </font></b><br/>"
8991 "<li> - MS Office Communications Server 2007 R2</li><br/>"
8992 "<li> - MS Office Communications Server 2007</li><br/>"
8993 "<li> - MS Live Communications Server 2005</li><br/>"
8994 "<li> - MS Live Communications Server 2003</li><br/>"
8995 "<li> - Reuters Messaging</li><br/>"
8997 /* 2 */ "%s: <a href=\"" PACKAGE_URL
"\">" PACKAGE_URL
"</a><br/>"
8998 /* 3,4 */ "%s: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">%s</a><br/>"
8999 /* 5,6 */ "%s: <a href=\"" PACKAGE_BUGREPORT
"\">%s</a><br/>"
9000 /* 7 */ "%s: <a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a><br/>"
9001 /* 8 */ "%s: GPLv2+<br/>"
9005 " - Reuters Messaging network<br/>"
9006 " - Deutsche Bank<br/>"
9007 " - Merrill Lynch<br/>"
9016 " - Alcatel-Lucent<br/>"
9019 /* 10,11 */ "%s<a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a>%s.<br/>"
9021 /* 12 */ "<b>%s:</b><br/>"
9022 " - Anibal Avelar<br/>"
9023 " - Gabriel Burt<br/>"
9024 " - Stefan Becker<br/>"
9026 " - Jakub Adam<br/>"
9027 " - Tomáš Hrabčík<br/>"
9031 /* The next 13 texts make up the SIPE about note text */
9032 /* About note, part 1/13: introduction */
9033 _("A third-party plugin implementing extended version of SIP/SIMPLE used by various products"),
9034 /* About note, part 2/13: home page URL (label) */
9036 /* About note, part 3/13: support forum URL (label) */
9038 /* About note, part 4/13: support forum name (hyperlink text) */
9040 /* About note, part 5/13: bug tracker URL (label) */
9041 _("Report Problems"),
9042 /* About note, part 6/13: bug tracker URL (hyperlink text) */
9044 /* About note, part 7/13: translation service URL (label) */
9046 /* About note, part 8/13: license type (label) */
9048 /* About note, part 9/13: known users */
9049 _("We support users in such organizations as"),
9050 /* About note, part 10/13: translation request, text before Transifex.net URL */
9051 /* append a space if text is not empty */
9052 _("Please help us to translate SIPE to your native language here at "),
9053 /* About note, part 11/13: translation request, text after Transifex.net URL */
9054 /* start with a space if text is not empty */
9055 _(" using convenient web interface"),
9056 /* About note, part 12/13: author list (header) */
9058 /* About note, part 13/13: Localization credit */
9059 /* PLEASE NOTE: do *NOT* simply translate the english original */
9060 /* but write something similar to the following sentence: */
9061 /* "Localization for <language name> (<language code>): <name>" */
9062 _("Original texts in English (en): SIPE developers")
9064 purple_notify_formatted(gc
, NULL
, " ", NULL
, tmp
, NULL
, NULL
);
9068 static void sipe_republish_calendar(PurplePluginAction
*action
)
9070 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9071 struct sipe_account_data
*sip
= gc
->proto_data
;
9073 sipe_update_calendar(sip
);
9076 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
9080 struct sipe_publication
*publication
= value
;
9082 g_string_append_printf( str
,
9083 SIPE_PUB_XML_PUBLICATION_CLEAR
,
9084 publication
->category
,
9085 publication
->instance
,
9086 publication
->container
,
9087 publication
->version
,
9091 static void sipe_reset_status(PurplePluginAction
*action
)
9093 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9094 struct sipe_account_data
*sip
= gc
->proto_data
;
9096 if (sip
->ocs2007
) /* 2007+ */
9098 GString
* str
= g_string_new(NULL
);
9099 gchar
*publications
;
9101 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
9102 purple_debug_info("sipe", "sipe_reset_status: no userState publications, exiting.\n");
9106 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
9107 publications
= g_string_free(str
, FALSE
);
9109 send_presence_publish(sip
, publications
);
9110 g_free(publications
);
9114 send_presence_soap0(sip
, FALSE
, TRUE
);
9118 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
9121 PurpleConnection
*gc
= (PurpleConnection
*)context
;
9122 struct sipe_account_data
*sip
= gc
->proto_data
;
9124 PurplePluginAction
*act
;
9125 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
9127 act
= purple_plugin_action_new(_("About SIPE plugin..."), sipe_show_about_plugin
);
9128 menu
= g_list_prepend(menu
, act
);
9130 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
9131 menu
= g_list_prepend(menu
, act
);
9133 if (sipe_strequal(calendar
, "EXCH")) {
9134 act
= purple_plugin_action_new(_("Republish Calendar"), sipe_republish_calendar
);
9135 menu
= g_list_prepend(menu
, act
);
9138 act
= purple_plugin_action_new(_("Reset status"), sipe_reset_status
);
9139 menu
= g_list_prepend(menu
, act
);
9141 menu
= g_list_reverse(menu
);
9146 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
9150 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9156 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9162 static char *sipe_status_text(PurpleBuddy
*buddy
)
9164 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9165 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9166 const char *status_id
= purple_status_get_id(status
);
9167 struct sipe_account_data
*sip
= (struct sipe_account_data
*)buddy
->account
->gc
->proto_data
;
9168 struct sipe_buddy
*sbuddy
;
9171 if (!sip
) return NULL
; /* happens on pidgin exit */
9173 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9175 const char *activity_str
= sbuddy
->activity
?
9177 sipe_strequal(status_id
, SIPE_STATUS_ID_BUSY
) || sipe_strequal(status_id
, SIPE_STATUS_ID_BRB
) ?
9178 purple_status_get_name(status
) : NULL
;
9180 if (activity_str
&& sbuddy
->note
)
9182 text
= g_strdup_printf("%s - <i>%s</i>", activity_str
, sbuddy
->note
);
9184 else if (activity_str
)
9186 text
= g_strdup(activity_str
);
9188 else if (sbuddy
->note
)
9190 text
= g_strdup_printf("<i>%s</i>", sbuddy
->note
);
9197 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
9199 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9200 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9201 struct sipe_account_data
*sip
;
9202 struct sipe_buddy
*sbuddy
;
9204 gboolean is_oof_note
= FALSE
;
9205 char *activity
= NULL
;
9206 char *calendar
= NULL
;
9207 char *meeting_subject
= NULL
;
9208 char *meeting_location
= NULL
;
9210 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
9211 if (sip
) //happens on pidgin exit
9213 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9216 note
= sbuddy
->note
;
9217 is_oof_note
= sbuddy
->is_oof_note
;
9218 activity
= sbuddy
->activity
;
9219 calendar
= sipe_cal_get_description(sbuddy
);
9220 meeting_subject
= sbuddy
->meeting_subject
;
9221 meeting_location
= sbuddy
->meeting_location
;
9226 if (purple_presence_is_online(presence
))
9228 const char *status_str
= activity
? activity
: purple_status_get_name(status
);
9230 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
9232 if (purple_presence_is_online(presence
) &&
9233 !is_empty(calendar
))
9235 purple_notify_user_info_add_pair(user_info
, _("Calendar"), calendar
);
9238 if (!is_empty(meeting_location
))
9240 purple_notify_user_info_add_pair(user_info
, _("Meeting in"), meeting_location
);
9242 if (!is_empty(meeting_subject
))
9244 purple_notify_user_info_add_pair(user_info
, _("Meeting about"), meeting_subject
);
9249 char *tmp
= g_strdup_printf("<i>%s</i>", note
);
9250 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, note
);
9252 purple_notify_user_info_add_pair(user_info
, is_oof_note
? _("Out of office note") : _("Note"), tmp
);
9258 #if PURPLE_VERSION_CHECK(2,5,0)
9260 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
9263 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
9264 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
9269 static PurpleBuddy
*
9270 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
9273 const gchar
*server_alias
, *email
;
9274 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
9276 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
9278 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
9280 server_alias
= purple_buddy_get_server_alias(buddy
);
9282 purple_blist_server_alias_buddy(clone
, server_alias
);
9285 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9287 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
9290 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
9292 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
9297 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
9299 PurpleBuddy
*buddy
, *b
;
9300 PurpleConnection
*gc
;
9301 PurpleGroup
* group
= purple_find_group(group_name
);
9303 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
9305 buddy
= (PurpleBuddy
*)node
;
9307 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
9308 gc
= purple_account_get_connection(buddy
->account
);
9310 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
9312 purple_blist_add_buddy_clone(group
, buddy
);
9315 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
9319 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
9321 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9323 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
9325 /* 2007+ conference */
9328 sipe_conf_add(sip
, buddy
->name
);
9330 else /* 2005- multiparty chat */
9332 gchar
*self
= sip_uri_self(sip
);
9333 struct sip_session
*session
;
9335 session
= sipe_session_add_chat(sip
);
9336 session
->chat_title
= sipe_chat_get_name(session
->callid
);
9337 session
->roster_manager
= g_strdup(self
);
9339 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
9340 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
9341 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
9342 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
9349 sipe_is_election_finished(struct sip_session
*session
)
9351 gboolean res
= TRUE
;
9353 SIPE_DIALOG_FOREACH
{
9354 if (dialog
->election_vote
== 0) {
9358 } SIPE_DIALOG_FOREACH_END
;
9361 session
->is_voting_in_progress
= FALSE
;
9367 sipe_election_start(struct sipe_account_data
*sip
,
9368 struct sip_session
*session
)
9370 int election_timeout
;
9372 if (session
->is_voting_in_progress
) {
9373 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
9376 session
->is_voting_in_progress
= TRUE
;
9378 session
->bid
= rand();
9380 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
9382 SIPE_DIALOG_FOREACH
{
9383 /* reset election_vote for each chat participant */
9384 dialog
->election_vote
= 0;
9386 /* send RequestRM to each chat participant*/
9387 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
9388 } SIPE_DIALOG_FOREACH_END
;
9390 election_timeout
= 15; /* sec */
9391 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
9395 * @param who a URI to whom to invite to chat
9398 sipe_invite_to_chat(struct sipe_account_data
*sip
,
9399 struct sip_session
*session
,
9403 if (session
->focus_uri
)
9405 sipe_invite_conf(sip
, session
, who
);
9407 else /* a multi-party chat */
9409 gchar
*self
= sip_uri_self(sip
);
9410 if (session
->roster_manager
) {
9411 if (sipe_strcase_equal(session
->roster_manager
, self
)) {
9412 sipe_invite(sip
, session
, who
, NULL
, NULL
, NULL
, FALSE
);
9414 sipe_refer(sip
, session
, who
);
9417 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
9419 session
->pending_invite_queue
= slist_insert_unique_sorted(
9420 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
9422 sipe_election_start(sip
, session
);
9429 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
9430 struct sip_session
*session
)
9433 GSList
*entry
= session
->pending_invite_queue
;
9436 invitee
= entry
->data
;
9437 sipe_invite_to_chat(sip
, session
, invitee
);
9438 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
9444 sipe_election_result(struct sipe_account_data
*sip
,
9447 struct sip_session
*session
= (struct sip_session
*)sess
;
9449 gboolean has_won
= TRUE
;
9451 if (session
->roster_manager
) {
9452 purple_debug_info("sipe",
9453 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
9457 session
->is_voting_in_progress
= FALSE
;
9459 SIPE_DIALOG_FOREACH
{
9460 if (dialog
->election_vote
< 0) {
9462 rival
= dialog
->with
;
9465 } SIPE_DIALOG_FOREACH_END
;
9468 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
9470 session
->roster_manager
= sip_uri_self(sip
);
9472 SIPE_DIALOG_FOREACH
{
9473 /* send SetRM to each chat participant*/
9474 sipe_send_election_set_rm(sip
, dialog
);
9475 } SIPE_DIALOG_FOREACH_END
;
9477 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
9481 sipe_process_pending_invite_queue(sip
, session
);
9485 * For 2007+ conference only.
9488 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9490 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9491 struct sip_session
*session
;
9493 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
9494 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
9496 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9498 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
9502 * For 2007+ conference only.
9505 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9507 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9508 struct sip_session
*session
;
9510 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
9511 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
9513 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9515 sipe_conf_delete_user(sip
, session
, buddy
->name
);
9519 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
9521 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9522 struct sip_session
*session
;
9524 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
9525 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
9527 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9529 sipe_invite_to_chat(sip
, session
, buddy
->name
);
9533 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
9535 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9537 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
9539 char *tel_uri
= sip_to_tel_uri(phone
);
9541 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
9542 sip_csta_make_call(sip
, tel_uri
);
9549 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
9552 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
9554 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9557 char *mailto
= g_strdup_printf("mailto:%s", email
);
9558 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
9562 char *const parmList
[] = {"xdg-email", mailto
, NULL
};
9563 if ((pid
= fork()) == -1)
9565 purple_debug_info("sipe", "fork() error\n");
9569 execvp(parmList
[0], parmList
);
9570 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
9578 //@TODO resolve env variable %WINDIR% first
9579 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
9582 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
9591 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
9596 * A menu which appear when right-clicking on buddy in contact list.
9599 sipe_buddy_menu(PurpleBuddy
*buddy
)
9601 PurpleBlistNode
*g_node
;
9602 PurpleGroup
*group
, *gr_parent
;
9603 PurpleMenuAction
*act
;
9605 GList
*menu_groups
= NULL
;
9606 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9609 const char *phone_disp_str
;
9610 gchar
*self
= sip_uri_self(sip
);
9612 SIPE_SESSION_FOREACH
{
9613 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
9615 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
9617 PurpleConvChatBuddyFlags flags
;
9618 PurpleConvChatBuddyFlags flags_us
;
9620 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
9621 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9622 if (session
->focus_uri
9623 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
9624 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9626 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
9627 act
= purple_menu_action_new(label
,
9628 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
9629 session
->chat_title
, NULL
);
9631 menu
= g_list_prepend(menu
, act
);
9634 if (session
->focus_uri
9635 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9637 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
9638 act
= purple_menu_action_new(label
,
9639 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
9640 session
->chat_title
, NULL
);
9642 menu
= g_list_prepend(menu
, act
);
9647 if (!session
->focus_uri
9648 || (session
->focus_uri
&& !session
->locked
))
9650 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
9651 act
= purple_menu_action_new(label
,
9652 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
9653 session
->chat_title
, NULL
);
9655 menu
= g_list_prepend(menu
, act
);
9659 } SIPE_SESSION_FOREACH_END
;
9661 act
= purple_menu_action_new(_("New chat"),
9662 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
9664 menu
= g_list_prepend(menu
, act
);
9666 if (sip
->csta
&& !sip
->csta
->line_status
) {
9669 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
9670 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
9672 gchar
*label
= g_strdup_printf(_("Work %s"),
9673 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9674 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9678 menu
= g_list_prepend(menu
, act
);
9682 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
9683 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
9685 gchar
*label
= g_strdup_printf(_("Mobile %s"),
9686 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9687 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9691 menu
= g_list_prepend(menu
, act
);
9695 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
9696 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
9698 gchar
*label
= g_strdup_printf(_("Home %s"),
9699 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9700 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9704 menu
= g_list_prepend(menu
, act
);
9708 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
9709 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
9711 gchar
*label
= g_strdup_printf(_("Other %s"),
9712 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9713 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9717 menu
= g_list_prepend(menu
, act
);
9721 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
9722 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
9724 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
9725 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9726 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9730 menu
= g_list_prepend(menu
, act
);
9734 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9736 act
= purple_menu_action_new(_("Send email..."),
9737 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
9739 menu
= g_list_prepend(menu
, act
);
9742 gr_parent
= purple_buddy_get_group(buddy
);
9743 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
9744 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
9747 group
= (PurpleGroup
*)g_node
;
9748 if (group
== gr_parent
)
9751 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
9754 act
= purple_menu_action_new(purple_group_get_name(group
),
9755 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
9757 menu_groups
= g_list_prepend(menu_groups
, act
);
9759 menu_groups
= g_list_reverse(menu_groups
);
9761 act
= purple_menu_action_new(_("Copy to"),
9764 menu
= g_list_prepend(menu
, act
);
9765 menu
= g_list_reverse(menu
);
9772 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
9774 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9775 struct sip_session
*session
;
9777 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9778 sipe_conf_modify_conference_lock(sip
, session
, locked
);
9782 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
9784 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
9785 sipe_conf_modify_lock(chat
, FALSE
);
9789 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
9791 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
9792 sipe_conf_modify_lock(chat
, TRUE
);
9796 sipe_chat_menu(PurpleChat
*chat
)
9798 PurpleMenuAction
*act
;
9799 PurpleConvChatBuddyFlags flags_us
;
9801 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9802 struct sip_session
*session
;
9805 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9806 if (!session
) return NULL
;
9808 self
= sip_uri_self(sip
);
9809 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9811 if (session
->focus_uri
9812 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9814 if (session
->locked
) {
9815 act
= purple_menu_action_new(_("Unlock"),
9816 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
9818 menu
= g_list_prepend(menu
, act
);
9820 act
= purple_menu_action_new(_("Lock"),
9821 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
9823 menu
= g_list_prepend(menu
, act
);
9827 menu
= g_list_reverse(menu
);
9834 sipe_blist_node_menu(PurpleBlistNode
*node
)
9836 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
9837 return sipe_buddy_menu((PurpleBuddy
*) node
);
9838 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
9839 return sipe_chat_menu((PurpleChat
*)node
);
9846 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
9848 char *uri
= trans
->payload
->data
;
9850 PurpleNotifyUserInfo
*info
;
9851 PurpleBuddy
*pbuddy
= NULL
;
9852 struct sipe_buddy
*sbuddy
;
9853 const char *alias
= NULL
;
9854 char *device_name
= NULL
;
9855 char *server_alias
= NULL
;
9856 char *phone_number
= NULL
;
9859 char *first_name
= NULL
;
9860 char *last_name
= NULL
;
9862 if (!sip
) return FALSE
;
9864 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
9866 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
9867 alias
= purple_buddy_get_local_alias(pbuddy
);
9869 //will query buddy UA's capabilities and send answer to log
9870 sipe_options_request(sip
, uri
);
9872 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
9874 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
9877 info
= purple_notify_user_info_new();
9879 if (msg
->response
!= 200) {
9880 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
9882 xmlnode
*searchResults
;
9885 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
9886 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
9887 if (!searchResults
) {
9888 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
9889 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
9891 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
9892 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
9893 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
9895 /* For 2007 system we will take this from ContactCard -
9896 * it has cleaner tel: URIs at least
9898 if (!sip
->ocs2007
) {
9899 char *tel_uri
= sip_to_tel_uri(phone_number
);
9900 /* trims its parameters, so call first */
9901 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
9902 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
9903 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
9904 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
9908 if (server_alias
&& strlen(server_alias
) > 0) {
9909 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9911 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
9912 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
9914 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
9915 purple_notify_user_info_add_pair(info
, _("Office"), value
);
9917 if (phone_number
&& strlen(phone_number
) > 0) {
9918 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
9920 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
9921 purple_notify_user_info_add_pair(info
, _("Company"), value
);
9923 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
9924 purple_notify_user_info_add_pair(info
, _("City"), value
);
9926 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
9927 purple_notify_user_info_add_pair(info
, _("State"), value
);
9929 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
9930 purple_notify_user_info_add_pair(info
, _("Country"), value
);
9932 if (email
&& strlen(email
) > 0) {
9933 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9937 xmlnode_free(searchResults
);
9940 purple_notify_user_info_add_section_break(info
);
9942 if (is_empty(server_alias
)) {
9943 g_free(server_alias
);
9944 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
9946 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9950 /* present alias if it differs from server alias */
9951 if (alias
&& !sipe_strequal(alias
, server_alias
))
9953 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
9956 if (is_empty(email
)) {
9958 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
9960 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9964 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
9966 purple_notify_user_info_add_pair(info
, _("Site"), site
);
9969 sipe_get_first_last_names(sip
, uri
, &first_name
, &last_name
);
9970 if (first_name
&& last_name
) {
9971 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
9973 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
9980 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
9983 /* show a buddy's user info in a nice dialog box */
9984 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
9985 uri
, /* buddy's URI */
9987 NULL
, /* callback called when dialog closed */
9988 NULL
); /* userdata for callback */
9990 g_free(phone_number
);
9991 g_free(server_alias
);
9993 g_free(device_name
);
9999 * AD search first, LDAP based
10001 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
10003 struct sipe_account_data
*sip
= gc
->proto_data
;
10004 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
10005 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
10006 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
10007 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
10009 payload
->destroy
= g_free
;
10010 payload
->data
= g_strdup(username
);
10012 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
10013 send_soap_request_with_cb(sip
, domain_uri
, body
,
10014 (TransCallback
) process_get_info_response
, payload
);
10015 g_free(domain_uri
);
10020 PurplePluginProtocolInfo prpl_info
=
10022 OPT_PROTO_CHAT_TOPIC
,
10023 NULL
, /* user_splits */
10024 NULL
, /* protocol_options */
10025 NO_BUDDY_ICONS
, /* icon_spec */
10026 sipe_list_icon
, /* list_icon */
10027 NULL
, /* list_emblems */
10028 sipe_status_text
, /* status_text */
10029 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
10030 sipe_status_types
, /* away_states */
10031 sipe_blist_node_menu
, /* blist_node_menu */
10032 NULL
, /* chat_info */
10033 NULL
, /* chat_info_defaults */
10034 sipe_login
, /* login */
10035 sipe_close
, /* close */
10036 sipe_im_send
, /* send_im */
10037 NULL
, /* set_info */ // TODO maybe
10038 sipe_send_typing
, /* send_typing */
10039 sipe_get_info
, /* get_info */
10040 sipe_set_status
, /* set_status */
10041 sipe_set_idle
, /* set_idle */
10042 NULL
, /* change_passwd */
10043 sipe_add_buddy
, /* add_buddy */
10044 NULL
, /* add_buddies */
10045 sipe_remove_buddy
, /* remove_buddy */
10046 NULL
, /* remove_buddies */
10047 sipe_add_permit
, /* add_permit */
10048 sipe_add_deny
, /* add_deny */
10049 sipe_add_deny
, /* rem_permit */
10050 sipe_add_permit
, /* rem_deny */
10051 dummy_permit_deny
, /* set_permit_deny */
10052 NULL
, /* join_chat */
10053 NULL
, /* reject_chat */
10054 NULL
, /* get_chat_name */
10055 sipe_chat_invite
, /* chat_invite */
10056 sipe_chat_leave
, /* chat_leave */
10057 NULL
, /* chat_whisper */
10058 sipe_chat_send
, /* chat_send */
10059 sipe_keep_alive
, /* keepalive */
10060 NULL
, /* register_user */
10061 NULL
, /* get_cb_info */ // deprecated
10062 NULL
, /* get_cb_away */ // deprecated
10063 sipe_alias_buddy
, /* alias_buddy */
10064 sipe_group_buddy
, /* group_buddy */
10065 sipe_rename_group
, /* rename_group */
10066 NULL
, /* buddy_free */
10067 sipe_convo_closed
, /* convo_closed */
10068 purple_normalize_nocase
, /* normalize */
10069 NULL
, /* set_buddy_icon */
10070 sipe_remove_group
, /* remove_group */
10071 NULL
, /* get_cb_real_name */ // TODO?
10072 NULL
, /* set_chat_topic */
10073 NULL
, /* find_blist_chat */
10074 NULL
, /* roomlist_get_list */
10075 NULL
, /* roomlist_cancel */
10076 NULL
, /* roomlist_expand_category */
10077 NULL
, /* can_receive_file */
10078 sipe_ft_send_file
, /* send_file */
10079 sipe_ft_new_xfer
, /* new_xfer */
10080 NULL
, /* offline_message */
10081 NULL
, /* whiteboard_prpl_ops */
10082 sipe_send_raw
, /* send_raw */
10083 NULL
, /* roomlist_room_serialize */
10084 NULL
, /* unregister_user */
10085 NULL
, /* send_attention */
10086 NULL
, /* get_attention_types */
10087 #if !PURPLE_VERSION_CHECK(2,5,0)
10088 /* Backward compatibility when compiling against 2.4.x API */
10089 (void (*)(void)) /* _purple_reserved4 */
10091 sizeof(PurplePluginProtocolInfo
), /* struct_size */
10092 #if PURPLE_VERSION_CHECK(2,5,0)
10093 sipe_get_account_text_table
, /* get_account_text_table */
10094 #if PURPLE_VERSION_CHECK(2,6,0)
10095 NULL
, /* initiate_media */
10096 NULL
, /* get_media_caps */
10102 PurplePluginInfo info
= {
10103 PURPLE_PLUGIN_MAGIC
,
10104 PURPLE_MAJOR_VERSION
,
10105 PURPLE_MINOR_VERSION
,
10106 PURPLE_PLUGIN_PROTOCOL
, /**< type */
10107 NULL
, /**< ui_requirement */
10109 NULL
, /**< dependencies */
10110 PURPLE_PRIORITY_DEFAULT
, /**< priority */
10111 "prpl-sipe", /**< id */
10112 "Office Communicator", /**< name */
10113 PACKAGE_VERSION
, /**< version */
10114 "Microsoft Office Communicator Protocol Plugin", /**< summary */
10115 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
10116 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
10117 "Anibal Avelar <avelar@gmail.com>, " /**< author */
10118 "Gabriel Burt <gburt@novell.com>, " /**< author */
10119 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
10120 "pier11 <pier11@operamail.com>", /**< author */
10121 PACKAGE_URL
, /**< homepage */
10122 sipe_plugin_load
, /**< load */
10123 sipe_plugin_unload
, /**< unload */
10124 sipe_plugin_destroy
, /**< destroy */
10125 NULL
, /**< ui_info */
10126 &prpl_info
, /**< extra_info */
10135 void sipe_core_init(void)
10141 SIPE_DEBUG_INFO("bindtextdomain = %s",
10142 bindtextdomain(PACKAGE_NAME
, LOCALEDIR
));
10143 SIPE_DEBUG_INFO("bind_textdomain_codeset = %s",
10144 bind_textdomain_codeset(PACKAGE_NAME
, "UTF-8"));
10145 textdomain(PACKAGE_NAME
);
10149 void sipe_core_destroy(void)
10157 c-file-style: "bsd"
10158 indent-tabs-mode: t