6 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
7 * Copyright (C) 2009 pier11 <pier11@operamail.com>
8 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
9 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
10 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
13 * Thanks to Google's Summer of Code Program and the helpful mentors
16 * Session-based SIP MESSAGE documentation:
17 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #include <sys/socket.h>
36 #include <sys/ioctl.h>
37 #include <sys/types.h>
38 #include <netinet/in.h>
44 #define _LIBC_INTERNAL_
57 #include "accountopt.h"
59 #include "conversation.h"
73 #include "sipe-chat.h"
74 #include "sipe-conf.h"
76 #include "sipe-dialog.h"
78 #include "sipe-session.h"
79 #include "sipe-utils.h"
81 #include "sipe-sign.h"
85 /* Backward compatibility when compiling against 2.4.x API */
86 #if !PURPLE_VERSION_CHECK(2,5,0)
87 #define PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY 0x0100
90 /* Keep in sync with sipe_transport_type! */
91 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
92 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
94 /* Status identifiers (see also: sipe_status_types()) */
95 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
96 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
97 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
98 /* PURPLE_STATUS_UNAVAILABLE: */
99 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
100 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
101 #define SIPE_STATUS_ID_ONPHONE "on-the-phone" /* On The Phone */
102 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
103 /* PURPLE_STATUS_AWAY: */
104 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
105 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
106 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
107 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
108 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
109 /* ??? PURPLE_STATUS_MOBILE */
110 /* ??? PURPLE_STATUS_TUNE */
112 /* Status attributes (see also sipe_status_types() */
113 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
115 /* Action name templates */
116 #define ACTION_NAME_PRESENCE "<presence><%s>"
118 /* Our publication type keys. OCS 2007+
119 * Format: SIPE_PUB_{Category}[_{SubSategory}]
121 #define SIPE_PUB_DEVICE "000"
122 #define SIPE_PUB_STATE_MACHINE "100"
123 #define SIPE_PUB_STATE_USER "200"
125 /** Allows to send typed messages from chat window again after account reinstantiation. */
127 sipe_rejoin_chat(PurpleConversation
*conv
)
129 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
&&
130 PURPLE_CONV_CHAT(conv
)->left
)
132 PURPLE_CONV_CHAT(conv
)->left
= FALSE
;
133 purple_conversation_update(conv
, PURPLE_CONV_UPDATE_CHATLEFT
);
137 static char *genbranch()
139 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
140 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
141 rand() & 0xFFFF, rand() & 0xFFFF);
145 static char *default_ua
= NULL
;
147 sipe_get_useragent(struct sipe_account_data
*sip
)
149 const char *useragent
= purple_account_get_string(sip
->account
, "useragent", "");
150 if (is_empty(useragent
)) {
152 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
154 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
155 #define SIPE_TARGET_PLATFORM "linux"
156 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
157 #define SIPE_TARGET_PLATFORM "bsd"
158 # elif defined(__APPLE__) || defined(__MACOS__)
159 #define SIPE_TARGET_PLATFORM "macosx"
160 #elif defined(__solaris__) || defined(__sun)
161 #define SIPE_TARGET_PLATFORM "sun"
162 #elif defined(_WIN32)
163 #define SIPE_TARGET_PLATFORM "win"
165 #define SIPE_TARGET_PLATFORM "generic"
168 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
169 #define SIPE_TARGET_ARCH "x86_64"
170 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
171 #define SIPE_TARGET_ARCH "i386"
172 #elif defined(__ppc64__)
173 #define SIPE_TARGET_ARCH "ppc64"
174 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
175 #define SIPE_TARGET_ARCH "ppc"
177 #define SIPE_TARGET_ARCH "other"
180 default_ua
= g_strdup_printf("Purple/%d.%d.%d Sipe/%s (%s-%s; %s)",
181 PURPLE_MAJOR_VERSION
,
182 PURPLE_MINOR_VERSION
,
183 PURPLE_MICRO_VERSION
,
185 SIPE_TARGET_PLATFORM
,
187 sip
->server_version
? sip
->server_version
: "");
189 useragent
= default_ua
;
194 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
195 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
200 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
202 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
);
204 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
205 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
208 static void sipe_close(PurpleConnection
*gc
);
210 static void send_presence_status(struct sipe_account_data
*sip
);
212 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
214 static void sipe_keep_alive(PurpleConnection
*gc
)
216 struct sipe_account_data
*sip
= gc
->proto_data
;
217 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
218 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
219 gchar buf
[2] = {0, 0};
220 purple_debug_info("sipe", "sending keep alive\n");
221 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
223 time_t now
= time(NULL
);
224 if ((sip
->keepalive_timeout
> 0) &&
225 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
226 #if PURPLE_VERSION_CHECK(2,4,0)
227 && ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
230 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
231 sendout_pkt(gc
, "\r\n\r\n");
232 sip
->last_keepalive
= now
;
237 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
239 struct sip_connection
*ret
= NULL
;
240 GSList
*entry
= sip
->openconns
;
243 if (ret
->fd
== fd
) return ret
;
249 static void sipe_auth_free(struct sip_auth
*auth
)
251 g_free(auth
->opaque
);
255 g_free(auth
->target
);
257 auth
->type
= AUTH_TYPE_UNSET
;
260 g_free(auth
->gssapi_data
);
261 auth
->gssapi_data
= NULL
;
262 sip_sec_destroy_context(auth
->gssapi_context
);
263 auth
->gssapi_context
= NULL
;
266 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
268 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
270 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
274 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
276 struct sip_connection
*conn
= connection_find(sip
, fd
);
278 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
279 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
285 static void connection_free_all(struct sipe_account_data
*sip
)
287 struct sip_connection
*ret
= NULL
;
288 GSList
*entry
= sip
->openconns
;
291 connection_remove(sip
, ret
->fd
);
292 entry
= sip
->openconns
;
296 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
299 const char *authuser
= sip
->authuser
;
303 if (!authuser
|| strlen(authuser
) < 1) {
304 authuser
= sip
->username
;
307 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
308 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
310 // If we have a signature for the message, include that
311 if (msg
->signature
) {
312 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
);
315 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
316 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
320 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
323 purple_account_get_bool(sip
->account
, "sso", TRUE
),
324 sip
->authdomain
? sip
->authdomain
: "",
329 if (!gssapi_data
|| !auth
->gssapi_context
) {
330 sip
->gc
->wants_to_die
= TRUE
;
331 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
335 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
336 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
);
342 return g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth_protocol
, auth
->realm
, auth
->target
);
344 } else { /* Digest */
346 /* Calculate new session key */
348 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
349 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
350 authuser
, auth
->realm
, sip
->password
,
351 auth
->gssapi_data
, NULL
);
354 sprintf(noncecount
, "%08d", auth
->nc
++);
355 response
= purple_cipher_http_digest_calculate_response("md5",
356 msg
->method
, msg
->target
, NULL
, NULL
,
357 auth
->gssapi_data
, noncecount
, NULL
,
359 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
361 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
);
367 static char *parse_attribute(const char *attrname
, const char *source
)
369 const char *tmp
, *tmp2
;
371 int len
= strlen(attrname
);
373 if (!strncmp(source
, attrname
, len
)) {
375 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
377 retval
= g_strndup(tmp
, tmp2
- tmp
);
379 retval
= g_strdup(tmp
);
385 static void fill_auth(gchar
*hdr
, struct sip_auth
*auth
)
391 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
395 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
396 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
397 auth
->type
= AUTH_TYPE_NTLM
;
400 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
401 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
402 auth
->type
= AUTH_TYPE_KERBEROS
;
406 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
407 auth
->type
= AUTH_TYPE_DIGEST
;
411 parts
= g_strsplit(hdr
, "\", ", 0);
412 for (i
= 0; parts
[i
]; i
++) {
415 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
417 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
418 g_free(auth
->gssapi_data
);
419 auth
->gssapi_data
= tmp
;
421 if (auth
->type
== AUTH_TYPE_NTLM
) {
422 /* NTLM module extracts nonce from gssapi-data */
426 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
427 /* Only used with AUTH_TYPE_DIGEST */
428 g_free(auth
->gssapi_data
);
429 auth
->gssapi_data
= tmp
;
430 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
431 g_free(auth
->opaque
);
433 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
437 if (auth
->type
== AUTH_TYPE_DIGEST
) {
438 /* Throw away old session key */
439 g_free(auth
->opaque
);
444 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
445 g_free(auth
->target
);
454 static void sipe_canwrite_cb(gpointer data
,
455 SIPE_UNUSED_PARAMETER gint source
,
456 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
458 PurpleConnection
*gc
= data
;
459 struct sipe_account_data
*sip
= gc
->proto_data
;
463 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
465 if (max_write
== 0) {
466 if (sip
->tx_handler
!= 0){
467 purple_input_remove(sip
->tx_handler
);
473 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
475 if (written
< 0 && errno
== EAGAIN
)
477 else if (written
<= 0) {
478 /*TODO: do we really want to disconnect on a failure to write?*/
479 purple_connection_error(gc
, _("Could not write"));
483 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
486 static void sipe_canwrite_cb_ssl(gpointer data
,
487 SIPE_UNUSED_PARAMETER gint src
,
488 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
490 PurpleConnection
*gc
= data
;
491 struct sipe_account_data
*sip
= gc
->proto_data
;
495 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
497 if (max_write
== 0) {
498 if (sip
->tx_handler
!= 0) {
499 purple_input_remove(sip
->tx_handler
);
505 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
507 if (written
< 0 && errno
== EAGAIN
)
509 else if (written
<= 0) {
510 /*TODO: do we really want to disconnect on a failure to write?*/
511 purple_connection_error(gc
, _("Could not write"));
515 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
518 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
520 static void send_later_cb(gpointer data
, gint source
,
521 SIPE_UNUSED_PARAMETER
const gchar
*error
)
523 PurpleConnection
*gc
= data
;
524 struct sipe_account_data
*sip
;
525 struct sip_connection
*conn
;
527 if (!PURPLE_CONNECTION_IS_VALID(gc
))
535 purple_connection_error(gc
, _("Could not connect"));
539 sip
= gc
->proto_data
;
541 sip
->connecting
= FALSE
;
542 sip
->last_keepalive
= time(NULL
);
544 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
546 /* If there is more to write now, we need to register a handler */
547 if (sip
->txbuf
->bufused
> 0)
548 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
550 conn
= connection_create(sip
, source
);
551 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
554 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
556 struct sipe_account_data
*sip
;
557 struct sip_connection
*conn
;
559 if (!PURPLE_CONNECTION_IS_VALID(gc
))
561 if (gsc
) purple_ssl_close(gsc
);
565 sip
= gc
->proto_data
;
568 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
569 sip
->connecting
= FALSE
;
570 sip
->last_keepalive
= time(NULL
);
572 conn
= connection_create(sip
, gsc
->fd
);
574 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
579 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
580 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
582 PurpleConnection
*gc
= data
;
583 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
584 if (sip
== NULL
) return;
586 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
588 /* If there is more to write now */
589 if (sip
->txbuf
->bufused
> 0) {
590 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
595 static void sendlater(PurpleConnection
*gc
, const char *buf
)
597 struct sipe_account_data
*sip
= gc
->proto_data
;
599 if (!sip
->connecting
) {
600 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
601 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
602 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
604 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
605 purple_connection_error(gc
, _("Could not create socket"));
608 sip
->connecting
= TRUE
;
611 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
612 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
614 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
617 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
619 struct sipe_account_data
*sip
= gc
->proto_data
;
620 time_t currtime
= time(NULL
);
621 int writelen
= strlen(buf
);
624 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sending - %s######\n%s######\n", ctime(&currtime
), tmp
= fix_newlines(buf
));
626 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
627 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
628 purple_debug_info("sipe", "could not send packet\n");
637 if (sip
->tx_handler
) {
642 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
644 ret
= write(sip
->fd
, buf
, writelen
);
648 if (ret
< 0 && errno
== EAGAIN
)
650 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
655 if (ret
< writelen
) {
656 if (!sip
->tx_handler
){
658 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
661 sip
->tx_handler
= purple_input_add(sip
->fd
,
662 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
667 /* XXX: is it OK to do this? You might get part of a request sent
668 with part of another. */
669 if (sip
->txbuf
->bufused
> 0)
670 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
672 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
678 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
680 sendout_pkt(gc
, buf
);
684 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
686 GSList
*tmp
= msg
->headers
;
689 GString
*outstr
= g_string_new("");
690 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
692 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
693 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
694 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
695 tmp
= g_slist_next(tmp
);
697 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
698 sendout_pkt(sip
->gc
, outstr
->str
);
699 g_string_free(outstr
, TRUE
);
702 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
706 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
710 if (sip
->registrar
.gssapi_context
) {
711 struct sipmsg_breakdown msgbd
;
712 gchar
*signature_input_str
;
714 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
715 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
716 sip
->registrar
.ntlm_num
++;
717 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
718 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
719 if (signature_input_str
!= NULL
) {
720 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
721 msg
->signature
= signature_hex
;
722 msg
->rand
= g_strdup(msgbd
.rand
);
723 msg
->num
= g_strdup(msgbd
.num
);
724 g_free(signature_input_str
);
726 sipmsg_breakdown_free(&msgbd
);
729 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
730 buf
= auth_header(sip
, &sip
->registrar
, msg
);
732 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
735 } else if (!strcmp(method
,"SUBSCRIBE") || !strcmp(method
,"SERVICE") || !strcmp(method
,"MESSAGE") || !strcmp(method
,"INVITE") || !strcmp(method
, "ACK") || !strcmp(method
, "NOTIFY") || !strcmp(method
, "BYE") || !strcmp(method
, "INFO") || !strcmp(method
, "OPTIONS") || !strcmp(method
, "REFER")) {
736 sip
->registrar
.nc
= 3;
738 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
740 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
743 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
748 buf
= auth_header(sip
, &sip
->registrar
, msg
);
749 sipmsg_add_header_now_pos(msg
, "Proxy-Authorization", buf
, 5);
752 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
756 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
757 const char *text
, const char *body
)
761 GString
*outstr
= g_string_new("");
762 struct sipe_account_data
*sip
= gc
->proto_data
;
765 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
767 contact
= get_contact(sip
);
768 sipmsg_add_header(msg
, "Contact", contact
);
773 sprintf(len
, "%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
774 sipmsg_add_header(msg
, "Content-Length", len
);
776 sipmsg_add_header(msg
, "Content-Length", "0");
779 msg
->response
= code
;
781 sipmsg_strip_headers(msg
, keepers
);
782 sipmsg_merge_new_headers(msg
);
783 sign_outgoing_message(msg
, sip
, msg
->method
);
785 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
788 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
789 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
791 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
792 tmp
= g_slist_next(tmp
);
794 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
795 sendout_pkt(gc
, outstr
->str
);
796 g_string_free(outstr
, TRUE
);
799 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
801 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
802 purple_debug_info("sipe", "sip->transactions count:%d after removal\n", g_slist_length(sip
->transactions
));
803 if (trans
->msg
) sipmsg_free(trans
->msg
);
804 if (trans
->payload
) {
805 (*trans
->payload
->destroy
)(trans
->payload
->data
);
806 g_free(trans
->payload
);
812 static struct transaction
*
813 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
815 gchar
*call_id
= NULL
;
817 struct transaction
*trans
= g_new0(struct transaction
, 1);
819 trans
->time
= time(NULL
);
820 trans
->msg
= (struct sipmsg
*)msg
;
821 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
822 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
823 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
824 trans
->callback
= callback
;
825 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
826 purple_debug_info("sipe", "sip->transactions count:%d after addition\n", g_slist_length(sip
->transactions
));
830 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
832 struct transaction
*trans
;
833 GSList
*transactions
= sip
->transactions
;
834 gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
835 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
836 gchar
*key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
838 while (transactions
) {
839 trans
= transactions
->data
;
840 if (!g_strcasecmp(trans
->key
, key
)) {
844 transactions
= transactions
->next
;
852 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
853 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
854 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
856 struct sipe_account_data
*sip
= gc
->proto_data
;
857 const char *addh
= "";
860 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
861 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
862 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
863 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
864 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
865 gchar
*route
= g_strdup("");
866 gchar
*epid
= get_epid(sip
);
867 int cseq
= dialog
? ++dialog
->cseq
: 1 /* as Call-Id is new in this case */;
868 struct transaction
*trans
= NULL
;
870 if (dialog
&& dialog
->routes
)
872 GSList
*iter
= dialog
->routes
;
877 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
879 iter
= g_slist_next(iter
);
883 if (!ourtag
&& !dialog
) {
887 if (!strcmp(method
, "REGISTER")) {
888 if (sip
->regcallid
) {
890 callid
= g_strdup(sip
->regcallid
);
892 sip
->regcallid
= g_strdup(callid
);
897 if (addheaders
) addh
= addheaders
;
899 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
900 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
901 "From: <sip:%s>%s%s;epid=%s\r\n"
902 "To: <%s>%s%s%s%s\r\n"
903 "Max-Forwards: 70\r\n"
908 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
910 dialog
&& dialog
->request
? dialog
->request
: url
,
911 TRANSPORT_DESCRIPTOR
,
912 purple_network_get_my_ip(-1),
914 branch
? ";branch=" : "",
915 branch
? branch
: "",
917 ourtag
? ";tag=" : "",
918 ourtag
? ourtag
: "",
921 theirtag
? ";tag=" : "",
922 theirtag
? theirtag
: "",
923 theirepid
? ";epid=" : "",
924 theirepid
? theirepid
: "",
927 sipe_get_useragent(sip
),
931 body
? (gsize
) strlen(body
) : 0,
935 //printf ("parsing msg buf:\n%s\n\n", buf);
936 msg
= sipmsg_parse_msg(buf
);
947 sign_outgoing_message (msg
, sip
, method
);
949 buf
= sipmsg_to_string (msg
);
951 /* add to ongoing transactions */
952 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
953 if (strcmp(method
, "ACK")) {
954 trans
= transactions_add_buf(sip
, msg
, tc
);
958 sendout_pkt(gc
, buf
);
965 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
968 send_soap_request_with_cb(struct sipe_account_data
*sip
,
971 TransCallback callback
,
972 struct transaction_payload
*payload
)
974 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sip
);
975 gchar
*contact
= get_contact(sip
);
976 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
977 "Content-Type: application/SOAP+xml\r\n",contact
);
979 struct transaction
*trans
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
980 trans
->payload
= payload
;
987 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
989 send_soap_request_with_cb(sip
, NULL
, body
, NULL
, NULL
);
992 static char *get_contact_register(struct sipe_account_data
*sip
)
994 char *epid
= get_epid(sip
);
995 char *uuid
= generateUUIDfromEPID(epid
);
996 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
);
1002 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1010 if (!sip
->sipdomain
) return;
1012 uri
= sip_uri_from_name(sip
->sipdomain
);
1013 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1014 to
= sip_uri_self(sip
);
1015 contact
= get_contact_register(sip
);
1016 hdr
= g_strdup_printf("Contact: %s\r\n"
1017 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1018 "Event: registration\r\n"
1019 "Allow-Events: presence\r\n"
1020 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1021 "%s", contact
, expires
);
1025 sip
->registerstatus
= 1;
1027 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1028 process_register_response
);
1035 static void do_register_cb(struct sipe_account_data
*sip
,
1036 SIPE_UNUSED_PARAMETER
void *unused
)
1038 do_register_exp(sip
, -1);
1039 sip
->reregister_set
= FALSE
;
1042 static void do_register(struct sipe_account_data
*sip
)
1044 do_register_exp(sip
, -1);
1048 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1050 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1051 send_soap_request(sip
, body
);
1056 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1059 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1061 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1064 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1068 void sipe_auth_user_cb(void * data
)
1070 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1073 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1078 void sipe_deny_user_cb(void * data
)
1080 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1083 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1088 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1090 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1091 sipe_contact_allow_deny(sip
, name
, TRUE
);
1095 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1097 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1098 sipe_contact_allow_deny(sip
, name
, FALSE
);
1102 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1104 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1105 sipe_contact_set_acl(sip, name, "");
1109 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1113 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1114 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1116 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1118 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1119 if (!watchers
) return;
1121 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1122 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1123 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1124 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1126 // TODO pull out optional displayName to pass as alias
1128 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1129 job
->who
= remote_user
;
1131 purple_account_request_authorization(
1145 xmlnode_free(watchers
);
1150 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1152 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1153 if (!purple_group
) {
1154 purple_group
= purple_group_new(group
->name
);
1155 purple_blist_add_group(purple_group
, NULL
);
1159 group
->purple_group
= purple_group
;
1160 sip
->groups
= g_slist_append(sip
->groups
, group
);
1161 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1163 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1167 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1169 struct sipe_group
*group
;
1175 entry
= sip
->groups
;
1177 group
= entry
->data
;
1178 if (group
->id
== id
) {
1181 entry
= entry
->next
;
1186 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1188 struct sipe_group
*group
;
1194 entry
= sip
->groups
;
1196 group
= entry
->data
;
1197 if (!strcmp(group
->name
, name
)) {
1200 entry
= entry
->next
;
1206 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1209 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1210 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1211 send_soap_request(sip
, body
);
1213 g_free(group
->name
);
1214 group
->name
= g_strdup(name
);
1218 * Only appends if no such value already stored.
1221 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1222 GSList
* res
= list
;
1223 if (!g_slist_find_custom(list
, data
, func
)) {
1224 res
= g_slist_insert_sorted(list
, data
, func
);
1230 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1231 return group1
->id
- group2
->id
;
1235 * Returns string like "2 4 7 8" - group ids buddy belong to.
1238 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1241 //creating array from GList, converting int to gchar*
1242 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1243 GSList
*entry
= buddy
->groups
;
1245 struct sipe_group
* group
= entry
->data
;
1246 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1247 entry
= entry
->next
;
1251 res
= g_strjoinv(" ", ids_arr
);
1252 g_strfreev(ids_arr
);
1257 * Sends buddy update to server
1260 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1262 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1263 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1265 if (buddy
&& purple_buddy
) {
1266 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1268 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1269 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1271 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1272 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1274 send_soap_request(sip
, body
);
1280 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1282 if (msg
->response
== 200) {
1283 struct sipe_group
*group
;
1284 struct group_user_context
*ctx
= trans
->payload
->data
;
1288 struct sipe_buddy
*buddy
;
1290 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1295 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1301 group_id
= xmlnode_get_data(node
);
1307 group
= g_new0(struct sipe_group
, 1);
1308 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1310 group
->name
= g_strdup(ctx
->group_name
);
1312 sipe_group_add(sip
, group
);
1314 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1316 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1319 sipe_group_set_user(sip
, ctx
->user_name
);
1327 static void sipe_group_context_destroy(gpointer data
)
1329 struct group_user_context
*ctx
= data
;
1330 g_free(ctx
->group_name
);
1331 g_free(ctx
->user_name
);
1335 static void sipe_group_create (struct sipe_account_data
*sip
, const gchar
*name
, const gchar
* who
)
1337 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
1338 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
1340 ctx
->group_name
= g_strdup(name
);
1341 ctx
->user_name
= g_strdup(who
);
1342 payload
->destroy
= sipe_group_context_destroy
;
1343 payload
->data
= ctx
;
1345 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1346 send_soap_request_with_cb(sip
, NULL
, body
, process_add_group_response
, payload
);
1351 * Data structure for scheduled actions
1354 struct scheduled_action
{
1357 * Format is <Event>[<Data>...]
1358 * Example: <presence><sip:user@domain.com> or <registration>
1361 guint timeout_handler
;
1362 gboolean repetitive
;
1364 GDestroyNotify destroy
;
1365 struct sipe_account_data
*sip
;
1371 * Should return FALSE if repetitive action is not needed
1373 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1376 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1377 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1378 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1379 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1380 ret
= sched_action
->repetitive
;
1381 if (sched_action
->destroy
) {
1382 (*sched_action
->destroy
)(sched_action
->payload
);
1384 g_free(sched_action
->name
);
1385 g_free(sched_action
);
1390 * Kills action timer effectively cancelling
1393 * @param name of action
1395 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1399 if (!sip
->timeouts
|| !name
) return;
1401 entry
= sip
->timeouts
;
1403 struct scheduled_action
*sched_action
= entry
->data
;
1404 if(!strcmp(sched_action
->name
, name
)) {
1405 GSList
*to_delete
= entry
;
1406 entry
= entry
->next
;
1407 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1408 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1409 purple_timeout_remove(sched_action
->timeout_handler
);
1410 if (sched_action
->destroy
) {
1411 (*sched_action
->destroy
)(sched_action
->payload
);
1413 g_free(sched_action
->name
);
1414 g_free(sched_action
);
1416 entry
= entry
->next
;
1422 sipe_schedule_action0(const gchar
*name
,
1426 GDestroyNotify destroy
,
1427 struct sipe_account_data
*sip
,
1430 struct scheduled_action
*sched_action
;
1432 /* Make sure each action only exists once */
1433 sipe_cancel_scheduled_action(sip
, name
);
1435 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1436 sched_action
= g_new0(struct scheduled_action
, 1);
1437 sched_action
->repetitive
= FALSE
;
1438 sched_action
->name
= g_strdup(name
);
1439 sched_action
->action
= action
;
1440 sched_action
->destroy
= destroy
;
1441 sched_action
->sip
= sip
;
1442 sched_action
->payload
= payload
;
1443 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1444 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1445 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1446 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1450 sipe_schedule_action(const gchar
*name
,
1453 GDestroyNotify destroy
,
1454 struct sipe_account_data
*sip
,
1457 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1461 * Same as sipe_schedule_action() but timeout is in milliseconds.
1464 sipe_schedule_action_msec(const gchar
*name
,
1467 GDestroyNotify destroy
,
1468 struct sipe_account_data
*sip
,
1471 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1475 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1477 /** Should be g_free()'d
1480 sipe_get_subscription_key(gchar
*event
,
1485 if (is_empty(event
)) return NULL
;
1487 if (event
&& !g_ascii_strcasecmp(event
, "presence")) {
1488 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1489 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, with
);
1491 /* @TODO drop participated buddies' just_added flag */
1493 /* Subscription is identified by <event> key */
1494 key
= g_strdup_printf("<%s>", event
);
1500 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1501 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1503 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
1504 gchar
*event
= sipmsg_find_header(msg
, "Event");
1505 gchar
*key
= sipe_get_subscription_key(event
, with
);
1507 /* 200 OK; 481 Call Leg Does Not Exist */
1508 if (key
&& (msg
->response
== 200 || msg
->response
== 481)) {
1509 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
1510 g_hash_table_remove(sip
->subscriptions
, key
);
1511 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
1515 /* create/store subscription dialog if not yet */
1516 if (msg
->response
== 200) {
1517 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1518 gchar
*cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
1521 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
1522 g_hash_table_insert(sip
->subscriptions
, g_strdup(key
), subscription
);
1524 subscription
->dialog
.callid
= g_strdup(callid
);
1525 subscription
->dialog
.cseq
= atoi(cseq
);
1526 subscription
->dialog
.with
= g_strdup(with
);
1527 subscription
->event
= g_strdup(event
);
1528 sipe_dialog_parse(&subscription
->dialog
, msg
, TRUE
);
1530 purple_debug_info("sipe", "process_subscribe_response: subscription dialog added for: %s\n", key
);
1539 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1541 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1546 static void sipe_subscribe_resource_uri(const char *name
,
1547 SIPE_UNUSED_PARAMETER gpointer value
,
1548 gchar
**resources_uri
)
1550 gchar
*tmp
= *resources_uri
;
1551 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1555 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1557 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1558 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1559 gchar
*tmp
= *resources_uri
;
1561 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
1563 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
1568 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1569 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1570 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1571 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1572 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1575 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1578 gchar
*contact
= get_contact(sip
);
1581 gchar
*require
= "";
1583 gchar
*autoextend
= "";
1584 gchar
*content_type
;
1585 struct sip_dialog
*dialog
;
1588 require
= ", categoryList";
1589 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1590 content_type
= "application/msrtc-adrl-categorylist+xml";
1591 content
= g_strdup_printf(
1592 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1593 "<action name=\"subscribe\" id=\"63792024\">\n"
1594 "<adhocList>\n%s</adhocList>\n"
1595 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1596 "<category name=\"calendarData\"/>\n"
1597 "<category name=\"contactCard\"/>\n"
1598 "<category name=\"note\"/>\n"
1599 "<category name=\"state\"/>\n"
1602 "</batchSub>", sip
->username
, resources_uri
);
1604 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1605 content_type
= "application/adrl+xml";
1606 content
= g_strdup_printf(
1607 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1608 "<create xmlns=\"\">\n%s</create>\n"
1609 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1611 g_free(resources_uri
);
1613 request
= g_strdup_printf(
1614 "Require: adhoclist%s\r\n"
1615 "Supported: eventlist\r\n"
1616 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1617 "Supported: ms-piggyback-first-notify\r\n"
1618 "%sSupported: ms-benotify\r\n"
1619 "Proxy-Require: ms-benotify\r\n"
1620 "Event: presence\r\n"
1621 "Content-Type: %s\r\n"
1622 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1625 /* subscribe to buddy presence */
1626 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1627 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1628 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1629 purple_debug_info("sipe", "sipe_subscribe_presence_batched_to: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1631 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1639 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1640 SIPE_UNUSED_PARAMETER
void *unused
)
1642 gchar
*to
= sip_uri_self(sip
);
1643 gchar
*resources_uri
= g_strdup("");
1645 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1647 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1649 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1652 struct presence_batched_routed
{
1657 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1659 struct presence_batched_routed
*data
= payload
;
1660 GSList
*buddies
= data
->buddies
;
1662 g_free(buddies
->data
);
1663 buddies
= buddies
->next
;
1665 g_slist_free(data
->buddies
);
1670 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
1672 struct presence_batched_routed
*data
= payload
;
1673 GSList
*buddies
= data
->buddies
;
1674 gchar
*resources_uri
= g_strdup("");
1676 gchar
*tmp
= resources_uri
;
1677 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
1679 buddies
= buddies
->next
;
1681 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
1682 g_strdup(data
->host
));
1686 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1687 * The user sends a single SUBSCRIBE request to the subscribed contact.
1688 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1692 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
1696 gchar
*to
= sip_uri((char *)buddy_name
);
1697 gchar
*tmp
= get_contact(sip
);
1699 gchar
*content
= NULL
;
1700 gchar
*autoextend
= "";
1701 gchar
*content_type
= "";
1702 struct sip_dialog
*dialog
;
1703 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, to
);
1704 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1706 if (sbuddy
) sbuddy
->just_added
= FALSE
;
1709 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
1711 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1714 request
= g_strdup_printf(
1715 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1716 "Supported: ms-piggyback-first-notify\r\n"
1717 "%s%sSupported: ms-benotify\r\n"
1718 "Proxy-Require: ms-benotify\r\n"
1719 "Event: presence\r\n"
1720 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
1723 content
= g_strdup_printf(
1724 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1725 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1726 "<resource uri=\"%s\"%s\n"
1728 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1729 "<category name=\"calendarData\"/>\n"
1730 "<category name=\"contactCard\"/>\n"
1731 "<category name=\"note\"/>\n"
1732 "<category name=\"state\"/>\n"
1735 "</batchSub>", sip
->username
, to
, context
);
1740 /* subscribe to buddy presence */
1741 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1742 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1743 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1744 purple_debug_info("sipe", "sipe_subscribe_presence_single: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1746 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1754 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1756 purple_debug_info("sipe", "sipe_set_status: status=%s\n", purple_status_get_id(status
));
1758 if (!purple_status_is_active(status
))
1762 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1766 g_free(sip
->status
);
1767 sip
->status
= g_strdup(purple_status_get_id(status
));
1769 /* schedule 2 sec to capture idle flag */
1770 action_name
= g_strdup_printf("<%s>", "+set-status");
1771 sipe_schedule_action(action_name
, 2, (Action
)send_presence_status
, NULL
, sip
, NULL
);
1772 g_free(action_name
);
1777 sipe_set_idle(PurpleConnection
* gc
,
1780 purple_debug_info("sipe", "sipe_set_idle: time=%d\n", time
);
1783 struct sipe_account_data
*sip
= gc
->proto_data
;
1786 sip
->was_idle
= sip
->is_idle
;
1787 sip
->is_idle
= (time
> 0);
1793 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
1794 SIPE_UNUSED_PARAMETER
const char *alias
)
1796 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1797 sipe_group_set_user(sip
, name
);
1801 sipe_group_buddy(PurpleConnection
*gc
,
1803 const char *old_group_name
,
1804 const char *new_group_name
)
1806 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1807 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1808 struct sipe_group
* old_group
= NULL
;
1809 struct sipe_group
* new_group
;
1811 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1812 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1814 if(!buddy
) { // buddy not in roaming list
1818 if (old_group_name
) {
1819 old_group
= sipe_group_find_by_name(sip
, old_group_name
);
1821 new_group
= sipe_group_find_by_name(sip
, new_group_name
);
1824 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1825 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1829 sipe_group_create(sip
, new_group_name
, who
);
1831 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1832 sipe_group_set_user(sip
, who
);
1836 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1838 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1840 /* libpurple can call us with undefined buddy or group */
1841 if (buddy
&& group
) {
1842 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1844 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1845 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
1846 purple_blist_rename_buddy(buddy
, buddy_name
);
1849 /* Prepend sip: if needed */
1850 if (strncmp("sip:", buddy
->name
, 4)) {
1851 gchar
*buf
= sip_uri_from_name(buddy
->name
);
1852 purple_blist_rename_buddy(buddy
, buf
);
1856 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1857 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
1858 purple_debug_info("sipe", "sipe_add_buddy: adding %s\n", buddy
->name
);
1859 b
->name
= g_strdup(buddy
->name
);
1860 b
->just_added
= TRUE
;
1861 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1862 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1863 /* @TODO should go to callback */
1864 sipe_subscribe_presence_single(sip
, b
->name
);
1866 purple_debug_info("sipe", "sipe_add_buddy: buddy %s already in internal list\n", buddy
->name
);
1871 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1875 * We are calling g_hash_table_foreach_steal(). That means that no
1876 * key/value deallocation functions are called. Therefore the glib
1877 * hash code does not touch the key (buddy->name) or value (buddy)
1878 * of the to-be-deleted hash node at all. It follows that we
1880 * - MUST free the memory for the key ourselves and
1881 * - ARE allowed to do it in this function
1883 * Conclusion: glib must be broken on the Windows platform if sipe
1884 * crashes with SIGTRAP when closing. You'll have to live
1885 * with the memory leak until this is fixed.
1887 g_free(buddy
->name
);
1889 g_free(buddy
->activity
);
1890 g_free(buddy
->meeting_subject
);
1891 g_free(buddy
->meeting_location
);
1892 g_free(buddy
->annotation
);
1893 g_free(buddy
->device_name
);
1894 g_slist_free(buddy
->groups
);
1899 * Unassociates buddy from group first.
1900 * Then see if no groups left, removes buddy completely.
1901 * Otherwise updates buddy groups on server.
1903 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1905 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1906 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1907 struct sipe_group
*g
= NULL
;
1909 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1914 g
= sipe_group_find_by_name(sip
, group
->name
);
1918 b
->groups
= g_slist_remove(b
->groups
, g
);
1919 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1922 if (g_slist_length(b
->groups
) < 1) {
1923 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
1924 sipe_cancel_scheduled_action(sip
, action_name
);
1925 g_free(action_name
);
1927 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1930 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1931 send_soap_request(sip
, body
);
1937 //updates groups on server
1938 sipe_group_set_user(sip
, b
->name
);
1944 sipe_rename_group(PurpleConnection
*gc
,
1945 const char *old_name
,
1947 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
1949 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1950 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, old_name
);
1952 sipe_group_rename(sip
, s_group
, group
->name
);
1954 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1959 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1961 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1962 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1965 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1966 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1967 send_soap_request(sip
, body
);
1970 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1971 g_free(s_group
->name
);
1974 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1978 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
1980 PurpleStatusType
*type
;
1981 GList
*types
= NULL
;
1983 /* Macros to reduce code repetition.
1984 Translators: noun */
1985 #define SIPE_ADD_STATUS(prim,id,name) type = purple_status_type_new_with_attrs( \
1987 TRUE, TRUE, FALSE, \
1988 SIPE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
1990 types = g_list_append(types, type);
1991 #define SIPE_ADD_STATUS_NO_MSG(prim,id,name,user) type = purple_status_type_new( \
1992 prim, id, name, user); \
1993 types = g_list_append(types, type);
1996 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2000 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2001 SIPE_STATUS_ID_BUSY
, _("Busy"));
2003 /* Do Not Disturb (not user settable) */
2004 SIPE_ADD_STATUS_NO_MSG(PURPLE_STATUS_UNAVAILABLE
,
2005 SIPE_STATUS_ID_DND
, NULL
,
2009 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2010 SIPE_STATUS_ID_BRB
, _("Be Right Back"));
2013 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2017 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2018 SIPE_STATUS_ID_ONPHONE
, _("On The Phone"));
2021 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2022 SIPE_STATUS_ID_LUNCH
, _("Out To Lunch"));
2024 /* Idle/Inactive (not user settable) */
2025 SIPE_ADD_STATUS_NO_MSG(PURPLE_STATUS_AWAY
,
2026 SIPE_STATUS_ID_IDLE
, _("Inactive"),
2029 /* Appear Offline */
2030 SIPE_ADD_STATUS_NO_MSG(PURPLE_STATUS_INVISIBLE
,
2035 SIPE_ADD_STATUS_NO_MSG(PURPLE_STATUS_OFFLINE
,
2043 * A callback for g_hash_table_foreach
2045 static void sipe_buddy_subscribe_cb(SIPE_UNUSED_PARAMETER
char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
2047 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2048 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
2049 guint time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
2050 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
2051 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy
->name
));
2055 * Removes entries from purple buddy list
2056 * that does not correspond ones in the roaming contact list.
2058 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
2059 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
2060 GSList
*entry
= buddies
;
2061 struct sipe_buddy
*buddy
;
2065 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
2066 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
2069 g
= purple_buddy_get_group(b
);
2070 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
2072 gboolean in_sipe_groups
= FALSE
;
2073 GSList
*entry2
= buddy
->groups
;
2075 struct sipe_group
*group
= entry2
->data
;
2076 if (!strcmp(group
->name
, g
->name
)) {
2077 in_sipe_groups
= TRUE
;
2080 entry2
= entry2
->next
;
2082 if(!in_sipe_groups
) {
2083 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
2084 purple_blist_remove_buddy(b
);
2087 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
2088 purple_blist_remove_buddy(b
);
2090 entry
= entry
->next
;
2092 g_slist_free(buddies
);
2095 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2097 int len
= msg
->bodylen
;
2099 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
2102 const gchar
*contacts_delta
;
2103 xmlnode
*group_node
;
2104 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
2108 /* Convert the contact from XML to Purple Buddies */
2109 isc
= xmlnode_from_str(msg
->body
, len
);
2114 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
2115 if (contacts_delta
) {
2116 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2119 if (!strcmp(isc
->name
, "contactList")) {
2122 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
2123 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2124 const char *name
= xmlnode_get_attrib(group_node
, "name");
2126 if (!strncmp(name
, "~", 1)) {
2127 name
= _("Other Contacts");
2129 group
->name
= g_strdup(name
);
2130 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2132 sipe_group_add(sip
, group
);
2135 // Make sure we have at least one group
2136 if (g_slist_length(sip
->groups
) == 0) {
2137 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2138 PurpleGroup
*purple_group
;
2139 group
->name
= g_strdup(_("Other Contacts"));
2141 purple_group
= purple_group_new(group
->name
);
2142 purple_blist_add_group(purple_group
, NULL
);
2143 sip
->groups
= g_slist_append(sip
->groups
, group
);
2146 /* Parse contacts */
2147 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2148 const gchar
*uri
= xmlnode_get_attrib(item
, "uri");
2149 const gchar
*name
= xmlnode_get_attrib(item
, "name");
2151 struct sipe_buddy
*buddy
= NULL
;
2153 gchar
**item_groups
;
2156 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2157 tmp
= sip_uri_from_name(uri
);
2158 buddy_name
= g_ascii_strdown(tmp
, -1);
2161 /* assign to group Other Contacts if nothing else received */
2162 tmp
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2163 if(!tmp
|| !strcmp("", tmp
) ) {
2164 struct sipe_group
*group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
2166 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
2168 item_groups
= g_strsplit(tmp
, " ", 0);
2171 while (item_groups
[i
]) {
2172 struct sipe_group
*group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2174 // If couldn't find the right group for this contact, just put them in the first group we have
2175 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2176 group
= sip
->groups
->data
;
2179 if (group
!= NULL
) {
2180 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2182 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2183 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2185 purple_debug_info("sipe", "Created new buddy %s with alias %s\n", buddy_name
, uri
);
2188 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2189 if (name
!= NULL
&& strlen(name
) != 0) {
2190 purple_blist_alias_buddy(b
, name
);
2192 purple_debug_info("sipe", "Replaced buddy %s alias with %s\n", buddy_name
, name
);
2197 buddy
= g_new0(struct sipe_buddy
, 1);
2198 buddy
->name
= g_strdup(b
->name
);
2199 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2202 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2204 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2206 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2211 } // while, contact groups
2212 g_strfreev(item_groups
);
2217 sipe_cleanup_local_blist(sip
);
2221 //subscribe to buddies
2222 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2223 if(sip
->batched_support
){
2224 sipe_subscribe_presence_batched(sip
, NULL
);
2227 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2229 sip
->subscribed_buddies
= TRUE
;
2236 * Subscribe roaming contacts
2238 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2240 gchar
*to
= sip_uri_self(sip
);
2241 gchar
*tmp
= get_contact(sip
);
2242 gchar
*hdr
= g_strdup_printf(
2243 "Event: vnd-microsoft-roaming-contacts\r\n"
2244 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2245 "Supported: com.microsoft.autoextend\r\n"
2246 "Supported: ms-benotify\r\n"
2247 "Proxy-Require: ms-benotify\r\n"
2248 "Supported: ms-piggyback-first-notify\r\n"
2249 "Contact: %s\r\n", tmp
);
2252 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2257 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2258 SIPE_UNUSED_PARAMETER
void *unused
)
2261 struct sip_dialog
*dialog
;
2262 gchar
*to
= sip_uri_self(sip
);
2263 gchar
*tmp
= get_contact(sip
);
2264 gchar
*hdr
= g_strdup_printf(
2265 "Event: presence.wpending\r\n"
2266 "Accept: text/xml+msrtc.wpending\r\n"
2267 "Supported: com.microsoft.autoextend\r\n"
2268 "Supported: ms-benotify\r\n"
2269 "Proxy-Require: ms-benotify\r\n"
2270 "Supported: ms-piggyback-first-notify\r\n"
2271 "Contact: %s\r\n", tmp
);
2274 /* Subscription is identified by <event> key */
2275 key
= g_strdup_printf("<%s>", "presence.wpending");
2276 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2277 purple_debug_info("sipe", "sipe_subscribe_presence_wpending: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2279 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", dialog
, process_subscribe_response
);
2287 * Fires on deregistration event initiated by server.
2288 * [MS-SIPREGE] SIP extension.
2293 // Content-Type: text/registration-event
2294 // subscription-state: terminated;expires=0
2295 // ms-diagnostics-public: 4141;reason="User disabled"
2297 // deregistered;event=rejected
2299 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2301 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2302 gchar
*event
= NULL
;
2303 gchar
*reason
= NULL
;
2304 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2306 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2307 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2309 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2310 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2311 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2312 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2314 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2318 if (warning
!= NULL
) {
2319 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2320 } else { // for LCS2005
2322 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2323 error_id
= 4140; // [MS-SIPREGE]
2324 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2325 reason
= g_strdup(_("you are already signed in at another location"));
2326 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2328 reason
= g_strdup(_("user disabled")); // [MS-OCER]
2329 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2331 reason
= g_strdup(_("user moved")); // [MS-OCER]
2335 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
2338 sip
->gc
->wants_to_die
= TRUE
;
2339 purple_connection_error(sip
->gc
, warning
);
2344 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2346 xmlnode
*xn_provision_group_list
;
2349 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2351 /* provisionGroup */
2352 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2353 if (!strcmp("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2354 g_free(sip
->focus_factory_uri
);
2355 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2356 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2357 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2361 xmlnode_free(xn_provision_group_list
);
2364 /** for 2005 system */
2366 sipe_process_provisioning(struct sipe_account_data
*sip
,
2369 xmlnode
*xn_provision
;
2372 xn_provision
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2373 if ((node
= xmlnode_get_child(xn_provision
, "user"))) {
2374 purple_debug_info("sipe", "sipe_process_provisioning: uri=%s\n", xmlnode_get_attrib(node
, "uri"));
2375 if ((node
= xmlnode_get_child(node
, "line"))) {
2376 const gchar
*line_uri
= xmlnode_get_attrib(node
, "uri");
2377 const gchar
*server
= xmlnode_get_attrib(node
, "server");
2378 purple_debug_info("sipe", "sipe_process_provisioning: line_uri=%s server=%s\n", line_uri
, server
);
2379 sip_csta_open(sip
, line_uri
, server
);
2382 xmlnode_free(xn_provision
);
2385 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2387 const gchar
*contacts_delta
;
2390 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2396 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2399 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2406 free_container(struct sipe_container
*container
)
2410 if (!container
) return;
2412 entry
= container
->members
;
2414 g_free(entry
->data
);
2415 entry
= g_slist_remove(entry
, entry
->data
);
2421 * Finds locally stored MS-PRES container member
2423 static struct sipe_container_member
*
2424 sipe_find_container_member(struct sipe_container
*container
,
2428 struct sipe_container_member
*member
;
2431 if (container
== NULL
|| type
== NULL
) {
2435 entry
= container
->members
;
2437 member
= entry
->data
;
2438 if (!g_strcasecmp(member
->type
, type
)
2439 && ((!member
->value
&& !value
)
2440 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2444 entry
= entry
->next
;
2450 * Finds locally stored MS-PRES container by id
2452 static struct sipe_container
*
2453 sipe_find_container(struct sipe_account_data
*sip
,
2456 struct sipe_container
*container
;
2463 entry
= sip
->containers
;
2465 container
= entry
->data
;
2466 if (id
== container
->id
) {
2469 entry
= entry
->next
;
2483 sipe_find_access_level(struct sipe_account_data
*sip
,
2487 guint containers
[] = {32000, 400, 300, 200, 100};
2490 for (i
= 0; i
< 5; i
++) {
2491 struct sipe_container_member
*member
;
2492 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2493 if (!container
) continue;
2495 member
= sipe_find_container_member(container
, type
, value
);
2497 return containers
[i
];
2505 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2507 guint container_version
,
2508 const gchar
* action
,
2512 gchar
*self
= sip_uri_self(sip
);
2513 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2516 gchar
*body
= g_strdup_printf(
2517 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2518 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2519 "</setContainerMembers>",
2527 contact
= get_contact(sip
);
2528 hdr
= g_strdup_printf("Contact: %s\r\n"
2529 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2532 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2540 free_publication(struct sipe_publication
*publication
)
2542 g_free(publication
->category
);
2543 g_free(publication
->note
);
2544 g_free(publication
);
2547 /* key is <category><instance><container> */
2549 sipe_is_our_publication(struct sipe_account_data
*sip
,
2554 /* filling keys for our publications if not yet cached */
2555 if (!sip
->our_publication_keys
) {
2556 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2557 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2558 guint user_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
);
2560 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2561 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2563 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2564 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2565 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2566 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2568 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2569 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2570 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2571 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2573 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2574 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
2575 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2576 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
2577 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2578 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
2580 //purple_debug_info("sipe", "sipe_is_our_publication: sip->our_publication_keys length=%d\n",
2581 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
2584 //purple_debug_info("sipe", "sipe_is_our_publication: key=%s\n", key);
2586 entry
= sip
->our_publication_keys
;
2588 //purple_debug_info("sipe", " sipe_is_our_publication: entry->data=%s\n", entry->data);
2589 if (!strcmp(entry
->data
, key
)) {
2592 entry
= entry
->next
;
2597 /** Property names to store in blist.xml */
2598 #define ALIAS_PROP "alias"
2599 #define EMAIL_PROP "email"
2600 #define PHONE_PROP "phone"
2601 #define PHONE_DISPLAY_PROP "phone-display"
2602 #define PHONE_MOBILE_PROP "phone-mobile"
2603 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
2604 #define PHONE_HOME_PROP "phone-home"
2605 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
2606 #define PHONE_OTHER_PROP "phone-other"
2607 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
2608 #define PHONE_CUSTOM1_PROP "phone-custom1"
2609 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
2610 #define SITE_PROP "site"
2611 #define COMPANY_PROP "company"
2612 #define DEPARTMENT_PROP "department"
2613 #define TITLE_PROP "title"
2614 #define OFFICE_PROP "office"
2615 /** implies work address */
2616 #define ADDRESS_STREET_PROP "address-street"
2617 #define ADDRESS_CITY_PROP "address-city"
2618 #define ADDRESS_STATE_PROP "address-state"
2619 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
2620 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
2622 * Update user information
2624 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2625 * @param property_name
2626 * @param property_value may be modified to strip white space
2629 sipe_update_user_info(struct sipe_account_data
*sip
,
2631 const char *property_name
,
2632 char *property_value
)
2634 GSList
*buddies
, *entry
;
2636 if (!property_name
|| strlen(property_name
) == 0) return;
2639 property_value
= g_strstrip(property_value
);
2641 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
2643 const char *prop_str
;
2644 const char *server_alias
;
2645 PurpleBuddy
*p_buddy
= entry
->data
;
2647 /* for Display Name */
2648 if (!strcmp(property_name
, ALIAS_PROP
)) {
2649 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
2650 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, property_value
);
2651 purple_blist_alias_buddy(p_buddy
, property_value
);
2654 server_alias
= purple_buddy_get_server_alias(p_buddy
);
2655 if (property_value
&& strlen(property_value
) > 0 &&
2656 ( (server_alias
&& strcmp(property_value
, server_alias
))
2657 || !server_alias
|| strlen(server_alias
) == 0 )
2659 purple_blist_server_alias_buddy(p_buddy
, property_value
);
2662 /* for other properties */
2664 if (property_value
&& strlen(property_value
) > 0) {
2665 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
2666 if (!prop_str
|| g_ascii_strcasecmp(prop_str
, property_value
)) {
2667 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
2672 entry
= entry
->next
;
2674 g_slist_free(buddies
);
2679 * Suitable for both 2005 and 2007 systems.
2681 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2683 * @param phone may be modified to strip white space
2684 * @param phone_display_string may be modified to strip white space
2687 sipe_update_user_phone(struct sipe_account_data
*sip
,
2689 const gchar
*phone_type
,
2691 gchar
*phone_display_string
)
2693 const char *phone_node
= PHONE_PROP
; /* work phone by default */
2694 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
2696 if(!phone
|| strlen(phone
) == 0) return;
2698 if (phone_type
&& (!strcmp(phone_type
, "mobile") || !strcmp(phone_type
, "cell"))) {
2699 phone_node
= PHONE_MOBILE_PROP
;
2700 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
2701 } else if (phone_type
&& !strcmp(phone_type
, "home")) {
2702 phone_node
= PHONE_HOME_PROP
;
2703 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
2704 } else if (phone_type
&& !strcmp(phone_type
, "other")) {
2705 phone_node
= PHONE_OTHER_PROP
;
2706 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
2707 } else if (phone_type
&& !strcmp(phone_type
, "custom1")) {
2708 phone_node
= PHONE_CUSTOM1_PROP
;
2709 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
2712 sipe_update_user_info(sip
, uri
, phone_node
, phone
);
2713 if (phone_display_string
) {
2714 sipe_update_user_info(sip
, uri
, phone_display_node
, phone_display_string
);
2719 send_publish_category_initial(struct sipe_account_data
*sip
);
2722 * When we receive some self (BE) NOTIFY with a new subscriber
2723 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2726 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2733 char *display_name
= NULL
;
2735 GSList
*category_names
= NULL
;
2737 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2739 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2742 contact
= get_contact(sip
);
2743 to
= sip_uri_self(sip
);
2747 /* set list of categories participating in this XML */
2748 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2749 const gchar
*name
= xmlnode_get_attrib(node
, "name");
2750 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
2752 purple_debug_info("sipe", "sipe_process_roaming_self: category_names length=%d\n",
2753 category_names
? (int) g_slist_length(category_names
) : -1);
2754 /* drop category information */
2755 if (category_names
) {
2756 GSList
*entry
= category_names
;
2758 GHashTable
*cat_publications
;
2759 const gchar
*category
= entry
->data
;
2760 entry
= entry
->next
;
2761 purple_debug_info("sipe", "sipe_process_roaming_self: dropping category: %s\n", category
);
2762 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
2763 if (cat_publications
) {
2764 g_hash_table_remove(sip
->our_publications
, category
);
2765 purple_debug_info("sipe", " sipe_process_roaming_self: dropped category: %s\n", category
);
2769 g_slist_free(category_names
);
2770 /* filling our categories reflected in roaming data */
2771 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2772 const gchar
*name
= xmlnode_get_attrib(node
, "name");
2773 const gchar
*container
= xmlnode_get_attrib(node
, "container");
2774 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
2775 const gchar
*version
= xmlnode_get_attrib(node
, "version");
2776 guint version_int
= version
? atoi(version
) : 0;
2779 if (!container
|| !instance
) continue;
2781 /* key is <category><instance><container> */
2782 key
= g_strdup_printf("<%s><%s><%s>", name
, instance
, container
);
2783 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version_int
);
2784 if (sipe_is_our_publication(sip
, key
)) {
2785 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
2787 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2788 publication
->category
= g_strdup(name
);
2789 publication
->instance
= atoi(instance
);
2790 publication
->container
= atoi(container
);
2791 publication
->version
= version_int
;
2792 /* filling publication->availability */
2793 if (!strcmp(name
, "state")) {
2794 xmlnode
*xn_avail
= xmlnode_get_descendant(node
, "state", "availability", NULL
);
2796 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
2798 publication
->availability
= atoi(avail_str
);
2803 /* filling publication->note */
2804 if (!strcmp(name
, "note")) {
2805 xmlnode
*xn_body
= xmlnode_get_descendant(node
, "note", "body", NULL
);
2807 publication
->note
= xmlnode_get_data(xn_body
);
2811 if (!cat_publications
) {
2812 cat_publications
= g_hash_table_new_full(
2813 g_str_hash
, g_str_equal
,
2814 g_free
, (GDestroyNotify
)free_publication
);
2815 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
2816 purple_debug_info("sipe", "sipe_process_roaming_self: added GHashTable cat=%s\n", name
);
2818 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
2819 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version_int
);
2823 /* userProperties published by server from AD */
2824 if (!sip
->csta
&& !strcmp(name
, "userProperties")) {
2826 /* line, for Remote Call Control (RCC) */
2827 for (line
= xmlnode_get_descendant(node
, "userProperties", "lines", "line", NULL
); line
; line
= xmlnode_get_next_twin(line
)) {
2828 const gchar
*line_server
= xmlnode_get_attrib(line
, "lineServer");
2829 const gchar
*line_type
= xmlnode_get_attrib(line
, "lineType");
2832 if (!line_server
|| (strcmp(line_type
, "Rcc") && strcmp(line_type
, "Dual"))) continue;
2834 line_uri
= xmlnode_get_data(line
);
2836 purple_debug_info("sipe", "sipe_process_roaming_self: line_uri=%s server=%s\n", line_uri
, line_server
);
2837 sip_csta_open(sip
, line_uri
, line_server
);
2845 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
2846 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
2849 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2850 guint id
= atoi(xmlnode_get_attrib(node
, "id"));
2851 struct sipe_container
*container
= sipe_find_container(sip
, id
);
2854 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2855 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
2856 free_container(container
);
2858 container
= g_new0(struct sipe_container
, 1);
2860 container
->version
= atoi(xmlnode_get_attrib(node
, "version"));
2861 sip
->containers
= g_slist_append(sip
->containers
, container
);
2862 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
2864 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
2865 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2866 member
->type
= xmlnode_get_attrib(node2
, "type");
2867 member
->value
= xmlnode_get_attrib(node2
, "value");
2868 container
->members
= g_slist_append(container
->members
, member
);
2869 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
2870 member
->type
, member
->value
? member
->value
: "");
2874 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
2875 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
2876 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
2877 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
2878 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
2879 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
2880 /* initial set-up to let counterparties see your status */
2881 if (sameEnterpriseAL
< 0) {
2882 struct sipe_container
*container
= sipe_find_container(sip
, 200);
2883 guint version
= container
? container
->version
: 0;
2884 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
2886 if (federatedAL
< 0) {
2887 struct sipe_container
*container
= sipe_find_container(sip
, 100);
2888 guint version
= container
? container
->version
: 0;
2889 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
2891 sip
->access_level_set
= TRUE
;
2895 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2897 const char *acknowledged
;
2901 user
= xmlnode_get_attrib(node
, "user"); /* without 'sip:' prefix */
2902 if (!user
) continue;
2903 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2904 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
2905 uri
= sip_uri_from_name(user
);
2907 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
2909 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
2910 if(!g_ascii_strcasecmp(acknowledged
,"false")){
2911 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
2912 if (!purple_find_buddy(sip
->account
, uri
)) {
2913 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
2916 hdr
= g_strdup_printf(
2918 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2920 body
= g_strdup_printf(
2921 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2922 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2923 "</setSubscribers>", user
);
2925 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2929 g_free(display_name
);
2937 /* Publish initial state if not yet.
2938 * Assuming this happens on initial responce to subscription to roaming-self
2939 * so we've already updated our roaming data in full.
2942 if (sip
->ocs2007
&& !sip
->initial_state_published
) {
2943 send_publish_category_initial(sip
);
2944 sip
->initial_state_published
= TRUE
;
2948 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
2950 gchar
*to
= sip_uri_self(sip
);
2951 gchar
*tmp
= get_contact(sip
);
2952 gchar
*hdr
= g_strdup_printf(
2953 "Event: vnd-microsoft-roaming-ACL\r\n"
2954 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2955 "Supported: com.microsoft.autoextend\r\n"
2956 "Supported: ms-benotify\r\n"
2957 "Proxy-Require: ms-benotify\r\n"
2958 "Supported: ms-piggyback-first-notify\r\n"
2959 "Contact: %s\r\n", tmp
);
2962 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2968 * To request for presence information about the user, access level settings that have already been configured by the user
2969 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2970 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2973 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
2975 gchar
*to
= sip_uri_self(sip
);
2976 gchar
*tmp
= get_contact(sip
);
2977 gchar
*hdr
= g_strdup_printf(
2978 "Event: vnd-microsoft-roaming-self\r\n"
2979 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2980 "Supported: ms-benotify\r\n"
2981 "Proxy-Require: ms-benotify\r\n"
2982 "Supported: ms-piggyback-first-notify\r\n"
2984 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2986 gchar
*body
=g_strdup(
2987 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2988 "<roaming type=\"categories\"/>"
2989 "<roaming type=\"containers\"/>"
2990 "<roaming type=\"subscribers\"/></roamingList>");
2993 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3002 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
3004 gchar
*to
= sip_uri_self(sip
);
3005 gchar
*tmp
= get_contact(sip
);
3006 gchar
*hdr
= g_strdup_printf(
3007 "Event: vnd-microsoft-provisioning\r\n"
3008 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
3009 "Supported: com.microsoft.autoextend\r\n"
3010 "Supported: ms-benotify\r\n"
3011 "Proxy-Require: ms-benotify\r\n"
3012 "Supported: ms-piggyback-first-notify\r\n"
3014 "Contact: %s\r\n", tmp
);
3017 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
3022 /** Subscription for provisioning information to help with initial
3023 * configuration. This subscription is a one-time query (denoted by the Expires header,
3024 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
3025 * configuration, meeting policies, and policy settings that Communicator must enforce.
3026 * TODO: for what we need this information.
3029 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
3031 gchar
*to
= sip_uri_self(sip
);
3032 gchar
*tmp
= get_contact(sip
);
3033 gchar
*hdr
= g_strdup_printf(
3034 "Event: vnd-microsoft-provisioning-v2\r\n"
3035 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
3036 "Supported: com.microsoft.autoextend\r\n"
3037 "Supported: ms-benotify\r\n"
3038 "Proxy-Require: ms-benotify\r\n"
3039 "Supported: ms-piggyback-first-notify\r\n"
3042 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
3043 gchar
*body
= g_strdup(
3044 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
3045 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
3046 "<provisioningGroup name=\"ucPolicy\"/>"
3047 "</provisioningGroupList>");
3050 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3057 sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
3058 gpointer value
, gpointer user_data
)
3060 struct sip_subscription
*subscription
= value
;
3061 struct sip_dialog
*dialog
= &subscription
->dialog
;
3062 struct sipe_account_data
*sip
= user_data
;
3063 gchar
*tmp
= get_contact(sip
);
3064 gchar
*hdr
= g_strdup_printf(
3067 "Contact: %s\r\n", subscription
->event
, tmp
);
3070 /* Rate limit to max. 25 requests per seconds */
3071 g_usleep(1000000 / 25);
3073 send_sip_request(sip
->gc
, "SUBSCRIBE", dialog
->with
, dialog
->with
, hdr
, NULL
, dialog
, NULL
);
3077 /* IM Session (INVITE and MESSAGE methods) */
3079 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
3081 get_end_points (struct sipe_account_data
*sip
,
3082 struct sip_session
*session
)
3086 if (session
== NULL
) {
3090 res
= g_strdup_printf("<sip:%s>", sip
->username
);
3092 SIPE_DIALOG_FOREACH
{
3094 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
3097 if (dialog
->theirepid
) {
3099 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
3102 } SIPE_DIALOG_FOREACH_END
;
3108 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
3110 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3112 gboolean ret
= TRUE
;
3114 if (msg
->response
!= 200) {
3115 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
3119 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
3125 * Asks UA/proxy about its capabilities.
3127 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
3129 gchar
*to
= sip_uri(who
);
3130 gchar
*contact
= get_contact(sip
);
3131 gchar
*request
= g_strdup_printf(
3132 "Accept: application/sdp\r\n"
3133 "Contact: %s\r\n", contact
);
3136 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
3143 sipe_notify_user(struct sipe_account_data
*sip
,
3144 struct sip_session
*session
,
3145 PurpleMessageFlags flags
,
3146 const gchar
*message
)
3148 PurpleConversation
*conv
;
3150 if (!session
->conv
) {
3151 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
3153 conv
= session
->conv
;
3155 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
3159 sipe_present_info(struct sipe_account_data
*sip
,
3160 struct sip_session
*session
,
3161 const gchar
*message
)
3163 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
3167 sipe_present_err(struct sipe_account_data
*sip
,
3168 struct sip_session
*session
,
3169 const gchar
*message
)
3171 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
3175 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
3176 struct sip_session
*session
,
3179 const gchar
*message
)
3181 char *msg
, *msg_tmp
, *msg_tmp2
;
3184 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
3185 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
3187 /* Service unavailable; Server Internal Error; Server Time-out */
3188 if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
3189 label
= _("This message was not delivered to %s because the service is not available");
3190 } else if (sip_error
== 486) { /* Busy Here */
3191 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
3193 label
= _("This message was not delivered to %s because one or more recipients are offline");
3196 msg_tmp
= g_strdup_printf( "%s:\n%s" ,
3197 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""), msg
? msg
: "");
3198 sipe_present_err(sip
, session
, msg_tmp
);
3205 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
);
3208 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
3209 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3211 gboolean ret
= TRUE
;
3212 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3213 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
3214 struct sip_dialog
*dialog
;
3220 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
3225 dialog
= sipe_dialog_find(session
, with
);
3227 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
3232 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3233 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
3235 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3237 if (msg
->response
>= 400) {
3238 PurpleBuddy
*pbuddy
;
3239 gchar
*alias
= with
;
3241 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
3243 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
3244 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
3247 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, alias
, message
);
3250 gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
3252 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
));
3253 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
3254 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
3257 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3258 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
3259 key
, g_hash_table_size(session
->unconfirmed_messages
));
3265 if (ret
) sipe_im_process_queue(sip
, session
);
3270 sipe_is_election_finished(struct sip_session
*session
);
3273 sipe_election_result(struct sipe_account_data
*sip
,
3277 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
3278 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3280 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3281 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3282 struct sip_dialog
*dialog
;
3283 struct sip_session
*session
;
3285 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3287 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
3291 if (msg
->response
== 200 && !strncmp(contenttype
, "application/x-ms-mim", 20)) {
3292 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3293 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
3294 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
3296 if (xn_request_rm_response
) {
3297 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
3298 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
3300 dialog
= sipe_dialog_find(session
, with
);
3302 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
3306 if (allow
&& !g_strcasecmp(allow
, "true")) {
3307 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
3308 dialog
->election_vote
= 1;
3309 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
3310 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
3311 dialog
->election_vote
= -1;
3314 if (sipe_is_election_finished(session
)) {
3315 sipe_election_result(sip
, session
);
3318 } else if (xn_set_rm_response
) {
3321 xmlnode_free(xn_action
);
3328 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
)
3337 sipe_parse_html(msg
, &msgformat
, &msgtext
);
3338 purple_debug_info("sipe", "sipe_send_message: msgformat=%s\n", msgformat
);
3340 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3343 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
3346 msgr
= g_strdup("");
3349 tmp
= get_contact(sip
);
3350 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
3351 //hdr = g_strdup("Content-Type: text/rtf\r\n");
3352 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
3353 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n", tmp
, msgr
);
3357 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
3364 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
3366 GSList
*entry2
= session
->outgoing_message_queue
;
3368 char *queued_msg
= entry2
->data
;
3370 /* for multiparty chat or conference */
3371 if (session
->is_multiparty
|| session
->focus_uri
) {
3372 gchar
*who
= sip_uri_self(sip
);
3373 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
3374 PURPLE_MESSAGE_SEND
, queued_msg
, time(NULL
));
3378 SIPE_DIALOG_FOREACH
{
3381 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
3383 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
3384 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
3385 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
3386 key
, g_hash_table_size(session
->unconfirmed_messages
));
3389 sipe_send_message(sip
, dialog
, queued_msg
);
3390 } SIPE_DIALOG_FOREACH_END
;
3392 entry2
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
3398 sipe_refer_notify(struct sipe_account_data
*sip
,
3399 struct sip_session
*session
,
3406 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3408 hdr
= g_strdup_printf(
3410 "Subscription-State: %s\r\n"
3411 "Content-Type: message/sipfrag\r\n",
3412 status
>= 200 ? "terminated" : "active");
3414 body
= g_strdup_printf(
3415 "SIP/2.0 %d %s\r\n",
3418 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
3425 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
3427 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3428 struct sip_session
*session
;
3429 struct sip_dialog
*dialog
;
3433 struct sipmsg
*request_msg
= trans
->msg
;
3435 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3438 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3440 session
= sipe_session_find_im(sip
, with
);
3443 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
3448 dialog
= sipe_dialog_find(session
, with
);
3450 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
3455 sipe_dialog_parse(dialog
, msg
, TRUE
);
3457 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3458 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
3460 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3462 if (msg
->response
!= 200) {
3463 PurpleBuddy
*pbuddy
;
3464 gchar
*alias
= with
;
3466 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
3468 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
3469 alias
= (gchar
*)purple_buddy_get_alias(pbuddy
);
3473 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, alias
, message
);
3475 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
3476 sipe_present_err(sip
, session
, tmp_msg
);
3480 sipe_dialog_remove(session
, with
);
3488 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
3489 dialog
->outgoing_invite
= NULL
;
3490 dialog
->is_established
= TRUE
;
3492 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
3494 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
3495 g_free(referred_by
);
3498 /* add user to chat if it is a multiparty session */
3499 if (session
->is_multiparty
) {
3500 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
3502 PURPLE_CBFLAGS_NONE
, TRUE
);
3505 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
3506 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
3507 if (session
->outgoing_message_queue
) {
3508 char *queued_msg
= session
->outgoing_message_queue
->data
;
3509 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
3514 sipe_im_process_queue(sip
, session
);
3516 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3517 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
3518 key
, g_hash_table_size(session
->unconfirmed_messages
));
3527 sipe_invite(struct sipe_account_data
*sip
,
3528 struct sip_session
*session
,
3530 const gchar
*msg_body
,
3531 const gchar
*referred_by
,
3532 const gboolean is_triggered
)
3539 char *ms_text_format
= NULL
;
3540 gchar
*roster_manager
;
3542 gchar
*referred_by_str
;
3543 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3545 if (dialog
&& dialog
->is_established
) {
3546 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
3551 dialog
= sipe_dialog_add(session
);
3552 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
3553 dialog
->with
= g_strdup(who
);
3556 if (!(dialog
->ourtag
)) {
3557 dialog
->ourtag
= gentag();
3570 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3571 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
3573 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3577 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
3581 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3582 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
3587 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3588 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
3589 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
3590 key
, g_hash_table_size(session
->unconfirmed_messages
));
3594 contact
= get_contact(sip
);
3595 end_points
= get_end_points(sip
, session
);
3596 self
= sip_uri_self(sip
);
3597 roster_manager
= g_strdup_printf(
3598 "Roster-Manager: %s\r\n"
3599 "EndPoints: %s\r\n",
3602 referred_by_str
= referred_by
?
3604 "Referred-By: %s\r\n",
3607 hdr
= g_strdup_printf(
3608 "Supported: ms-sender\r\n"
3614 "Content-Type: application/sdp\r\n",
3615 (session
->roster_manager
&& !strcmp(session
->roster_manager
, self
)) ? roster_manager
: "",
3617 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3618 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3620 ms_text_format
? ms_text_format
: "");
3621 g_free(ms_text_format
);
3624 body
= g_strdup_printf(
3626 "o=- 0 0 IN IP4 %s\r\n"
3630 "m=message %d sip null\r\n"
3631 "a=accept-types:text/plain text/html image/gif "
3632 "multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
3633 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
3635 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
3636 to
, to
, hdr
, body
, dialog
, process_invite_response
);
3639 g_free(roster_manager
);
3641 g_free(referred_by_str
);
3648 sipe_refer(struct sipe_account_data
*sip
,
3649 struct sip_session
*session
,
3654 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
3655 session
->roster_manager
);
3657 contact
= get_contact(sip
);
3658 hdr
= g_strdup_printf(
3660 "Refer-to: <%s>\r\n"
3661 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
3662 "Require: com.microsoft.rtc-multiparty\r\n",
3666 dialog
->ourtag
? ";tag=" : "",
3667 dialog
->ourtag
? dialog
->ourtag
: "",
3670 send_sip_request(sip
->gc
, "REFER",
3671 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
3678 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
3679 struct sip_dialog
*dialog
,
3682 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3684 gchar
*body
= g_strdup_printf(
3685 "<?xml version=\"1.0\"?>\r\n"
3686 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3687 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
3688 sip
->username
, bid
);
3690 send_sip_request(sip
->gc
, "INFO",
3691 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3697 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
3698 struct sip_dialog
*dialog
)
3700 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
3702 gchar
*body
= g_strdup_printf(
3703 "<?xml version=\"1.0\"?>\r\n"
3704 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3705 "<SetRM uri=\"sip:%s\"/></action>\r\n",
3708 send_sip_request(sip
->gc
, "INFO",
3709 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
3715 sipe_session_close(struct sipe_account_data
*sip
,
3716 struct sip_session
* session
)
3718 if (session
&& session
->focus_uri
) {
3719 sipe_conf_immcu_closed(sip
, session
);
3720 conf_session_close(sip
, session
);
3724 SIPE_DIALOG_FOREACH
{
3725 /* @TODO slow down BYE message sending rate */
3726 /* @see single subscription code */
3727 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
3728 } SIPE_DIALOG_FOREACH_END
;
3730 sipe_session_remove(sip
, session
);
3735 sipe_session_close_all(struct sipe_account_data
*sip
)
3738 while ((entry
= sip
->sessions
) != NULL
) {
3739 sipe_session_close(sip
, entry
->data
);
3744 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3746 struct sipe_account_data
*sip
= gc
->proto_data
;
3748 purple_debug_info("sipe", "conversation with %s closed\n", who
);
3749 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
3753 sipe_chat_leave (PurpleConnection
*gc
, int id
)
3755 struct sipe_account_data
*sip
= gc
->proto_data
;
3756 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
3758 sipe_session_close(sip
, session
);
3761 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
3762 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3764 struct sipe_account_data
*sip
= gc
->proto_data
;
3765 struct sip_session
*session
;
3766 struct sip_dialog
*dialog
;
3768 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
3770 session
= sipe_session_find_or_add_im(sip
, who
);
3771 dialog
= sipe_dialog_find(session
, who
);
3773 // Queue the message
3774 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
3776 if (dialog
&& !dialog
->outgoing_invite
) {
3777 sipe_im_process_queue(sip
, session
);
3778 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3779 // Need to send the INVITE to get the outgoing dialog setup
3780 sipe_invite(sip
, session
, who
, what
, NULL
, FALSE
);
3786 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
3787 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3789 struct sipe_account_data
*sip
= gc
->proto_data
;
3790 struct sip_session
*session
;
3792 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
3794 session
= sipe_session_find_chat_by_id(sip
, id
);
3796 // Queue the message
3797 if (session
&& session
->dialogs
) {
3798 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
3800 sipe_im_process_queue(sip
, session
);
3802 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
3803 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
3805 purple_debug_info("sipe", "sipe_chat_send: chat_name='%s'\n", chat_name
? chat_name
: "NULL");
3806 purple_debug_info("sipe", "sipe_chat_send: proto_chat_id='%s'\n", proto_chat_id
? proto_chat_id
: "NULL");
3809 struct sip_session
*session
= sipe_session_add_chat(sip
);
3811 session
->is_multiparty
= FALSE
;
3812 session
->focus_uri
= g_strdup(proto_chat_id
);
3813 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
,
3815 sipe_invite_conf_focus(sip
, session
);
3822 /* End IM Session (INVITE and MESSAGE methods) */
3824 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3826 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
3827 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3829 struct sip_session
*session
;
3831 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
3833 /* Call Control protocol */
3834 if (g_str_has_prefix(contenttype
, "application/csta+xml"))
3836 process_incoming_info_csta(sip
, msg
);
3840 from
= parse_from(sipmsg_find_header(msg
, "From"));
3841 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3843 session
= sipe_session_find_im(sip
, from
);
3850 if (g_str_has_prefix(contenttype
, "application/x-ms-mim"))
3852 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3853 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
3854 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
3856 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
3858 if (xn_request_rm
) {
3859 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
3860 int bid
= atoi(xmlnode_get_attrib(xn_request_rm
, "bid"));
3861 gchar
*body
= g_strdup_printf(
3862 "<?xml version=\"1.0\"?>\r\n"
3863 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3864 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
3866 session
->bid
< bid
? "true" : "false");
3867 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3869 } else if (xn_set_rm
) {
3871 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
3872 g_free(session
->roster_manager
);
3873 session
->roster_manager
= g_strdup(rm
);
3875 body
= g_strdup_printf(
3876 "<?xml version=\"1.0\"?>\r\n"
3877 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
3878 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
3880 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3883 xmlnode_free(xn_action
);
3888 /* looks like purple lacks typing notification for chat */
3889 if (!session
->is_multiparty
&& !session
->focus_uri
) {
3890 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3891 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
3893 if (status
&& !strcmp(status
, "type")) {
3894 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3895 } else if (status
&& !strcmp(status
, "idle")) {
3896 serv_got_typing_stopped(sip
->gc
, from
);
3898 xmlnode_free(xn_keyboard_activity
);
3901 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3906 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3908 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3909 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3910 struct sip_session
*session
;
3911 struct sip_dialog
*dialog
;
3913 /* collect dialog identification
3914 * we need callid, ourtag and theirtag to unambiguously identify dialog
3916 /* take data before 'msg' will be modified by send_sip_response */
3917 dialog
= g_new0(struct sip_dialog
, 1);
3918 dialog
->callid
= g_strdup(callid
);
3919 dialog
->cseq
= parse_cseq(sipmsg_find_header(msg
, "CSeq"));
3920 dialog
->with
= g_strdup(from
);
3921 sipe_dialog_parse(dialog
, msg
, FALSE
);
3923 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3925 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3927 session
= sipe_session_find_im(sip
, from
);
3934 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
3935 g_free(session
->roster_manager
);
3936 session
->roster_manager
= NULL
;
3939 /* This what BYE is essentially for - terminating dialog */
3940 sipe_dialog_remove_3(session
, dialog
);
3941 sipe_dialog_free(dialog
);
3942 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
3943 sipe_conf_immcu_closed(sip
, session
);
3944 } else if (session
->is_multiparty
) {
3945 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
3951 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3953 gchar
*self
= sip_uri_self(sip
);
3954 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3955 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3956 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
3957 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
3958 struct sip_session
*session
;
3959 struct sip_dialog
*dialog
;
3961 session
= sipe_session_find_chat_by_callid(sip
, callid
);
3962 dialog
= sipe_dialog_find(session
, from
);
3964 if (!session
|| !dialog
|| !session
->roster_manager
|| strcmp(session
->roster_manager
, self
)) {
3965 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
3967 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
3969 sipe_invite(sip
, session
, refer_to
, NULL
, referred_by
, FALSE
);
3975 g_free(referred_by
);
3979 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
3981 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
3982 struct sip_session
*session
;
3983 struct sip_dialog
*dialog
;
3985 if (state
== PURPLE_NOT_TYPING
)
3988 session
= sipe_session_find_im(sip
, who
);
3989 dialog
= sipe_dialog_find(session
, who
);
3991 if (session
&& dialog
&& dialog
->is_established
) {
3992 send_sip_request(gc
, "INFO", who
, who
,
3993 "Content-Type: application/xml\r\n",
3994 SIPE_SEND_TYPING
, dialog
, NULL
);
3996 return SIPE_TYPING_SEND_TIMEOUT
;
3999 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
4001 GSList
*tmp
= sip
->transactions
;
4002 time_t currtime
= time(NULL
);
4004 struct transaction
*trans
= tmp
->data
;
4006 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
4007 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
4010 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
4012 sendout_sipmsg(sip
, trans
->msg
);
4019 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
4020 SIPE_UNUSED_PARAMETER
void *unused
)
4022 /* register again when security token expires */
4023 /* we have to start a new authentication as the security token
4024 * is almost expired by sending a not signed REGISTER message */
4025 purple_debug_info("sipe", "do a full reauthentication\n");
4026 sipe_auth_free(&sip
->registrar
);
4027 sipe_auth_free(&sip
->proxy
);
4028 sip
->registerstatus
= 0;
4030 sip
->reauthenticate_set
= FALSE
;
4033 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4037 gboolean found
= FALSE
;
4039 from
= parse_from(sipmsg_find_header(msg
, "From"));
4043 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
4045 contenttype
= sipmsg_find_header(msg
, "Content-Type");
4046 if (!strncmp(contenttype
, "text/plain", 10)
4047 || !strncmp(contenttype
, "text/html", 9)
4048 || !strncmp(contenttype
, "multipart/related", 21))
4050 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4051 gchar
*html
= get_html_message(contenttype
, msg
->body
);
4053 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
4055 session
= sipe_session_find_im(sip
, from
);
4058 if (session
&& session
->focus_uri
) { /* a conference */
4059 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
4060 gchar
*sender
= parse_from(tmp
);
4062 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
4063 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4065 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
4066 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
4067 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4069 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
4072 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4075 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
4076 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4081 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
4085 state
= xmlnode_get_child(isc
, "state");
4088 purple_debug_info("sipe", "process_incoming_message: no state found\n");
4093 statedata
= xmlnode_get_data(state
);
4095 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
4096 else serv_got_typing_stopped(sip
->gc
, from
);
4101 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4105 purple_debug_info("sipe", "got unknown mime-type");
4106 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
4111 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4117 gboolean is_multiparty
= FALSE
;
4118 gboolean is_triggered
= FALSE
;
4119 gboolean was_multiparty
= TRUE
;
4120 gboolean just_joined
= FALSE
;
4122 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4123 gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
4124 gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
4125 gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
4126 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4127 GSList
*end_points
= NULL
;
4129 struct sip_session
*session
;
4131 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? tmp
= fix_newlines(msg
->body
) : "");
4134 /* Invitation to join conference */
4135 if (!strncmp(content_type
, "application/ms-conf-invite+xml", 30)) {
4136 process_incoming_invite_conf(sip
, msg
);
4140 /* Only accept text invitations */
4141 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
4142 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4146 // TODO There *must* be a better way to clean up the To header to add a tag...
4147 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
4148 oldHeader
= sipmsg_find_header(msg
, "To");
4150 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
4151 sipmsg_remove_header_now(msg
, "To");
4152 sipmsg_add_header_now(msg
, "To", newHeader
);
4155 if (end_points_hdr
) {
4156 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
4158 if (g_slist_length(end_points
) > 2) {
4159 is_multiparty
= TRUE
;
4162 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
4163 is_triggered
= TRUE
;
4164 is_multiparty
= TRUE
;
4167 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4168 /* Convert to multiparty */
4169 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
4170 g_free(session
->with
);
4171 session
->with
= NULL
;
4172 was_multiparty
= FALSE
;
4173 session
->is_multiparty
= TRUE
;
4174 session
->chat_id
= rand();
4177 if (!session
&& is_multiparty
) {
4178 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
4181 from
= parse_from(sipmsg_find_header(msg
, "From"));
4183 session
= sipe_session_find_or_add_im(sip
, from
);
4186 g_free(session
->callid
);
4187 session
->callid
= g_strdup(callid
);
4189 session
->is_multiparty
= is_multiparty
;
4190 if (roster_manager
) {
4191 session
->roster_manager
= g_strdup(roster_manager
);
4194 if (is_multiparty
&& end_points
) {
4195 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
4196 GSList
*entry
= end_points
;
4198 struct sip_dialog
*dialog
;
4199 struct sipendpoint
*end_point
= entry
->data
;
4200 entry
= entry
->next
;
4202 if (!g_strcasecmp(from
, end_point
->contact
) ||
4203 !g_strcasecmp(to
, end_point
->contact
))
4206 dialog
= sipe_dialog_find(session
, end_point
->contact
);
4208 g_free(dialog
->theirepid
);
4209 dialog
->theirepid
= end_point
->epid
;
4210 end_point
->epid
= NULL
;
4212 dialog
= sipe_dialog_add(session
);
4214 dialog
->callid
= g_strdup(session
->callid
);
4215 dialog
->with
= end_point
->contact
;
4216 end_point
->contact
= NULL
;
4217 dialog
->theirepid
= end_point
->epid
;
4218 end_point
->epid
= NULL
;
4222 /* send triggered INVITE */
4223 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, TRUE
);
4230 GSList
*entry
= end_points
;
4232 struct sipendpoint
*end_point
= entry
->data
;
4233 entry
= entry
->next
;
4234 g_free(end_point
->contact
);
4235 g_free(end_point
->epid
);
4238 g_slist_free(end_points
);
4242 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
4244 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
4246 dialog
= sipe_dialog_add(session
);
4248 dialog
->callid
= g_strdup(session
->callid
);
4249 dialog
->with
= g_strdup(from
);
4250 sipe_dialog_parse(dialog
, msg
, FALSE
);
4252 if (!dialog
->ourtag
) {
4253 dialog
->ourtag
= newTag
;
4260 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
4264 if (is_multiparty
&& !session
->conv
) {
4265 gchar
*chat_title
= sipe_chat_get_name(callid
);
4266 gchar
*self
= sip_uri_self(sip
);
4267 /* create prpl chat */
4268 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_title
);
4269 session
->chat_title
= g_strdup(chat_title
);
4270 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
4272 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4274 PURPLE_CBFLAGS_NONE
, FALSE
);
4279 if (is_multiparty
&& !was_multiparty
) {
4280 /* add current IM counterparty to chat */
4281 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4282 sipe_dialog_first(session
)->with
, NULL
,
4283 PURPLE_CBFLAGS_NONE
, FALSE
);
4286 /* add inviting party to chat */
4287 if (just_joined
&& session
->conv
) {
4288 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4290 PURPLE_CBFLAGS_NONE
, TRUE
);
4293 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
4295 /* This used only in 2005 official client, not 2007 or Reuters.
4296 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
4297 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
4299 if (is_multiparty
) {
4300 /* please do not optimize logic inside as this code may be re-enabled for other cases */
4301 gchar
*ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
4302 if (ms_text_format
) {
4303 if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html")) {
4305 gchar
*html
= get_html_message(ms_text_format
, NULL
);
4307 if (is_multiparty
) {
4308 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
4309 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
4311 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
4314 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
4323 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
4324 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
4325 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
4327 body
= g_strdup_printf(
4329 "o=- 0 0 IN IP4 %s\r\n"
4333 "m=message %d sip sip:%s\r\n"
4334 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
4335 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
4336 sip
->realport
, sip
->username
);
4337 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4341 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4345 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
4346 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
4347 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
4349 body
= g_strdup_printf(
4351 "o=- 0 0 IN IP4 0.0.0.0\r\n"
4353 "c=IN IP4 0.0.0.0\r\n"
4355 "m=message %d sip sip:%s\r\n"
4356 "a=accept-types:text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml\r\n",
4357 sip
->realport
, sip
->username
);
4358 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4362 static void sipe_connection_cleanup(struct sipe_account_data
*);
4363 static void create_connection(struct sipe_account_data
*, gchar
*, int);
4365 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4366 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4369 const gchar
*expires_header
;
4371 GSList
*hdr
= msg
->headers
;
4372 struct siphdrelement
*elem
;
4374 expires_header
= sipmsg_find_header(msg
, "Expires");
4375 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
4376 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
4378 switch (msg
->response
) {
4381 sip
->registerstatus
= 0;
4383 gchar
*contact_hdr
= NULL
;
4388 gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
4390 if (!sip
->reregister_set
) {
4391 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
4392 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
4393 g_free(action_name
);
4394 sip
->reregister_set
= TRUE
;
4397 sip
->registerstatus
= 3;
4399 if (server_hdr
&& !sip
->server_version
) {
4400 sip
->server_version
= g_strdup(server_hdr
);
4406 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4408 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
4411 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4415 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
4416 fill_auth(tmp
, &sip
->registrar
);
4419 if (!sip
->reauthenticate_set
) {
4420 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
4421 guint reauth_timeout
;
4422 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
4423 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
4424 reauth_timeout
= sip
->registrar
.expires
- 300;
4426 /* NTLM: we have to reauthenticate as our security token expires
4427 after eight hours (be five minutes early) */
4428 reauth_timeout
= (8 * 3600) - 300;
4430 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
4431 g_free(action_name
);
4432 sip
->reauthenticate_set
= TRUE
;
4435 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
4437 epid
= get_epid(sip
);
4438 uuid
= generateUUIDfromEPID(epid
);
4441 // There can be multiple Contact headers (one per location where the user is logged in) so
4442 // make sure to only get the one for this uuid
4443 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
4444 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
4445 if (valid_contact
) {
4446 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
4447 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
4448 g_free(valid_contact
);
4451 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
4456 g_free(sip
->contact
);
4458 sip
->contact
= g_strdup_printf("<%s>", gruu
);
4461 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
4462 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
);
4464 sip
->ocs2007
= FALSE
;
4465 sip
->batched_support
= FALSE
;
4470 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
4471 if (!g_ascii_strcasecmp(elem
->value
, "msrtc-event-categories")) {
4472 /* We interpret this as OCS2007+ indicator */
4473 sip
->ocs2007
= TRUE
;
4474 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
4476 if (!g_ascii_strcasecmp(elem
->value
, "adhoclist")) {
4477 sip
->batched_support
= TRUE
;
4478 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
4481 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
4482 gchar
**caps
= g_strsplit(elem
->value
,",",0);
4485 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
4486 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
4491 hdr
= g_slist_next(hdr
);
4494 /* rejoin open chats to be able to use them by continue to send messages */
4495 purple_conversation_foreach(sipe_rejoin_chat
);
4498 if (!sip
->subscribed
) { //do it just once, not every re-register
4500 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
4501 (GCompareFunc
)g_ascii_strcasecmp
)) {
4502 sipe_subscribe_roaming_contacts(sip
);
4505 /* For 2007+ it does not make sence to subscribe to:
4506 * vnd-microsoft-roaming-ACL
4507 * vnd-microsoft-provisioning (not v2)
4509 * These are for backward compatibility.
4513 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
4514 (GCompareFunc
)g_ascii_strcasecmp
)) {
4515 sipe_subscribe_roaming_self(sip
);
4517 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
4518 (GCompareFunc
)g_ascii_strcasecmp
)) {
4519 sipe_subscribe_roaming_provisioning_v2(sip
);
4522 /* For 2005- servers */
4525 //sipe_options_request(sip, sip->sipdomain);
4527 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
4528 (GCompareFunc
)g_ascii_strcasecmp
)) {
4529 sipe_subscribe_roaming_acl(sip
);
4531 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
4532 (GCompareFunc
)g_ascii_strcasecmp
)) {
4533 sipe_subscribe_roaming_provisioning(sip
);
4535 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
4536 (GCompareFunc
)g_ascii_strcasecmp
)) {
4537 sipe_subscribe_presence_wpending(sip
, msg
);
4540 /* For 2007+ we publish our initial statuses only after
4541 * received our existing publications in sipe_process_roaming_self()
4542 * Only in this case we know versions of current publications made
4545 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
4548 sip
->subscribed
= TRUE
;
4551 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
4552 "timeout=", ";", NULL
);
4553 if (timeout
!= NULL
) {
4554 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
4555 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
4556 sip
->keepalive_timeout
);
4560 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
4565 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
4567 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
4568 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
4572 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
4575 tmp
= g_strsplit(parts
[0], ":", 0);
4576 hostname
= g_strdup(tmp
[0]);
4577 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
4581 tmp
= g_strsplit(parts
[i
], "=", 0);
4583 if (g_strcasecmp("transport", tmp
[0]) == 0) {
4584 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
4585 transport
= SIPE_TRANSPORT_TCP
;
4586 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
4587 transport
= SIPE_TRANSPORT_UDP
;
4596 /* Close old connection */
4597 sipe_connection_cleanup(sip
);
4599 /* Create new connection */
4600 sip
->transport
= transport
;
4601 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
4602 hostname
, port
, TRANSPORT_DESCRIPTOR
);
4603 create_connection(sip
, hostname
, port
);
4609 if (sip
->registerstatus
!= 2) {
4610 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
4611 if (sip
->registrar
.retries
> 3) {
4612 sip
->gc
->wants_to_die
= TRUE
;
4613 purple_connection_error(sip
->gc
, _("Wrong password"));
4617 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4619 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
4622 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4625 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
4626 fill_auth(tmp
, &sip
->registrar
);
4627 sip
->registerstatus
= 2;
4628 if (sip
->account
->disconnecting
) {
4629 do_register_exp(sip
, 0);
4637 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
4638 gchar
**reason
= NULL
;
4639 if (warning
!= NULL
) {
4641 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
4643 reason
= g_strsplit(warning
, "\"", 0);
4645 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
4646 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
4649 sip
->gc
->wants_to_die
= TRUE
;
4650 purple_connection_error(sip
->gc
, warning
);
4657 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4658 gchar
*reason
= NULL
;
4659 if (warning
!= NULL
) {
4660 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4662 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
4663 warning
? (reason
? reason
: _("no reason given")) :
4664 _("SIP is either not enabled for the destination URI or it does not exist"));
4667 sip
->gc
->wants_to_die
= TRUE
;
4668 purple_connection_error(sip
->gc
, warning
);
4674 case 504: /* Server time-out */
4676 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
4677 gchar
*reason
= NULL
;
4678 if (warning
!= NULL
) {
4679 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
4681 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
4684 sip
->gc
->wants_to_die
= TRUE
;
4685 purple_connection_error(sip
->gc
, warning
);
4695 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
4698 sipe_get_status_by_availability(int avail
)
4703 status
= SIPE_STATUS_ID_OFFLINE
;
4704 else if (avail
< 4500)
4705 status
= SIPE_STATUS_ID_AVAILABLE
;
4706 else if (avail
< 6000)
4707 status
= SIPE_STATUS_ID_IDLE
;
4708 else if (avail
< 7500)
4709 status
= SIPE_STATUS_ID_BUSY
;
4710 else if (avail
< 9000)
4711 status
= SIPE_STATUS_ID_AWAY
;
4712 else if (avail
< 12000)
4713 status
= SIPE_STATUS_ID_DND
;
4714 else if (avail
< 15000)
4715 status
= SIPE_STATUS_ID_BRB
;
4716 else if (avail
< 18000)
4717 status
= SIPE_STATUS_ID_AWAY
;
4719 status
= SIPE_STATUS_ID_OFFLINE
;
4724 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4727 xmlnode
*xn_categories
;
4728 xmlnode
*xn_category
;
4731 xn_categories
= xmlnode_from_str(data
, len
);
4732 uri
= xmlnode_get_attrib(xn_categories
, "uri"); /* with 'sip:' prefix */
4734 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
4736 xn_category
= xmlnode_get_next_twin(xn_category
) )
4738 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
4741 if (!strcmp(attrVar
, "contactCard"))
4744 /* identity - Display Name and email */
4745 node
= xmlnode_get_descendant(xn_category
, "contactCard", "identity", NULL
);
4747 char* display_name
= xmlnode_get_data(
4748 xmlnode_get_descendant(node
, "name", "displayName", NULL
));
4749 char* email
= xmlnode_get_data(
4750 xmlnode_get_child(node
, "email"));
4752 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
4753 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
4755 g_free(display_name
);
4759 node
= xmlnode_get_descendant(xn_category
, "contactCard", "company", NULL
);
4761 char* company
= xmlnode_get_data(node
);
4762 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
4766 node
= xmlnode_get_descendant(xn_category
, "contactCard", "department", NULL
);
4768 char* department
= xmlnode_get_data(node
);
4769 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
4773 node
= xmlnode_get_descendant(xn_category
, "contactCard", "title", NULL
);
4775 char* title
= xmlnode_get_data(node
);
4776 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
4780 node
= xmlnode_get_descendant(xn_category
, "contactCard", "office", NULL
);
4782 char* office
= xmlnode_get_data(node
);
4783 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
4787 node
= xmlnode_get_descendant(xn_category
, "contactCard", "url", NULL
);
4789 char* site
= xmlnode_get_data(node
);
4790 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
4794 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "phone", NULL
);
4796 node
= xmlnode_get_next_twin(node
))
4798 const char *phone_type
= xmlnode_get_attrib(node
, "type");
4799 char* phone
= xmlnode_get_data(xmlnode_get_child(node
, "uri"));
4800 char* phone_display_string
= xmlnode_get_data(xmlnode_get_child(node
, "displayString"));
4802 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
4805 g_free(phone_display_string
);
4808 for (node
= xmlnode_get_descendant(xn_category
, "contactCard", "address", NULL
);
4810 node
= xmlnode_get_next_twin(node
))
4812 if (!strcmp(xmlnode_get_attrib(node
, "type"), "work")) {
4813 char* street
= xmlnode_get_data(xmlnode_get_child(node
, "street"));
4814 char* city
= xmlnode_get_data(xmlnode_get_child(node
, "city"));
4815 char* state
= xmlnode_get_data(xmlnode_get_child(node
, "state"));
4816 char* zipcode
= xmlnode_get_data(xmlnode_get_child(node
, "zipcode"));
4817 char* country_code
= xmlnode_get_data(xmlnode_get_child(node
, "countryCode"));
4819 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
4820 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
4821 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
4822 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
4823 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
4829 g_free(country_code
);
4836 else if (!strcmp(attrVar
, "note"))
4839 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
4844 xn_node
= xmlnode_get_child(xn_category
, "note");
4845 if (!xn_node
) continue;
4846 xn_node
= xmlnode_get_child(xn_node
, "body");
4847 if (!xn_node
) continue;
4848 note
= xmlnode_get_data(xn_node
);
4849 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
? note
: "");
4850 g_free(sbuddy
->annotation
);
4851 sbuddy
->annotation
= NULL
;
4852 if (note
) sbuddy
->annotation
= g_strdup(note
);
4859 else if(!strcmp(attrVar
, "state"))
4864 xmlnode
*xn_availability
;
4865 xmlnode
*xn_activity
;
4866 xmlnode
*xn_meeting_subject
;
4867 xmlnode
*xn_meeting_location
;
4868 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
4870 xn_node
= xmlnode_get_child(xn_category
, "state");
4871 if (!xn_node
) continue;
4872 xn_availability
= xmlnode_get_child(xn_node
, "availability");
4873 if (!xn_availability
) continue;
4874 xn_activity
= xmlnode_get_child(xn_node
, "activity");
4875 xn_meeting_subject
= xmlnode_get_child(xn_node
, "meetingSubject");
4876 xn_meeting_location
= xmlnode_get_child(xn_node
, "meetingLocation");
4878 data
= xmlnode_get_data(xn_availability
);
4879 availability
= atoi(data
);
4882 /* activity, meeting_subject, meeting_location */
4885 g_free(sbuddy
->activity
);
4886 sbuddy
->activity
= NULL
;
4888 const char *token
= xmlnode_get_attrib(xn_activity
, "token");
4889 xmlnode
*xn_custom
= xmlnode_get_child(xn_activity
, "custom");
4892 if (!is_empty(token
)) {
4893 if (!strcmp(token
, "on-the-phone")) {
4894 sbuddy
->activity
= g_strdup(_("On The Phone"));
4895 } else if (!strcmp(token
, "in-a-conference")) {
4896 sbuddy
->activity
= g_strdup(_("In a Conference"));
4897 } else if (!strcmp(token
, "in-a-meeting")) {
4898 sbuddy
->activity
= g_strdup(_("In a Meeting"));
4899 } else if (!strcmp(token
, "out-of-office")) {
4900 sbuddy
->activity
= g_strdup(_("Out of Office"));
4901 } else if (!strcmp(token
, "urgent-interruptions-only")) {
4902 sbuddy
->activity
= g_strdup(_("Urgent Interruptions Only"));
4905 /* form custom element */
4907 char *custom
= xmlnode_get_data(xn_custom
);
4909 if (!is_empty(custom
)) {
4910 sbuddy
->activity
= custom
;
4916 /* meeting_subject */
4917 g_free(sbuddy
->meeting_subject
);
4918 sbuddy
->meeting_subject
= NULL
;
4919 if (xn_meeting_subject
) {
4920 char *meeting_subject
= xmlnode_get_data(xn_meeting_subject
);
4922 if (!is_empty(meeting_subject
)) {
4923 sbuddy
->meeting_subject
= meeting_subject
;
4924 meeting_subject
= NULL
;
4926 g_free(meeting_subject
);
4928 /* meeting_location */
4929 g_free(sbuddy
->meeting_location
);
4930 sbuddy
->meeting_location
= NULL
;
4931 if (xn_meeting_location
) {
4932 char *meeting_location
= xmlnode_get_data(xn_meeting_location
);
4934 if (!is_empty(meeting_location
)) {
4935 sbuddy
->meeting_location
= meeting_location
;
4936 meeting_location
= NULL
;
4938 g_free(meeting_location
);
4942 status
= sipe_get_status_by_availability(availability
);
4944 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
4945 purple_prpl_got_user_status(sip
->account
, uri
, status
, NULL
);
4950 xmlnode_free(xn_categories
);
4953 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
4955 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4956 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
4957 payload
->host
= g_strdup(host
);
4958 payload
->buddies
= server
;
4959 sipe_subscribe_presence_batched_routed(sip
, payload
);
4960 sipe_subscribe_presence_batched_routed_free(payload
);
4963 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
4966 xmlnode
*xn_resource
;
4967 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4972 xn_list
= xmlnode_from_str(data
, len
);
4974 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
4976 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
4978 const char *uri
, *state
;
4979 xmlnode
*xn_instance
;
4981 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
4982 if (!xn_instance
) continue;
4984 uri
= xmlnode_get_attrib(xn_resource
, "uri");
4985 state
= xmlnode_get_attrib(xn_instance
, "state");
4986 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
4988 if (strstr(state
, "resubscribe")) {
4989 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
4991 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4992 gchar
*user
= g_strdup(uri
);
4993 host
= g_strdup(poolFqdn
);
4994 server
= g_hash_table_lookup(servers
, host
);
4995 server
= g_slist_append(server
, user
);
4996 g_hash_table_insert(servers
, host
, server
);
4998 sipe_subscribe_presence_single(sip
, (void *) uri
);
5003 /* Send out any deferred poolFqdn subscriptions */
5004 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
5005 g_hash_table_destroy(servers
);
5007 xmlnode_free(xn_list
);
5010 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5014 gchar
*activity
= NULL
;
5016 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
5017 gboolean isonline
= FALSE
;
5018 xmlnode
*display_name_node
;
5020 pidf
= xmlnode_from_str(data
, len
);
5022 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
5026 uri
= xmlnode_get_attrib(pidf
, "entity"); /* with 'sip:' prefix */
5028 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
5030 if ((status
= xmlnode_get_child(tuple
, "status"))) {
5031 basicstatus
= xmlnode_get_child(status
, "basic");
5036 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
5041 getbasic
= xmlnode_get_data(basicstatus
);
5043 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
5048 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
5049 if (strstr(getbasic
, "open")) {
5054 display_name_node
= xmlnode_get_child(pidf
, "display-name");
5055 if (display_name_node
) {
5056 char * display_name
= xmlnode_get_data(display_name_node
);
5058 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5059 g_free(display_name
);
5062 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
5063 if ((status
= xmlnode_get_child(tuple
, "status"))) {
5064 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
5065 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
5066 activity
= xmlnode_get_data(basicstatus
);
5067 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
5074 const gchar
* status_id
= NULL
;
5076 if (strstr(activity
, "busy")) {
5077 status_id
= SIPE_STATUS_ID_BUSY
;
5078 } else if (strstr(activity
, "away")) {
5079 status_id
= SIPE_STATUS_ID_AWAY
;
5084 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5087 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
5088 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
5090 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
5097 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5099 const char *activity
= NULL
;
5101 const char *status_id
= NULL
;
5106 const char *device_name
= NULL
;
5107 struct sipe_buddy
*sbuddy
;
5109 xmlnode
*xn_presentity
;
5110 xmlnode
*xn_availability
;
5111 xmlnode
*xn_activity
;
5112 xmlnode
*xn_display_name
;
5114 xmlnode
*xn_phone_number
;
5115 xmlnode
*xn_userinfo
;
5117 xmlnode
*xn_contact
;
5120 char *free_activity
;
5122 /* fix for Reuters environment on Linux */
5123 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
5125 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
5126 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
5129 xn_presentity
= xmlnode_from_str(data
, len
);
5132 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
5133 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
5134 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
5135 xn_email
= xmlnode_get_child(xn_presentity
, "email");
5136 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
5137 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
5138 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
5140 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
5141 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
5142 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
5144 free_activity
= NULL
;
5146 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
5147 uri
= sip_uri_from_name(name
);
5148 avl
= atoi(xmlnode_get_attrib(xn_availability
, "aggregate"));
5149 epid
= xmlnode_get_attrib(xn_availability
, "epid");
5150 act
= atoi(xmlnode_get_attrib(xn_activity
, "aggregate"));
5152 if (xn_display_name
) {
5153 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
5154 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
5155 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
5156 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
5157 char *tel_uri
= sip_to_tel_uri(phone_number
);
5159 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5160 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5161 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
5162 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
5165 g_free(phone_label
);
5166 g_free(phone_number
);
5168 g_free(display_name
);
5173 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
5175 /* Ex.: <tel type="work">tel:+3222220000</tel> */
5176 const char *phone_type
= xmlnode_get_attrib(node
, "type");
5177 char* phone
= xmlnode_get_data(node
);
5179 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
5187 activity
= _("Out of Office");
5190 /* devicePresence */
5191 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
5192 xmlnode
*xn_device_name
;
5196 if (strcmp(xmlnode_get_attrib(node
, "epid"), epid
)) continue;
5198 xn_device_name
= xmlnode_get_child(node
, "deviceName");
5199 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
5201 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
5202 state
= xn_state
? xmlnode_get_data(xn_state
) : NULL
;
5204 if (!is_empty(state
)) {
5205 if (!strcmp(state
, "on-the-phone")) {
5206 activity
= _("On The Phone");
5207 } else if (!strcmp(state
, "presenting")) {
5208 activity
= _("In a Conference");
5210 activity
= free_activity
= state
;
5218 /* [MS-SIP] 2.2.1 */
5220 status_id
= SIPE_STATUS_ID_AWAY
;
5222 status_id
= SIPE_STATUS_ID_LUNCH
;
5224 status_id
= SIPE_STATUS_ID_IDLE
;
5226 status_id
= SIPE_STATUS_ID_BRB
;
5228 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5230 status_id
= SIPE_STATUS_ID_ONPHONE
;
5232 status_id
= SIPE_STATUS_ID_BUSY
;
5234 status_id
= SIPE_STATUS_ID_AWAY
;
5236 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5239 status_id
= SIPE_STATUS_ID_OFFLINE
;
5242 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
5245 g_free(sbuddy
->activity
);
5246 sbuddy
->activity
= NULL
;
5247 if (!is_empty(activity
)) { sbuddy
->activity
= g_strdup(activity
); }
5249 g_free(sbuddy
->annotation
);
5250 sbuddy
->annotation
= NULL
;
5251 if (!is_empty(note
)) { sbuddy
->annotation
= g_strdup(note
); }
5253 g_free(sbuddy
->device_name
);
5254 sbuddy
->device_name
= NULL
;
5255 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
5258 if (free_activity
) g_free(free_activity
);
5260 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
5261 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
5263 xmlnode_free(xn_presentity
);
5267 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5269 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
5271 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
5273 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
5274 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
5276 const char *content
= msg
->body
;
5277 unsigned length
= msg
->bodylen
;
5278 PurpleMimeDocument
*mime
= NULL
;
5280 if (strstr(ctype
, "multipart"))
5282 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
5283 const char *content_type
;
5285 mime
= purple_mime_document_parse(doc
);
5286 parts
= purple_mime_document_get_parts(mime
);
5288 content
= purple_mime_part_get_data(parts
->data
);
5289 length
= purple_mime_part_get_length(parts
->data
);
5290 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
5291 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
5293 process_incoming_notify_rlmi_resub(sip
, content
, length
);
5295 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
5297 process_incoming_notify_msrtc(sip
, content
, length
);
5301 process_incoming_notify_rlmi(sip
, content
, length
);
5303 parts
= parts
->next
;
5309 purple_mime_document_free(mime
);
5312 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
5314 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
5316 else if(strstr(ctype
, "application/rlmi+xml"))
5318 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
5321 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
5323 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
5327 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
5331 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
5333 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
5334 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
5336 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
5339 strstr(ctype
, "multipart") &&
5340 (strstr(ctype
, "application/rlmi+xml") ||
5341 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
5342 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
5343 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
5344 GList
*parts
= purple_mime_document_get_parts(mime
);
5345 GSList
*buddies
= NULL
;
5346 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
5349 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
5350 purple_mime_part_get_length(parts
->data
));
5352 if (strcmp(xml
->name
, "list")) {
5353 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
5355 buddies
= g_slist_append(buddies
, uri
);
5359 parts
= parts
->next
;
5362 if (mime
) purple_mime_document_free(mime
);
5364 payload
->host
= g_strdup(who
);
5365 payload
->buddies
= buddies
;
5366 sipe_schedule_action(action_name
, timeout
,
5367 sipe_subscribe_presence_batched_routed
,
5368 sipe_subscribe_presence_batched_routed_free
,
5370 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
5373 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
5374 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
5376 g_free(action_name
);
5380 * Dispatcher for all incoming subscription information
5381 * whether it comes from NOTIFY, BENOTIFY requests or
5382 * piggy-backed to subscription's OK responce.
5384 * @param request whether initiated from BE/NOTIFY request or OK-response message.
5385 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
5387 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
5389 gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
5390 gchar
*event
= sipmsg_find_header(msg
, "Event");
5391 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
5395 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
5397 tmp
= fix_newlines(msg
->body
));
5399 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
5401 /* implicit subscriptions */
5402 if (content_type
&& purple_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
5403 sipe_process_imdn(sip
, msg
);
5408 const gchar
*expires_header
;
5409 expires_header
= sipmsg_find_header(msg
, "Expires");
5410 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
5411 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
5412 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
; // 2 min ahead of expiration
5415 /* for one off subscriptions (send with Expire: 0) */
5416 if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
5418 sipe_process_provisioning_v2(sip
, msg
);
5420 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning"))
5422 sipe_process_provisioning(sip
, msg
);
5425 if (!subscription_state
|| strstr(subscription_state
, "active"))
5427 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
5429 sipe_process_presence(sip
, msg
);
5431 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
5433 sipe_process_roaming_contacts(sip
, msg
);
5435 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self"))
5437 sipe_process_roaming_self(sip
, msg
);
5439 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
5441 sipe_process_roaming_acl(sip
, msg
);
5443 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
5445 sipe_process_presence_wpending(sip
, msg
);
5447 else if (event
&& !g_ascii_strcasecmp(event
, "conference"))
5449 sipe_process_conference(sip
, msg
);
5453 /* The server sends status 'terminated' */
5454 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
5455 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
5456 gchar
*key
= sipe_get_subscription_key(event
, who
);
5458 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
5461 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
5462 g_hash_table_remove(sip
->subscriptions
, key
);
5463 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
5469 if (timeout
&& event
) {// For LSC 2005 and OCS 2007
5470 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
5471 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
5473 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
5474 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_contacts, NULL, sip, msg);
5475 g_free(action_name);
5477 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
5478 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
5480 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
5481 sipe_schedule_action(action_name, timeout, sipe_subscribe_roaming_acl, NULL, sip, msg);
5482 g_free(action_name);
5485 if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
5486 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
5488 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
5489 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
5490 g_free(action_name
);
5492 else if (!g_ascii_strcasecmp(event
, "presence") &&
5493 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
5495 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
5496 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
5497 if (sip
->batched_support
) {
5498 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
5501 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
5502 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
5504 g_free(action_name
);
5509 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
5511 sipe_process_registration_notify(sip
, msg
);
5514 /* The client responses on received a NOTIFY message */
5515 if (request
&& !benotify
)
5518 gchar
*who
= parse_from(sipmsg_find_header(msg
, "From"));
5519 gchar
*key
= sipe_get_subscription_key(event
, who
);
5522 if (!key
|| (key
&& g_hash_table_lookup(sip
->subscriptions
, key
))) {
5523 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5525 send_sip_response(sip
->gc
, msg
, 481, "Call Leg Does Not Exist", NULL
);
5530 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5535 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
5537 int availability
= 300; // online
5538 int activity
= 400; // Available
5541 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
5543 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
5545 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
5547 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
5549 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
5551 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
5553 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
5554 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
5555 availability
= 0; // offline
5558 activity
= 400; // available
5561 //@TODO: send user data - state; add hostname in upper case
5562 body
= g_markup_printf_escaped(note
? SIPE_SOAP_SET_PRESENCE(SIPE_SOAP_SET_PRESENCE_NOTE_XML
) :
5563 SIPE_SOAP_SET_PRESENCE(SIPE_SOAP_SET_PRESENCE_NOTE_XML_EMPTY
),
5569 send_soap_request(sip
, body
);
5574 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
5576 struct transaction
*trans
)
5578 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
5580 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
5586 gboolean has_device_publication
= FALSE
;
5588 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5590 /* test if version mismatch fault */
5591 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
5592 if (strcmp(fault_code
, "Client.BadCall.WrongDelta")) {
5593 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
5600 /* accumulating information about faulty versions */
5601 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
5602 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
5604 node
= xmlnode_get_next_twin(node
))
5606 const gchar
*index
= xmlnode_get_attrib(node
, "index");
5607 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
5609 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
5610 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
5614 /* here we are parsing own request to figure out what publication
5615 * referensed here only by index went wrong
5617 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
5620 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
5621 index_our
= 1; /* starts with 1 - our first publication */
5623 node
= xmlnode_get_next_twin(node
), index_our
++)
5625 gchar
*idx
= g_strdup_printf("%d", index_our
);
5626 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
5627 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
5630 if (!strcmp("device", categoryName
)) {
5631 has_device_publication
= TRUE
;
5634 if (curVersion
) { /* fault exist on this index */
5635 const gchar
*container
= xmlnode_get_attrib(node
, "container");
5636 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
5637 /* key is <category><instance><container> */
5638 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
5639 struct sipe_publication
*publication
=
5640 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
5642 purple_debug_info("sipe", "key is %s\n", key
);
5645 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
5646 key
, curVersion
, publication
->version
);
5647 /* updating publication's version to the correct one */
5648 publication
->version
= atoi(curVersion
);
5654 g_hash_table_destroy(faults
);
5656 /* rebublishing with right versions */
5657 if (has_device_publication
) {
5658 send_publish_category_initial(sip
);
5660 send_presence_status(sip
);
5667 * Returns 'device' XML part for publication.
5668 * Must be g_free'd after use.
5671 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
5675 gchar
*epid
= get_epid(sip
);
5676 gchar
*uuid
= generateUUIDfromEPID(epid
);
5677 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
5678 /* key is <category><instance><container> */
5679 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
5680 struct sipe_publication
*publication
=
5681 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
5686 uri
= sip_uri_self(sip
);
5687 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
5689 publication
? publication
->version
: 0,
5692 "00:00:00+01:00", /* @TODO make timezone real*/
5693 sipe_get_host_name()
5703 * A service method - use
5704 * - send_publish_get_category_state_machine and
5705 * - send_publish_get_category_state_user instead.
5706 * Must be g_free'd after use.
5709 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
5710 gboolean is_user_state
)
5713 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
5714 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
5715 /* key is <category><instance><container> */
5716 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
5717 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
5718 struct sipe_publication
*publication_2
=
5719 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
5720 struct sipe_publication
*publication_3
=
5721 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
5726 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
5727 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
5728 availability
= 15500;
5729 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
5730 availability
= 12500;
5731 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
5732 availability
= 9500;
5733 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
) ||
5734 !strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
5735 availability
= 6500;
5736 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
5737 availability
= 3500;
5738 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
5741 // Offline or invisible
5742 availability
= 18500;
5745 if (publication_2
&& (publication_2
->availability
== availability
))
5747 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
5748 return NULL
; /* nothing to update */
5751 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
5753 publication_2
? publication_2
->version
: 0,
5756 publication_3
? publication_3
->version
: 0,
5761 * Returns 'machineState' XML part for publication.
5762 * Must be g_free'd after use.
5765 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
5767 return sipe_publish_get_category_state(sip
, FALSE
);
5771 * Returns 'userState' XML part for publication.
5772 * Must be g_free'd after use.
5775 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
5777 return sipe_publish_get_category_state(sip
, TRUE
);
5781 * Returns 'note' XML part for publication.
5782 * Must be g_free'd after use.
5785 sipe_publish_get_category_note(struct sipe_account_data
*sip
, const char *note
)
5787 /* key is <category><instance><container> */
5788 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", 0, 200);
5789 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", 0, 300);
5790 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", 0, 400);
5791 struct sipe_publication
*publication_note_200
=
5792 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
5793 struct sipe_publication
*publication_note_300
=
5794 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
5795 struct sipe_publication
*publication_note_400
=
5796 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
5798 const char *n1
= note
;
5799 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
5801 g_free(key_note_200
);
5802 g_free(key_note_300
);
5803 g_free(key_note_400
);
5805 if (((!n1
|| !strlen(n1
)) && (!n2
|| !strlen(n2
))) /* both empty */
5806 || (n1
&& n2
&& !strcmp(n1
, n2
))) /* or not empty and equal */
5808 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
5809 return NULL
; /* nothing to update */
5812 return g_markup_printf_escaped(SIPE_PUB_XML_NOTE
,
5813 publication_note_200
? publication_note_200
->version
: 0,
5815 publication_note_300
? publication_note_300
->version
: 0,
5817 publication_note_400
? publication_note_400
->version
: 0,
5821 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
5828 uri
= sip_uri_self(sip
);
5829 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
5833 tmp
= get_contact(sip
);
5834 hdr
= g_strdup_printf("Contact: %s\r\n"
5835 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
5837 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
5846 send_publish_category_initial(struct sipe_account_data
*sip
)
5848 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
5849 gchar
*pub_machine
= sipe_publish_get_category_state_machine(sip
);
5850 gchar
*publications
= g_strdup_printf("%s%s",
5852 pub_machine
? pub_machine
: "");
5854 g_free(pub_machine
);
5856 send_presence_publish(sip
, publications
);
5857 g_free(publications
);
5861 send_presence_category_publish(struct sipe_account_data
*sip
,
5865 * Whether user manually changed status or
5866 * it was changed automatically due to user
5867 * became inactive/active again
5869 gboolean is_machine
= (sip
->was_idle
&& !sip
->is_idle
) || (!sip
->was_idle
&& sip
->is_idle
);
5870 gchar
*pub_state
= is_machine
? sipe_publish_get_category_state_machine(sip
) :
5871 sipe_publish_get_category_state_user(sip
);
5872 gchar
*pub_note
= sipe_publish_get_category_note(sip
, note
);
5873 gchar
*publications
;
5875 if (!pub_state
&& !pub_note
) {
5876 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
5880 publications
= g_strdup_printf("%s%s",
5881 pub_state
? pub_state
: "",
5882 pub_note
? pub_note
: "");
5884 purple_debug_info("sipe", "send_presence_category_publish: sip->status: %s sip->is_idle:%s sip->was_idle:%s\n",
5885 sip
->status
, sip
->is_idle
? "Y" : "N", sip
->was_idle
? "Y" : "N");
5890 send_presence_publish(sip
, publications
);
5891 g_free(publications
);
5894 static void send_presence_status(struct sipe_account_data
*sip
)
5896 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
5898 if (!status
) return;
5900 note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
5901 purple_debug_info("sipe", "send_presence_status: note: '%s'\n", note
? note
: "");
5904 send_presence_category_publish(sip
, note
);
5906 send_presence_soap(sip
, note
);
5910 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
5912 gboolean found
= FALSE
;
5913 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
5914 if (msg
->response
== 0) { /* request */
5915 if (!strcmp(msg
->method
, "MESSAGE")) {
5916 process_incoming_message(sip
, msg
);
5918 } else if (!strcmp(msg
->method
, "NOTIFY")) {
5919 purple_debug_info("sipe","send->process_incoming_notify\n");
5920 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
5922 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
5923 purple_debug_info("sipe","send->process_incoming_benotify\n");
5924 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
5926 } else if (!strcmp(msg
->method
, "INVITE")) {
5927 process_incoming_invite(sip
, msg
);
5929 } else if (!strcmp(msg
->method
, "REFER")) {
5930 process_incoming_refer(sip
, msg
);
5932 } else if (!strcmp(msg
->method
, "OPTIONS")) {
5933 process_incoming_options(sip
, msg
);
5935 } else if (!strcmp(msg
->method
, "INFO")) {
5936 process_incoming_info(sip
, msg
);
5938 } else if (!strcmp(msg
->method
, "ACK")) {
5939 // ACK's don't need any response
5941 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
5942 // LCS 2005 sends us these - just respond 200 OK
5944 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5945 } else if (!strcmp(msg
->method
, "BYE")) {
5946 process_incoming_bye(sip
, msg
);
5949 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
5951 } else { /* response */
5952 struct transaction
*trans
= transactions_find(sip
, msg
);
5954 if (msg
->response
== 407) {
5955 gchar
*resend
, *auth
, *ptmp
;
5957 if (sip
->proxy
.retries
> 30) return;
5958 sip
->proxy
.retries
++;
5959 /* do proxy authentication */
5961 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
5963 fill_auth(ptmp
, &sip
->proxy
);
5964 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
5965 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
5966 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
5968 resend
= sipmsg_to_string(trans
->msg
);
5969 /* resend request */
5970 sendout_pkt(sip
->gc
, resend
);
5973 if (msg
->response
< 200) {
5974 /* ignore provisional response */
5975 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
5977 sip
->proxy
.retries
= 0;
5978 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
5979 if (msg
->response
== 401)
5981 sip
->registrar
.retries
++;
5985 sip
->registrar
.retries
= 0;
5987 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
5989 if (msg
->response
== 401) {
5990 gchar
*resend
, *auth
, *ptmp
;
5992 if (sip
->registrar
.retries
> 4) return;
5993 sip
->registrar
.retries
++;
5996 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5998 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
6001 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
6005 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
);
6007 fill_auth(ptmp
, &sip
->registrar
);
6008 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
6009 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
6010 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
6012 //sipmsg_remove_header_now(trans->msg, "Authorization");
6013 //sipmsg_add_header(trans->msg, "Authorization", auth);
6015 resend
= sipmsg_to_string(trans
->msg
);
6016 /* resend request */
6017 sendout_pkt(sip
->gc
, resend
);
6022 if (trans
->callback
) {
6023 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
6024 /* call the callback to process response*/
6025 (trans
->callback
)(sip
, msg
, trans
);
6028 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
6029 transactions_remove(sip
, trans
);
6035 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
6039 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
6043 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
6052 /* according to the RFC remove CRLF at the beginning */
6053 while (*cur
== '\r' || *cur
== '\n') {
6056 if (cur
!= conn
->inbuf
) {
6057 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
6058 conn
->inbufused
= strlen(conn
->inbuf
);
6061 /* Received a full Header? */
6062 sip
->processing_input
= TRUE
;
6063 while (sip
->processing_input
&&
6064 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
6065 time_t currtime
= time(NULL
);
6068 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
6070 msg
= sipmsg_parse_header(conn
->inbuf
);
6073 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
6074 if (msg
&& restlen
>= msg
->bodylen
) {
6075 dummy
= g_malloc(msg
->bodylen
+ 1);
6076 memcpy(dummy
, cur
, msg
->bodylen
);
6077 dummy
[msg
->bodylen
] = '\0';
6079 cur
+= msg
->bodylen
;
6080 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
6081 conn
->inbufused
= strlen(conn
->inbuf
);
6084 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
6091 purple_debug_info("sipe", "body:\n%s", msg->body);
6094 // Verify the signature before processing it
6095 if (sip
->registrar
.gssapi_context
) {
6096 struct sipmsg_breakdown msgbd
;
6097 gchar
*signature_input_str
;
6100 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
6101 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
6103 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
6105 if (rspauth
!= NULL
) {
6106 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
6107 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
6108 process_input_message(sip
, msg
);
6110 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
6111 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
6112 sip
->gc
->wants_to_die
= TRUE
;
6114 } else if (msg
->response
== 401) {
6115 purple_connection_error(sip
->gc
, _("Wrong password"));
6116 sip
->gc
->wants_to_die
= TRUE
;
6118 g_free(signature_input_str
);
6121 sipmsg_breakdown_free(&msgbd
);
6123 process_input_message(sip
, msg
);
6130 static void sipe_udp_process(gpointer data
, gint source
,
6131 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
6133 PurpleConnection
*gc
= data
;
6134 struct sipe_account_data
*sip
= gc
->proto_data
;
6139 static char buffer
[65536];
6140 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
6142 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
6143 msg
= sipmsg_parse_msg(buffer
);
6144 if (msg
) process_input_message(sip
, msg
);
6148 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
6150 struct sipe_account_data
*sip
= gc
->proto_data
;
6151 PurpleSslConnection
*gsc
= sip
->gsc
;
6153 purple_debug_error("sipe", "%s",debug
);
6154 purple_connection_error(gc
, msg
);
6156 /* Invalidate this connection. Next send will open a new one */
6158 connection_remove(sip
, gsc
->fd
);
6159 purple_ssl_close(gsc
);
6165 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
6166 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
6168 PurpleConnection
*gc
= data
;
6169 struct sipe_account_data
*sip
;
6170 struct sip_connection
*conn
;
6172 gboolean firstread
= TRUE
;
6174 /* NOTE: This check *IS* necessary */
6175 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
6176 purple_ssl_close(gsc
);
6180 sip
= gc
->proto_data
;
6181 conn
= connection_find(sip
, gsc
->fd
);
6183 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
6184 gc
->wants_to_die
= TRUE
;
6185 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
6189 /* Read all available data from the SSL connection */
6191 /* Increase input buffer size as needed */
6192 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
6193 conn
->inbuflen
+= SIMPLE_BUF_INC
;
6194 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
6195 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
6198 /* Try to read as much as there is space left in the buffer */
6199 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
6200 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
6202 if (len
< 0 && errno
== EAGAIN
) {
6203 /* Try again later */
6205 } else if (len
< 0) {
6206 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
6208 } else if (firstread
&& (len
== 0)) {
6209 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
6213 conn
->inbufused
+= len
;
6216 /* Equivalence indicates that there is possibly more data to read */
6217 } while (len
== readlen
);
6219 conn
->inbuf
[conn
->inbufused
] = '\0';
6220 process_input(sip
, conn
);
6224 static void sipe_input_cb(gpointer data
, gint source
,
6225 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
6227 PurpleConnection
*gc
= data
;
6228 struct sipe_account_data
*sip
= gc
->proto_data
;
6230 struct sip_connection
*conn
= connection_find(sip
, source
);
6232 purple_debug_error("sipe", "Connection not found!\n");
6236 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
6237 conn
->inbuflen
+= SIMPLE_BUF_INC
;
6238 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
6241 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
6243 if (len
< 0 && errno
== EAGAIN
)
6245 else if (len
<= 0) {
6246 purple_debug_info("sipe", "sipe_input_cb: read error\n");
6247 connection_remove(sip
, source
);
6248 if (sip
->fd
== source
) sip
->fd
= -1;
6252 conn
->inbufused
+= len
;
6253 conn
->inbuf
[conn
->inbufused
] = '\0';
6255 process_input(sip
, conn
);
6258 /* Callback for new connections on incoming TCP port */
6259 static void sipe_newconn_cb(gpointer data
, gint source
,
6260 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
6262 PurpleConnection
*gc
= data
;
6263 struct sipe_account_data
*sip
= gc
->proto_data
;
6264 struct sip_connection
*conn
;
6266 int newfd
= accept(source
, NULL
, NULL
);
6268 conn
= connection_create(sip
, newfd
);
6270 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
6273 static void login_cb(gpointer data
, gint source
,
6274 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
6276 PurpleConnection
*gc
= data
;
6277 struct sipe_account_data
*sip
;
6278 struct sip_connection
*conn
;
6280 if (!PURPLE_CONNECTION_IS_VALID(gc
))
6288 purple_connection_error(gc
, _("Could not connect"));
6292 sip
= gc
->proto_data
;
6294 sip
->last_keepalive
= time(NULL
);
6296 conn
= connection_create(sip
, source
);
6300 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
6303 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
6304 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
6306 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
6307 if (sip
== NULL
) return;
6312 static guint
sipe_ht_hash_nick(const char *nick
)
6314 char *lc
= g_utf8_strdown(nick
, -1);
6315 guint bucket
= g_str_hash(lc
);
6321 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
6323 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
6326 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
6328 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
6330 sip
->listen_data
= NULL
;
6332 if (listenfd
== -1) {
6333 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
6339 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
6340 sip
->listenfd
= sip
->fd
;
6342 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
6344 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
6348 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
6349 SIPE_UNUSED_PARAMETER
const char *error_message
)
6351 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
6353 sip
->query_data
= NULL
;
6355 if (!hosts
|| !hosts
->data
) {
6356 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
6360 hosts
= g_slist_remove(hosts
, hosts
->data
);
6361 g_free(sip
->serveraddr
);
6362 sip
->serveraddr
= hosts
->data
;
6363 hosts
= g_slist_remove(hosts
, hosts
->data
);
6365 hosts
= g_slist_remove(hosts
, hosts
->data
);
6366 g_free(hosts
->data
);
6367 hosts
= g_slist_remove(hosts
, hosts
->data
);
6370 /* create socket for incoming connections */
6371 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
6372 sipe_udp_host_resolved_listen_cb
, sip
);
6373 if (sip
->listen_data
== NULL
) {
6374 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
6379 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
6380 PurpleSslErrorType error
,
6383 PurpleConnection
*gc
= data
;
6384 struct sipe_account_data
*sip
;
6386 /* If the connection is already disconnected, we don't need to do anything else */
6387 if (!PURPLE_CONNECTION_IS_VALID(gc
))
6390 sip
= gc
->proto_data
;
6395 case PURPLE_SSL_CONNECT_FAILED
:
6396 purple_connection_error(gc
, _("Connection failed"));
6398 case PURPLE_SSL_HANDSHAKE_FAILED
:
6399 purple_connection_error(gc
, _("SSL handshake failed"));
6401 case PURPLE_SSL_CERTIFICATE_INVALID
:
6402 purple_connection_error(gc
, _("SSL certificate invalid"));
6408 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
6410 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
6411 PurpleProxyConnectData
*connect_data
;
6413 sip
->listen_data
= NULL
;
6415 sip
->listenfd
= listenfd
;
6416 if (sip
->listenfd
== -1) {
6417 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
6421 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
6422 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
6423 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
6424 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
6425 sipe_newconn_cb
, sip
->gc
);
6426 purple_debug_info("sipe", "connecting to %s port %d\n",
6427 sip
->realhostname
, sip
->realport
);
6428 /* open tcp connection to the server */
6429 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
6430 sip
->realport
, login_cb
, sip
->gc
);
6432 if (connect_data
== NULL
) {
6433 purple_connection_error(sip
->gc
, _("Could not create socket"));
6437 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
6439 PurpleAccount
*account
= sip
->account
;
6440 PurpleConnection
*gc
= sip
->gc
;
6443 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
6446 sip
->realhostname
= hostname
;
6447 sip
->realport
= port
;
6449 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
6452 /* TODO: is there a good default grow size? */
6453 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
6454 sip
->txbuf
= purple_circ_buffer_new(0);
6456 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
6458 if (!purple_ssl_is_supported()) {
6459 gc
->wants_to_die
= TRUE
;
6460 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
6464 purple_debug_info("sipe", "using SSL\n");
6466 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
6467 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
6468 if (sip
->gsc
== NULL
) {
6469 purple_connection_error(gc
, _("Could not create SSL context"));
6472 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
6474 purple_debug_info("sipe", "using UDP\n");
6476 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
6477 if (sip
->query_data
== NULL
) {
6478 purple_connection_error(gc
, _("Could not resolve hostname"));
6482 purple_debug_info("sipe", "using TCP\n");
6483 /* create socket for incoming connections */
6484 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
6485 sipe_tcp_connect_listen_cb
, sip
);
6486 if (sip
->listen_data
== NULL
) {
6487 purple_connection_error(gc
, _("Could not create listen socket"));
6493 /* Service list for autodection */
6494 static const struct sipe_service_data service_autodetect
[] = {
6495 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
6496 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
6497 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
6498 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
6502 /* Service list for SSL/TLS */
6503 static const struct sipe_service_data service_tls
[] = {
6504 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
6505 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
6509 /* Service list for TCP */
6510 static const struct sipe_service_data service_tcp
[] = {
6511 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
6512 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
6516 /* Service list for UDP */
6517 static const struct sipe_service_data service_udp
[] = {
6518 { "sip", "udp", SIPE_TRANSPORT_UDP
},
6522 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
6523 static void resolve_next_service(struct sipe_account_data
*sip
,
6524 const struct sipe_service_data
*start
)
6527 sip
->service_data
= start
;
6529 sip
->service_data
++;
6530 if (sip
->service_data
->service
== NULL
) {
6532 /* Try connecting to the SIP hostname directly */
6533 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
6534 if (sip
->auto_transport
) {
6535 // If SSL is supported, default to using it; OCS servers aren't configured
6536 // by default to accept TCP
6537 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
6538 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
6539 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
6542 hostname
= g_strdup(sip
->sipdomain
);
6543 create_connection(sip
, hostname
, 0);
6548 /* Try to resolve next service */
6549 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
6550 sip
->service_data
->transport
,
6555 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
6557 struct sipe_account_data
*sip
= data
;
6559 sip
->srv_query_data
= NULL
;
6561 /* find the host to connect to */
6563 gchar
*hostname
= g_strdup(resp
->hostname
);
6564 int port
= resp
->port
;
6565 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
6569 sip
->transport
= sip
->service_data
->type
;
6571 create_connection(sip
, hostname
, port
);
6573 resolve_next_service(sip
, NULL
);
6577 static void sipe_login(PurpleAccount
*account
)
6579 PurpleConnection
*gc
;
6580 struct sipe_account_data
*sip
;
6581 gchar
**signinname_login
, **userserver
;
6582 const char *transport
;
6584 const char *username
= purple_account_get_username(account
);
6585 gc
= purple_account_get_connection(account
);
6587 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
6589 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
6590 gc
->wants_to_die
= TRUE
;
6591 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
6595 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
6596 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
6597 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
6599 sip
->account
= account
;
6600 sip
->reregister_set
= FALSE
;
6601 sip
->reauthenticate_set
= FALSE
;
6602 sip
->subscribed
= FALSE
;
6603 sip
->subscribed_buddies
= FALSE
;
6604 sip
->initial_state_published
= FALSE
;
6606 /* username format: <username>,[<optional login>] */
6607 signinname_login
= g_strsplit(username
, ",", 2);
6608 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
6610 /* ensure that username format is name@domain */
6611 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
6612 g_strfreev(signinname_login
);
6613 gc
->wants_to_die
= TRUE
;
6614 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
6617 sip
->username
= g_strdup(signinname_login
[0]);
6619 /* login name specified? */
6620 if (signinname_login
[1] && strlen(signinname_login
[1])) {
6621 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
6622 gboolean has_domain
= domain_user
[1] != NULL
;
6623 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
6624 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
6625 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
6626 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
6627 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
6628 g_strfreev(domain_user
);
6631 userserver
= g_strsplit(signinname_login
[0], "@", 2);
6632 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
6633 purple_connection_set_display_name(gc
, userserver
[0]);
6634 sip
->sipdomain
= g_strdup(userserver
[1]);
6635 g_strfreev(userserver
);
6636 g_strfreev(signinname_login
);
6638 if (strchr(sip
->username
, ' ') != NULL
) {
6639 gc
->wants_to_die
= TRUE
;
6640 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
6644 sip
->password
= g_strdup(purple_connection_get_password(gc
));
6646 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
6647 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
6648 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
6649 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
6650 g_free
, (GDestroyNotify
)sipe_subscription_free
);
6652 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
6654 sip
->status
= g_strdup(purple_status_get_id(purple_account_get_active_status(account
)));
6656 sip
->auto_transport
= FALSE
;
6657 transport
= purple_account_get_string(account
, "transport", "auto");
6658 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
6659 if (userserver
[0]) {
6660 /* Use user specified server[:port] */
6664 port
= atoi(userserver
[1]);
6666 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
6667 userserver
[0], port
);
6669 if (strcmp(transport
, "auto") == 0) {
6670 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
6671 } else if (strcmp(transport
, "tls") == 0) {
6672 sip
->transport
= SIPE_TRANSPORT_TLS
;
6673 } else if (strcmp(transport
, "tcp") == 0) {
6674 sip
->transport
= SIPE_TRANSPORT_TCP
;
6676 sip
->transport
= SIPE_TRANSPORT_UDP
;
6679 create_connection(sip
, g_strdup(userserver
[0]), port
);
6681 /* Server auto-discovery */
6682 if (strcmp(transport
, "auto") == 0) {
6683 sip
->auto_transport
= TRUE
;
6684 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
6685 } else if (strcmp(transport
, "tls") == 0) {
6686 resolve_next_service(sip
, service_tls
);
6687 } else if (strcmp(transport
, "tcp") == 0) {
6688 resolve_next_service(sip
, service_tcp
);
6690 resolve_next_service(sip
, service_udp
);
6693 g_strfreev(userserver
);
6696 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
6698 connection_free_all(sip
);
6703 if (sip
->query_data
!= NULL
)
6704 purple_dnsquery_destroy(sip
->query_data
);
6705 sip
->query_data
= NULL
;
6707 if (sip
->srv_query_data
!= NULL
)
6708 purple_srv_cancel(sip
->srv_query_data
);
6709 sip
->srv_query_data
= NULL
;
6711 if (sip
->listen_data
!= NULL
)
6712 purple_network_listen_cancel(sip
->listen_data
);
6713 sip
->listen_data
= NULL
;
6715 if (sip
->gsc
!= NULL
)
6716 purple_ssl_close(sip
->gsc
);
6719 sipe_auth_free(&sip
->registrar
);
6720 sipe_auth_free(&sip
->proxy
);
6723 purple_circ_buffer_destroy(sip
->txbuf
);
6726 g_free(sip
->realhostname
);
6727 sip
->realhostname
= NULL
;
6729 g_free(sip
->server_version
);
6730 sip
->server_version
= NULL
;
6733 purple_input_remove(sip
->listenpa
);
6735 if (sip
->tx_handler
)
6736 purple_input_remove(sip
->tx_handler
);
6737 sip
->tx_handler
= 0;
6738 if (sip
->resendtimeout
)
6739 purple_timeout_remove(sip
->resendtimeout
);
6740 sip
->resendtimeout
= 0;
6741 if (sip
->timeouts
) {
6742 GSList
*entry
= sip
->timeouts
;
6744 struct scheduled_action
*sched_action
= entry
->data
;
6745 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
6746 purple_timeout_remove(sched_action
->timeout_handler
);
6747 if (sched_action
->destroy
) {
6748 (*sched_action
->destroy
)(sched_action
->payload
);
6750 g_free(sched_action
->name
);
6751 g_free(sched_action
);
6752 entry
= entry
->next
;
6755 g_slist_free(sip
->timeouts
);
6757 if (sip
->allow_events
) {
6758 GSList
*entry
= sip
->allow_events
;
6760 g_free(entry
->data
);
6761 entry
= entry
->next
;
6764 g_slist_free(sip
->allow_events
);
6766 if (sip
->containers
) {
6767 GSList
*entry
= sip
->containers
;
6769 free_container((struct sipe_container
*)entry
->data
);
6770 entry
= entry
->next
;
6773 g_slist_free(sip
->containers
);
6776 g_free(sip
->contact
);
6777 sip
->contact
= NULL
;
6779 g_free(sip
->regcallid
);
6780 sip
->regcallid
= NULL
;
6782 if (sip
->serveraddr
)
6783 g_free(sip
->serveraddr
);
6784 sip
->serveraddr
= NULL
;
6786 if (sip
->focus_factory_uri
)
6787 g_free(sip
->focus_factory_uri
);
6788 sip
->focus_factory_uri
= NULL
;
6791 sip
->processing_input
= FALSE
;
6795 * A callback for g_hash_table_foreach_remove
6797 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
6798 SIPE_UNUSED_PARAMETER gpointer user_data
)
6800 sipe_free_buddy((struct sipe_buddy
*) buddy
);
6802 /* We must return TRUE as the key/value have already been deleted */
6806 static void sipe_close(PurpleConnection
*gc
)
6808 struct sipe_account_data
*sip
= gc
->proto_data
;
6811 /* leave all conversations */
6812 sipe_session_close_all(sip
);
6813 sipe_session_remove_all(sip
);
6816 sip_csta_close(sip
);
6819 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
6820 /* unsubscribe all */
6821 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
6824 do_register_exp(sip
, 0);
6827 sipe_connection_cleanup(sip
);
6828 g_free(sip
->sipdomain
);
6829 g_free(sip
->username
);
6830 g_free(sip
->password
);
6831 g_free(sip
->authdomain
);
6832 g_free(sip
->authuser
);
6833 g_free(sip
->status
);
6835 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
6836 g_hash_table_destroy(sip
->buddies
);
6837 g_hash_table_destroy(sip
->our_publications
);
6838 g_hash_table_destroy(sip
->subscriptions
);
6841 GSList
*entry
= sip
->groups
;
6843 struct sipe_group
*group
= entry
->data
;
6844 g_free(group
->name
);
6846 entry
= entry
->next
;
6849 g_slist_free(sip
->groups
);
6851 if (sip
->our_publication_keys
) {
6852 GSList
*entry
= sip
->our_publication_keys
;
6854 g_free(entry
->data
);
6855 entry
= entry
->next
;
6858 g_slist_free(sip
->our_publication_keys
);
6860 while (sip
->transactions
)
6861 transactions_remove(sip
, sip
->transactions
->data
);
6863 g_free(gc
->proto_data
);
6864 gc
->proto_data
= NULL
;
6867 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
6868 SIPE_UNUSED_PARAMETER
void *user_data
)
6870 PurpleAccount
*acct
= purple_connection_get_account(gc
);
6871 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
6872 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
6874 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
6875 purple_conversation_present(conv
);
6879 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
6880 SIPE_UNUSED_PARAMETER
void *user_data
)
6883 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
6884 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
6887 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
6888 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
6890 PurpleNotifySearchResults
*results
;
6891 PurpleNotifySearchColumn
*column
;
6892 xmlnode
*searchResults
;
6894 int match_count
= 0;
6895 gboolean more
= FALSE
;
6898 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
6900 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
6901 if (!searchResults
) {
6902 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
6906 results
= purple_notify_searchresults_new();
6908 if (results
== NULL
) {
6909 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
6910 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
6912 xmlnode_free(searchResults
);
6916 column
= purple_notify_searchresults_column_new(_("User name"));
6917 purple_notify_searchresults_column_add(results
, column
);
6919 column
= purple_notify_searchresults_column_new(_("Name"));
6920 purple_notify_searchresults_column_add(results
, column
);
6922 column
= purple_notify_searchresults_column_new(_("Company"));
6923 purple_notify_searchresults_column_add(results
, column
);
6925 column
= purple_notify_searchresults_column_new(_("Country"));
6926 purple_notify_searchresults_column_add(results
, column
);
6928 column
= purple_notify_searchresults_column_new(_("Email"));
6929 purple_notify_searchresults_column_add(results
, column
);
6931 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
6934 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
6935 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
6936 g_strfreev(uri_parts
);
6938 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
6939 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
6940 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
6941 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
6943 purple_notify_searchresults_row_add(results
, row
);
6947 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
6948 char *data
= xmlnode_get_data_unescaped(mrow
);
6949 more
= (g_strcasecmp(data
, "true") == 0);
6953 secondary
= g_strdup_printf(
6954 dngettext(GETTEXT_PACKAGE
,
6955 "Found %d contact%s:",
6956 "Found %d contacts%s:", match_count
),
6957 match_count
, more
? _(" (more matched your query)") : "");
6959 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
6960 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
6961 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
6964 xmlnode_free(searchResults
);
6968 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
6970 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
6971 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
6975 PurpleRequestField
*field
= entries
->data
;
6976 const char *id
= purple_request_field_get_id(field
);
6977 const char *value
= purple_request_field_string_get_value(field
);
6979 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
6981 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
6982 } while ((entries
= g_list_next(entries
)) != NULL
);
6986 struct sipe_account_data
*sip
= gc
->proto_data
;
6987 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
6988 gchar
*query
= g_strjoinv(NULL
, attrs
);
6989 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
6990 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
6991 send_soap_request_with_cb(sip
, domain_uri
, body
,
6992 (TransCallback
) process_search_contact_response
, NULL
);
7001 static void sipe_show_find_contact(PurplePluginAction
*action
)
7003 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
7004 PurpleRequestFields
*fields
;
7005 PurpleRequestFieldGroup
*group
;
7006 PurpleRequestField
*field
;
7008 fields
= purple_request_fields_new();
7009 group
= purple_request_field_group_new(NULL
);
7010 purple_request_fields_add_group(fields
, group
);
7012 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
7013 purple_request_field_group_add_field(group
, field
);
7014 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
7015 purple_request_field_group_add_field(group
, field
);
7016 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
7017 purple_request_field_group_add_field(group
, field
);
7018 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
7019 purple_request_field_group_add_field(group
, field
);
7021 purple_request_fields(gc
,
7023 _("Search for a contact"),
7024 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
7026 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
7028 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
7031 static void sipe_show_about_plugin(PurplePluginAction
*action
)
7033 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
7035 "<b><font size=\"+1\">Sipe " SIPE_VERSION
"</font></b><br/>"
7037 "A third-party plugin implementing extended version of SIP/SIMPLE used by various products:<br/>"
7038 "<li> - MS Office Communications Server 2007 (R2)</li><br/>"
7039 "<li> - MS Live Communications Server 2005/2003</li><br/>"
7040 "<li> - Reuters Messaging</li><br/>"
7042 "Home: <a href=\"http://sipe.sourceforge.net\">http://sipe.sourceforge.net</a><br/>"
7043 "Support: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">Help Forum</a><br/>"
7044 "License: GPLv2<br/>"
7046 "We support users in the following organizations to mention a few:<br/>"
7048 " - Reuters Messaging network<br/>"
7049 " - Deutsche Bank<br/>"
7050 " - Merrill Lynch<br/>"
7053 " - Alcatel-Lucent<br/>"
7057 "<b>Authors:</b><br/>"
7058 " - Anibal Avelar<br/>"
7059 " - Gabriel Burt<br/>"
7060 " - Stefan Becker<br/>"
7063 purple_notify_formatted(gc
, NULL
, " ", NULL
, txt
, NULL
, NULL
);
7066 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
7067 SIPE_UNUSED_PARAMETER gpointer context
)
7070 PurplePluginAction
*act
;
7072 act
= purple_plugin_action_new(_("About SIPE plugin"), sipe_show_about_plugin
);
7073 menu
= g_list_prepend(menu
, act
);
7075 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
7076 menu
= g_list_prepend(menu
, act
);
7078 menu
= g_list_reverse(menu
);
7083 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
7087 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
7093 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
7099 static char *sipe_status_text(PurpleBuddy
*buddy
)
7101 struct sipe_account_data
*sip
;
7102 struct sipe_buddy
*sbuddy
;
7105 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
7106 if (sip
) //happens on pidgin exit
7108 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
7110 if (!is_empty(sbuddy
->activity
) && !is_empty(sbuddy
->annotation
))
7112 text
= g_strdup_printf("%s. %s", sbuddy
->activity
, sbuddy
->annotation
);
7114 else if (!is_empty(sbuddy
->activity
))
7116 text
= g_strdup(sbuddy
->activity
);
7120 text
= g_strdup(sbuddy
->annotation
);
7128 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
7130 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
7131 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
7132 struct sipe_account_data
*sip
;
7133 struct sipe_buddy
*sbuddy
;
7134 char *annotation
= NULL
;
7135 char *activity
= NULL
;
7136 char *meeting_subject
= NULL
;
7137 char *meeting_location
= NULL
;
7139 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
7140 if (sip
) //happens on pidgin exit
7142 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
7145 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
7146 activity
= sbuddy
->activity
;
7147 meeting_subject
= sbuddy
->meeting_subject
;
7148 meeting_location
= sbuddy
->meeting_location
;
7153 if (purple_presence_is_online(presence
))
7156 const char *status_str
= activity
&& status
&& strcmp(purple_status_get_id(status
), SIPE_STATUS_ID_ONPHONE
) ?
7157 (tmp
= g_strdup_printf("%s (%s)", purple_status_get_name(status
), activity
)) :
7158 purple_status_get_name(status
);
7160 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
7163 if (!is_empty(meeting_subject
))
7165 purple_notify_user_info_add_pair(user_info
, _("Meeting About"), meeting_subject
);
7167 if (!is_empty(meeting_location
))
7169 purple_notify_user_info_add_pair(user_info
, _("Meeting In"), meeting_location
);
7174 /* Tooltip does not know how to handle markup like <br> */
7175 gchar
*s
= annotation
;
7176 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, annotation
);
7177 while ((s
= strchr(s
, '<')) != NULL
) {
7178 if (!g_ascii_strncasecmp(s
, "<br>", 4)) {
7180 strcpy(s
+ 1, s
+ 4);
7184 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, annotation
);
7186 purple_notify_user_info_add_pair(user_info
, _("Note"), annotation
);
7192 #if PURPLE_VERSION_CHECK(2,5,0)
7194 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
7197 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
7198 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
7203 static PurpleBuddy
*
7204 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
7207 const gchar
*server_alias
, *email
;
7208 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
7210 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
7212 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
7214 server_alias
= purple_buddy_get_server_alias(buddy
);
7216 purple_blist_server_alias_buddy(clone
, server_alias
);
7219 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
7221 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
7224 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
7226 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
7231 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
7233 PurpleBuddy
*buddy
, *b
;
7234 PurpleConnection
*gc
;
7235 PurpleGroup
* group
= purple_find_group(group_name
);
7237 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
7239 buddy
= (PurpleBuddy
*)node
;
7241 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
7242 gc
= purple_account_get_connection(buddy
->account
);
7244 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
7246 b
= purple_blist_add_buddy_clone(group
, buddy
);
7249 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
7253 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
7255 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
7257 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
7259 /* 2007+ conference */
7262 sipe_conf_add(sip
, buddy
->name
);
7264 else /* 2005- multiparty chat */
7266 gchar
*self
= sip_uri_self(sip
);
7267 struct sip_session
*session
;
7269 session
= sipe_session_add_chat(sip
);
7270 session
->chat_title
= sipe_chat_get_name(session
->callid
);
7271 session
->roster_manager
= g_strdup(self
);
7273 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
7274 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
7275 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
7276 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, FALSE
);
7283 sipe_is_election_finished(struct sip_session
*session
)
7285 gboolean res
= TRUE
;
7287 SIPE_DIALOG_FOREACH
{
7288 if (dialog
->election_vote
== 0) {
7292 } SIPE_DIALOG_FOREACH_END
;
7295 session
->is_voting_in_progress
= FALSE
;
7301 sipe_election_start(struct sipe_account_data
*sip
,
7302 struct sip_session
*session
)
7304 int election_timeout
;
7306 if (session
->is_voting_in_progress
) {
7307 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
7310 session
->is_voting_in_progress
= TRUE
;
7312 session
->bid
= rand();
7314 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
7316 SIPE_DIALOG_FOREACH
{
7317 /* reset election_vote for each chat participant */
7318 dialog
->election_vote
= 0;
7320 /* send RequestRM to each chat participant*/
7321 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
7322 } SIPE_DIALOG_FOREACH_END
;
7324 election_timeout
= 15; /* sec */
7325 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
7329 * @param who a URI to whom to invite to chat
7332 sipe_invite_to_chat(struct sipe_account_data
*sip
,
7333 struct sip_session
*session
,
7337 if (session
->focus_uri
)
7339 sipe_invite_conf(sip
, session
, who
);
7341 else /* a multi-party chat */
7343 gchar
*self
= sip_uri_self(sip
);
7344 if (session
->roster_manager
) {
7345 if (!strcmp(session
->roster_manager
, self
)) {
7346 sipe_invite(sip
, session
, who
, NULL
, NULL
, FALSE
);
7348 sipe_refer(sip
, session
, who
);
7351 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
7353 session
->pending_invite_queue
= slist_insert_unique_sorted(
7354 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
7356 sipe_election_start(sip
, session
);
7363 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
7364 struct sip_session
*session
)
7367 GSList
*entry
= session
->pending_invite_queue
;
7370 invitee
= entry
->data
;
7371 sipe_invite_to_chat(sip
, session
, invitee
);
7372 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
7378 sipe_election_result(struct sipe_account_data
*sip
,
7381 struct sip_session
*session
= (struct sip_session
*)sess
;
7383 gboolean has_won
= TRUE
;
7385 if (session
->roster_manager
) {
7386 purple_debug_info("sipe",
7387 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
7391 session
->is_voting_in_progress
= FALSE
;
7393 SIPE_DIALOG_FOREACH
{
7394 if (dialog
->election_vote
< 0) {
7396 rival
= dialog
->with
;
7399 } SIPE_DIALOG_FOREACH_END
;
7402 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
7404 session
->roster_manager
= sip_uri_self(sip
);
7406 SIPE_DIALOG_FOREACH
{
7407 /* send SetRM to each chat participant*/
7408 sipe_send_election_set_rm(sip
, dialog
);
7409 } SIPE_DIALOG_FOREACH_END
;
7411 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
7415 sipe_process_pending_invite_queue(sip
, session
);
7419 * For 2007+ conference only.
7422 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
7424 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
7425 struct sip_session
*session
;
7427 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
7428 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
7430 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
7432 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
7436 * For 2007+ conference only.
7439 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
7441 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
7442 struct sip_session
*session
;
7444 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
7445 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
7447 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
7449 sipe_conf_delete_user(sip
, session
, buddy
->name
);
7453 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
7455 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
7456 struct sip_session
*session
;
7458 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
7459 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
7461 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
7463 sipe_invite_to_chat(sip
, session
, buddy
->name
);
7467 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
7469 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
7471 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
7473 char *tel_uri
= sip_to_tel_uri(phone
);
7475 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
7476 sip_csta_make_call(sip
, tel_uri
);
7483 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
7486 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
7488 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
7491 char *mailto
= g_strdup_printf("mailto:%s", email
);
7492 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
7496 char *const parmList
[] = {mailto
, NULL
};
7497 if ((pid
= fork()) == -1)
7499 purple_debug_info("sipe", "fork() error\n");
7503 execvp("xdg-email", parmList
);
7504 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
7512 //@TODO resolve env variable %WINDIR% first
7513 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
7516 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
7525 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
7530 * A menu which appear when right-clicking on buddy in contact list.
7533 sipe_buddy_menu(PurpleBuddy
*buddy
)
7535 PurpleBlistNode
*g_node
;
7536 PurpleGroup
*group
, *gr_parent
;
7537 PurpleMenuAction
*act
;
7539 GList
*menu_groups
= NULL
;
7540 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
7543 const char *phone_disp_str
;
7544 gchar
*self
= sip_uri_self(sip
);
7546 SIPE_SESSION_FOREACH
{
7547 if (g_ascii_strcasecmp(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
7549 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
7551 PurpleConvChatBuddyFlags flags
;
7552 PurpleConvChatBuddyFlags flags_us
;
7554 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
7555 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
7556 if (session
->focus_uri
7557 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
7558 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
7560 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
7561 act
= purple_menu_action_new(label
,
7562 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
7563 session
->chat_title
, NULL
);
7565 menu
= g_list_prepend(menu
, act
);
7568 if (session
->focus_uri
7569 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
7571 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
7572 act
= purple_menu_action_new(label
,
7573 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
7574 session
->chat_title
, NULL
);
7576 menu
= g_list_prepend(menu
, act
);
7581 if (!session
->focus_uri
7582 || (session
->focus_uri
&& !session
->locked
))
7584 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
7585 act
= purple_menu_action_new(label
,
7586 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
7587 session
->chat_title
, NULL
);
7589 menu
= g_list_prepend(menu
, act
);
7593 } SIPE_SESSION_FOREACH_END
;
7595 act
= purple_menu_action_new(_("New chat"),
7596 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
7598 menu
= g_list_prepend(menu
, act
);
7600 if (sip
->csta
&& !sip
->csta
->line_status
) {
7603 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
7604 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
7606 gchar
*label
= g_strdup_printf(_("Work %s"),
7607 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
7608 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
7612 menu
= g_list_prepend(menu
, act
);
7616 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
7617 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
7619 gchar
*label
= g_strdup_printf(_("Mobile %s"),
7620 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
7621 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
7625 menu
= g_list_prepend(menu
, act
);
7629 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
7630 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
7632 gchar
*label
= g_strdup_printf(_("Home %s"),
7633 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
7634 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
7638 menu
= g_list_prepend(menu
, act
);
7642 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
7643 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
7645 gchar
*label
= g_strdup_printf(_("Other %s"),
7646 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
7647 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
7651 menu
= g_list_prepend(menu
, act
);
7655 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
7656 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
7658 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
7659 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
7660 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
7664 menu
= g_list_prepend(menu
, act
);
7668 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
7670 act
= purple_menu_action_new(_("Send email..."),
7671 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
7673 menu
= g_list_prepend(menu
, act
);
7676 gr_parent
= purple_buddy_get_group(buddy
);
7677 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
7678 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
7681 group
= (PurpleGroup
*)g_node
;
7682 if (group
== gr_parent
)
7685 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
7688 act
= purple_menu_action_new(purple_group_get_name(group
),
7689 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
7691 menu_groups
= g_list_prepend(menu_groups
, act
);
7693 menu_groups
= g_list_reverse(menu_groups
);
7695 act
= purple_menu_action_new(_("Copy to"),
7698 menu
= g_list_prepend(menu
, act
);
7699 menu
= g_list_reverse(menu
);
7706 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
7708 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
7709 struct sip_session
*session
;
7711 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
7712 sipe_conf_modify_conference_lock(sip
, session
, locked
);
7716 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
7718 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
7719 sipe_conf_modify_lock(chat
, FALSE
);
7723 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
7725 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
7726 sipe_conf_modify_lock(chat
, TRUE
);
7730 sipe_chat_menu(PurpleChat
*chat
)
7732 PurpleMenuAction
*act
;
7733 PurpleConvChatBuddyFlags flags_us
;
7735 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
7736 struct sip_session
*session
;
7739 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
7740 if (!session
) return NULL
;
7742 self
= sip_uri_self(sip
);
7743 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
7745 if (session
->focus_uri
7746 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
7748 if (session
->locked
) {
7749 act
= purple_menu_action_new(_("Unlock"),
7750 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
7752 menu
= g_list_prepend(menu
, act
);
7754 act
= purple_menu_action_new(_("Lock"),
7755 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
7757 menu
= g_list_prepend(menu
, act
);
7761 menu
= g_list_reverse(menu
);
7768 sipe_blist_node_menu(PurpleBlistNode
*node
)
7770 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
7771 return sipe_buddy_menu((PurpleBuddy
*) node
);
7772 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
7773 return sipe_chat_menu((PurpleChat
*)node
);
7780 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
7782 gboolean ret
= TRUE
;
7783 char *uri
= trans
->payload
->data
;
7785 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
7786 PurpleBuddy
*pbuddy
;
7787 struct sipe_buddy
*sbuddy
;
7789 char *device_name
= NULL
;
7790 char *server_alias
= NULL
;
7791 char *phone_number
= NULL
;
7795 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
7797 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
7798 alias
= purple_buddy_get_local_alias(pbuddy
);
7802 //will query buddy UA's capabilities and send answer to log
7803 sipe_options_request(sip
, uri
);
7805 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
7808 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
7812 if (msg
->response
!= 200) {
7813 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
7815 xmlnode
*searchResults
;
7818 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
7819 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
7820 if (!searchResults
) {
7821 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
7822 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
7824 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
7825 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
7826 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
7828 /* For 2007 system we will take this from ContactCard -
7829 * it has cleaner tel: URIs at least
7831 if (!sip
->ocs2007
) {
7832 char *tel_uri
= sip_to_tel_uri(phone_number
);
7833 /* trims its parameters, so call first */
7834 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
7835 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
7836 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
7837 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
7841 if (server_alias
&& strlen(server_alias
) > 0) {
7842 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
7844 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
7845 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
7847 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
7848 purple_notify_user_info_add_pair(info
, _("Office"), value
);
7850 if (phone_number
&& strlen(phone_number
) > 0) {
7851 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
7853 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
7854 purple_notify_user_info_add_pair(info
, _("Company"), value
);
7856 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
7857 purple_notify_user_info_add_pair(info
, _("City"), value
);
7859 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
7860 purple_notify_user_info_add_pair(info
, _("State"), value
);
7862 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
7863 purple_notify_user_info_add_pair(info
, _("Country"), value
);
7865 if (email
&& strlen(email
) > 0) {
7866 purple_notify_user_info_add_pair(info
, _("E-Mail address"), email
);
7870 xmlnode_free(searchResults
);
7873 purple_notify_user_info_add_section_break(info
);
7875 if (!server_alias
|| !strcmp("", server_alias
)) {
7876 g_free(server_alias
);
7877 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
7879 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
7883 /* present alias if it differs from server alias */
7884 if (alias
&& (!server_alias
|| strcmp(alias
, server_alias
)))
7886 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
7889 if (!email
|| !strcmp("", email
)) {
7891 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
7893 purple_notify_user_info_add_pair(info
, _("E-Mail address"), email
);
7897 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
7899 purple_notify_user_info_add_pair(info
, _("Site"), site
);
7903 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
7906 /* show a buddy's user info in a nice dialog box */
7907 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
7908 uri
, /* buddy's URI */
7910 NULL
, /* callback called when dialog closed */
7911 NULL
); /* userdata for callback */
7913 g_free(phone_number
);
7914 g_free(server_alias
);
7916 g_free(device_name
);
7922 * AD search first, LDAP based
7924 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
7926 struct sipe_account_data
*sip
= gc
->proto_data
;
7927 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
7928 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
7929 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
7930 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
7932 payload
->destroy
= g_free
;
7933 payload
->data
= g_strdup(username
);
7935 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
7936 send_soap_request_with_cb(sip
, domain_uri
, body
,
7937 (TransCallback
) process_get_info_response
, payload
);
7943 static PurplePlugin
*my_protocol
= NULL
;
7945 static PurplePluginProtocolInfo prpl_info
=
7947 OPT_PROTO_CHAT_TOPIC
,
7948 NULL
, /* user_splits */
7949 NULL
, /* protocol_options */
7950 NO_BUDDY_ICONS
, /* icon_spec */
7951 sipe_list_icon
, /* list_icon */
7952 NULL
, /* list_emblems */
7953 sipe_status_text
, /* status_text */
7954 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
7955 sipe_status_types
, /* away_states */
7956 sipe_blist_node_menu
, /* blist_node_menu */
7957 NULL
, /* chat_info */
7958 NULL
, /* chat_info_defaults */
7959 sipe_login
, /* login */
7960 sipe_close
, /* close */
7961 sipe_im_send
, /* send_im */
7962 NULL
, /* set_info */ // TODO maybe
7963 sipe_send_typing
, /* send_typing */
7964 sipe_get_info
, /* get_info */
7965 sipe_set_status
, /* set_status */
7966 sipe_set_idle
, /* set_idle */
7967 NULL
, /* change_passwd */
7968 sipe_add_buddy
, /* add_buddy */
7969 NULL
, /* add_buddies */
7970 sipe_remove_buddy
, /* remove_buddy */
7971 NULL
, /* remove_buddies */
7972 sipe_add_permit
, /* add_permit */
7973 sipe_add_deny
, /* add_deny */
7974 sipe_add_deny
, /* rem_permit */
7975 sipe_add_permit
, /* rem_deny */
7976 dummy_permit_deny
, /* set_permit_deny */
7977 NULL
, /* join_chat */
7978 NULL
, /* reject_chat */
7979 NULL
, /* get_chat_name */
7980 sipe_chat_invite
, /* chat_invite */
7981 sipe_chat_leave
, /* chat_leave */
7982 NULL
, /* chat_whisper */
7983 sipe_chat_send
, /* chat_send */
7984 sipe_keep_alive
, /* keepalive */
7985 NULL
, /* register_user */
7986 NULL
, /* get_cb_info */ // deprecated
7987 NULL
, /* get_cb_away */ // deprecated
7988 sipe_alias_buddy
, /* alias_buddy */
7989 sipe_group_buddy
, /* group_buddy */
7990 sipe_rename_group
, /* rename_group */
7991 NULL
, /* buddy_free */
7992 sipe_convo_closed
, /* convo_closed */
7993 purple_normalize_nocase
, /* normalize */
7994 NULL
, /* set_buddy_icon */
7995 sipe_remove_group
, /* remove_group */
7996 NULL
, /* get_cb_real_name */ // TODO?
7997 NULL
, /* set_chat_topic */
7998 NULL
, /* find_blist_chat */
7999 NULL
, /* roomlist_get_list */
8000 NULL
, /* roomlist_cancel */
8001 NULL
, /* roomlist_expand_category */
8002 NULL
, /* can_receive_file */
8003 NULL
, /* send_file */
8004 NULL
, /* new_xfer */
8005 NULL
, /* offline_message */
8006 NULL
, /* whiteboard_prpl_ops */
8007 sipe_send_raw
, /* send_raw */
8008 NULL
, /* roomlist_room_serialize */
8009 NULL
, /* unregister_user */
8010 NULL
, /* send_attention */
8011 NULL
, /* get_attention_types */
8012 #if !PURPLE_VERSION_CHECK(2,5,0)
8013 /* Backward compatibility when compiling against 2.4.x API */
8014 (void (*)(void)) /* _purple_reserved4 */
8016 sizeof(PurplePluginProtocolInfo
), /* struct_size */
8017 #if PURPLE_VERSION_CHECK(2,5,0)
8018 sipe_get_account_text_table
, /* get_account_text_table */
8019 #if PURPLE_VERSION_CHECK(2,6,0)
8020 NULL
, /* initiate_media */
8021 NULL
, /* get_media_caps */
8027 static PurplePluginInfo info
= {
8028 PURPLE_PLUGIN_MAGIC
,
8029 PURPLE_MAJOR_VERSION
,
8030 PURPLE_MINOR_VERSION
,
8031 PURPLE_PLUGIN_PROTOCOL
, /**< type */
8032 NULL
, /**< ui_requirement */
8034 NULL
, /**< dependencies */
8035 PURPLE_PRIORITY_DEFAULT
, /**< priority */
8036 "prpl-sipe", /**< id */
8037 "Office Communicator", /**< name */
8038 SIPE_VERSION
, /**< version */
8039 "Microsoft Office Communicator Protocol Plugin", /**< summary */
8040 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
8041 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
8042 "Anibal Avelar <avelar@gmail.com>, " /**< author */
8043 "Gabriel Burt <gburt@novell.com>, " /**< author */
8044 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
8045 "pier11 <pier11@operamail.com>", /**< author */
8046 "http://sipe.sourceforge.net/", /**< homepage */
8047 sipe_plugin_load
, /**< load */
8048 sipe_plugin_unload
, /**< unload */
8049 sipe_plugin_destroy
, /**< destroy */
8050 NULL
, /**< ui_info */
8051 &prpl_info
, /**< extra_info */
8060 static void sipe_plugin_destroy(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
8064 entry
= prpl_info
.protocol_options
;
8066 purple_account_option_destroy(entry
->data
);
8067 entry
= g_list_delete_link(entry
, entry
);
8069 prpl_info
.protocol_options
= NULL
;
8071 entry
= prpl_info
.user_splits
;
8073 purple_account_user_split_destroy(entry
->data
);
8074 entry
= g_list_delete_link(entry
, entry
);
8076 prpl_info
.user_splits
= NULL
;
8079 static void init_plugin(PurplePlugin
*plugin
)
8081 PurpleAccountUserSplit
*split
;
8082 PurpleAccountOption
*option
;
8087 purple_debug_info(PACKAGE
, "bindtextdomain = %s\n", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
8088 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s\n",
8089 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
8090 textdomain(GETTEXT_PACKAGE
);
8093 purple_plugin_register(plugin
);
8095 split
= purple_account_user_split_new(_("Login\n user or DOMAIN\\user or\n user@company.com"), NULL
, ',');
8096 purple_account_user_split_set_reverse(split
, FALSE
);
8097 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
8099 option
= purple_account_option_string_new(_("Server[:Port]\n(leave empty for auto-discovery)"), "server", "");
8100 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8102 option
= purple_account_option_list_new(_("Connection type"), "transport", NULL
);
8103 purple_account_option_add_list_item(option
, _("Auto"), "auto");
8104 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
8105 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
8106 purple_account_option_add_list_item(option
, _("UDP"), "udp");
8107 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8109 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
8110 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
8112 option
= purple_account_option_string_new(_("User Agent"), "useragent", "");
8113 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8116 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
8117 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8119 /* Suitable for sspi/NTLM, sspi/Kerberos and krb5 security mechanisms
8120 * No login/password is taken into account if this option present,
8121 * instead used default credentials stored in OS.
8123 option
= purple_account_option_bool_new(_("Use Single Sign-On"), "sso", TRUE
);
8124 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
8126 my_protocol
= plugin
;
8129 /* I had to redefined the function for it load, but works */
8130 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
8131 plugin
->info
= &(info
);
8132 init_plugin((plugin
));
8133 sipe_plugin_load((plugin
));
8134 return purple_plugin_register(plugin
);