6 * Copyright (C) 2010 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 pier11 <pier11@operamail.com>
8 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
9 * Copyright (C) 2009 pier11 <pier11@operamail.com>
10 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
11 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
12 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
15 * Thanks to Google's Summer of Code Program and the helpful mentors
18 * Session-based SIP MESSAGE documentation:
19 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
44 #define _LIBC_INTERNAL_
47 #include "libc_interface.h"
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
62 #include "sipe-common.h"
66 #include "connection.h"
67 #include "conversation.h"
70 #include "circbuffer.h"
81 #include "savedstatuses.h"
87 #include "core-depurple.h" /* Temporary for the core de-purple transition */
93 #include "sipe-chat.h"
94 #include "sipe-conf.h"
95 #include "sipe-dialog.h"
99 #include "sipe-session.h"
100 #include "sipe-sign.h"
101 #include "sipe-utils.h"
102 #include "sipe-xml.h"
103 #include "http-conn.h"
107 /* Backward compatibility when compiling against 2.4.x API */
108 #if !PURPLE_VERSION_CHECK(2,5,0)
109 #define PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY 0x0100
112 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
114 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
115 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
117 /* Keep in sync with sipe_transport_type! */
118 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
119 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
121 /* Status identifiers (see also: sipe_status_types()) */
122 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
123 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
124 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
125 /* PURPLE_STATUS_UNAVAILABLE: */
126 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
127 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
128 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
129 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
130 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
131 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
132 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
133 /* PURPLE_STATUS_AWAY: */
134 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
135 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
136 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
137 /** Reuters status (user settable) */
138 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
139 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
140 /* ??? PURPLE_STATUS_MOBILE */
141 /* ??? PURPLE_STATUS_TUNE */
143 /* Status attributes (see also sipe_status_types() */
144 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
146 #define SDP_ACCEPT_TYPES "text/plain text/html image/gif multipart/related application/im-iscomposing+xml application/ms-imdn+xml text/x-msmsgsinvite"
148 static struct sipe_activity_map_struct
153 const char *status_id
;
155 } const sipe_activity_map
[] =
157 /* This has nothing to do with Availability numbers, like 3500 (online).
158 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
160 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
161 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
162 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
163 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
164 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
165 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
166 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
167 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
168 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
169 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
170 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
171 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
172 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
173 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
174 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
176 /** @param x is sipe_activity */
177 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
180 /* Action name templates */
181 #define ACTION_NAME_PRESENCE "<presence><%s>"
184 sipe_get_activity_by_token(const char *token
)
188 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
190 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
191 return sipe_activity_map
[i
].type
;
194 return sipe_activity_map
[0].type
;
198 sipe_get_activity_desc_by_token(const char *token
)
200 if (!token
) return NULL
;
202 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
205 /** Allows to send typed messages from chat window again after account reinstantiation. */
207 sipe_rejoin_chat(PurpleConversation
*conv
)
209 if (purple_conversation_get_type(conv
) == PURPLE_CONV_TYPE_CHAT
&&
210 PURPLE_CONV_CHAT(conv
)->left
)
212 PURPLE_CONV_CHAT(conv
)->left
= FALSE
;
213 purple_conversation_update(conv
, PURPLE_CONV_UPDATE_CHATLEFT
);
217 static char *genbranch()
219 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
220 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
221 rand() & 0xFFFF, rand() & 0xFFFF);
225 static char *default_ua
= NULL
;
227 sipe_get_useragent(struct sipe_account_data
*sip
)
229 const char *useragent
= purple_account_get_string(sip
->account
, "useragent", "");
230 if (is_empty(useragent
)) {
232 /*@TODO: better approach to define _user_ OS, it's version and host architecture */
234 #if defined(__linux__) || defined(__linux) || defined(__LINUX__)
235 #define SIPE_TARGET_PLATFORM "linux"
236 #elif defined(__NetBSD__) ||defined( __OpenBSD__) || defined(__FreeBSD__)
237 #define SIPE_TARGET_PLATFORM "bsd"
238 #elif defined(__APPLE__) || defined(__MACOS__)
239 #define SIPE_TARGET_PLATFORM "macosx"
240 #elif defined(_AIX) || defined(__AIX__) || defined(__aix__)
241 #define SIPE_TARGET_PLATFORM "aix"
242 #elif defined(__solaris__) || defined(__sun)
243 #define SIPE_TARGET_PLATFORM "sun"
244 #elif defined(_WIN32)
245 #define SIPE_TARGET_PLATFORM "win"
246 #elif defined(__CYGWIN__)
247 #define SIPE_TARGET_PLATFORM "cygwin"
248 #elif defined(__hpux__)
249 #define SIPE_TARGET_PLATFORM "hpux"
250 #elif defined(__sgi__)
251 #define SIPE_TARGET_PLATFORM "irix"
253 #define SIPE_TARGET_PLATFORM "unknown"
256 #if defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64)
257 #define SIPE_TARGET_ARCH "x86_64"
258 #elif defined(__386__) || defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(_M_I386)
259 #define SIPE_TARGET_ARCH "i386"
260 #elif defined(__ppc64__)
261 #define SIPE_TARGET_ARCH "ppc64"
262 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) || defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PWR)
263 #define SIPE_TARGET_ARCH "ppc"
264 #elif defined(__hppa__) || defined(__hppa)
265 #define SIPE_TARGET_ARCH "hppa"
266 #elif defined(__mips__) || defined(__mips) || defined(_MIPS_ARCH) || defined(_M_MRX000)
267 #define SIPE_TARGET_ARCH "mips"
268 #elif defined(__s390__) || defined(__s390) || defined(__s390x__) || defined(__s390x)
269 #define SIPE_TARGET_ARCH "s390"
270 #elif defined(__sparc__) || defined(__sparc) || defined(__sparcv8)
271 #define SIPE_TARGET_ARCH "sparc"
272 #elif defined(__arm__)
273 #define SIPE_TARGET_ARCH "arm"
275 #define SIPE_TARGET_ARCH "other"
278 default_ua
= g_strdup_printf("Purple/%s Sipe/" PACKAGE_VERSION
" (" SIPE_TARGET_PLATFORM
"-" SIPE_TARGET_ARCH
"; %s)",
279 purple_core_get_version(),
280 sip
->server_version
? sip
->server_version
: "");
282 useragent
= default_ua
;
287 static const char *sipe_list_icon(SIPE_UNUSED_PARAMETER PurpleAccount
*a
,
288 SIPE_UNUSED_PARAMETER PurpleBuddy
*b
)
293 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
);
295 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
296 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
299 static void sipe_close(PurpleConnection
*gc
);
301 static void send_presence_status(struct sipe_account_data
*sip
);
303 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
305 static void sipe_keep_alive(PurpleConnection
*gc
)
307 struct sipe_account_data
*sip
= gc
->proto_data
;
308 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
309 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
310 gchar buf
[2] = {0, 0};
311 purple_debug_info("sipe", "sending keep alive\n");
312 sendto(sip
->fd
, buf
, 1, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
));
314 time_t now
= time(NULL
);
315 if ((sip
->keepalive_timeout
> 0) &&
316 ((guint
) (now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
) &&
317 ((guint
) (now
- gc
->last_received
) >= sip
->keepalive_timeout
)
319 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
320 sendout_pkt(gc
, "\r\n\r\n");
321 sip
->last_keepalive
= now
;
326 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
328 struct sip_connection
*ret
= NULL
;
329 GSList
*entry
= sip
->openconns
;
332 if (ret
->fd
== fd
) return ret
;
338 static void sipe_auth_free(struct sip_auth
*auth
)
340 g_free(auth
->opaque
);
344 g_free(auth
->target
);
347 auth
->type
= AUTH_TYPE_UNSET
;
350 g_free(auth
->gssapi_data
);
351 auth
->gssapi_data
= NULL
;
352 sip_sec_destroy_context(auth
->gssapi_context
);
353 auth
->gssapi_context
= NULL
;
356 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
358 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
360 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
364 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
366 struct sip_connection
*conn
= connection_find(sip
, fd
);
368 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
369 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
375 static void connection_free_all(struct sipe_account_data
*sip
)
377 struct sip_connection
*ret
= NULL
;
378 GSList
*entry
= sip
->openconns
;
381 connection_remove(sip
, ret
->fd
);
382 entry
= sip
->openconns
;
387 sipe_make_signature(struct sipe_account_data
*sip
,
390 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
393 const char *authuser
= sip
->authuser
;
397 if (!authuser
|| strlen(authuser
) < 1) {
398 authuser
= sip
->username
;
401 if (auth
->type
== AUTH_TYPE_NTLM
|| auth
->type
== AUTH_TYPE_KERBEROS
) { /* NTLM or Kerberos */
402 gchar
*auth_protocol
= (auth
->type
== AUTH_TYPE_NTLM
? "NTLM" : "Kerberos");
405 // If we have a signature for the message, include that
406 if (msg
->signature
) {
407 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
);
410 if ((auth
->type
== AUTH_TYPE_NTLM
&& auth
->nc
== 3 && auth
->gssapi_data
&& auth
->gssapi_context
== NULL
)
411 || (auth
->type
== AUTH_TYPE_KERBEROS
&& auth
->nc
== 3)) {
414 gchar
*sign_str
= NULL
;
416 gssapi_data
= sip_sec_init_context(&(auth
->gssapi_context
),
419 purple_account_get_bool(sip
->account
, "sso", TRUE
),
420 sip
->authdomain
? sip
->authdomain
: "",
425 if (!gssapi_data
|| !auth
->gssapi_context
) {
426 sip
->gc
->wants_to_die
= TRUE
;
427 purple_connection_error(sip
->gc
, _("Failed to authenticate to server"));
431 if (auth
->version
> 3) {
432 sipe_make_signature(sip
, msg
);
433 sign_str
= g_strdup_printf(", crand=\"%s\", cnum=\"%s\", response=\"%s\"",
434 msg
->rand
, msg
->num
, msg
->signature
);
436 sign_str
= g_strdup("");
439 opaque
= (auth
->type
== AUTH_TYPE_NTLM
? g_strdup_printf(", opaque=\"%s\"", auth
->opaque
) : g_strdup(""));
440 version_str
= auth
->version
> 2 ? g_strdup_printf(", version=%d", auth
->version
) : g_strdup("");
441 ret
= g_strdup_printf("%s qop=\"auth\"%s, realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"%s%s", auth_protocol
, opaque
, auth
->realm
, auth
->target
, gssapi_data
, version_str
, sign_str
);
449 version_str
= auth
->version
> 2 ? g_strdup_printf(", version=%d", auth
->version
) : g_strdup("");
450 ret
= g_strdup_printf("%s qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"%s", auth_protocol
, auth
->realm
, auth
->target
, version_str
);
454 } else { /* Digest */
456 /* Calculate new session key */
458 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest nonce: %s realm: %s\n", auth
->gssapi_data
, auth
->realm
);
459 auth
->opaque
= purple_cipher_http_digest_calculate_session_key("md5",
460 authuser
, auth
->realm
, sip
->password
,
461 auth
->gssapi_data
, NULL
);
464 sprintf(noncecount
, "%08d", auth
->nc
++);
465 response
= purple_cipher_http_digest_calculate_response("md5",
466 msg
->method
, msg
->target
, NULL
, NULL
,
467 auth
->gssapi_data
, noncecount
, NULL
,
469 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Digest response %s\n", response
);
471 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
);
477 static char *parse_attribute(const char *attrname
, const char *source
)
479 const char *tmp
, *tmp2
;
481 int len
= strlen(attrname
);
483 if (g_str_has_prefix(source
, attrname
)) {
485 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
487 retval
= g_strndup(tmp
, tmp2
- tmp
);
489 retval
= g_strdup(tmp
);
495 static void fill_auth(const gchar
*hdr
, struct sip_auth
*auth
)
501 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
505 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
506 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type NTLM\n");
507 auth
->type
= AUTH_TYPE_NTLM
;
510 } else if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
511 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Kerberos\n");
512 auth
->type
= AUTH_TYPE_KERBEROS
;
516 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth: type Digest\n");
517 auth
->type
= AUTH_TYPE_DIGEST
;
521 parts
= g_strsplit(hdr
, "\", ", 0);
522 for (i
= 0; parts
[i
]; i
++) {
525 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
527 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
528 g_free(auth
->gssapi_data
);
529 auth
->gssapi_data
= tmp
;
531 if (auth
->type
== AUTH_TYPE_NTLM
) {
532 /* NTLM module extracts nonce from gssapi-data */
536 } else if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
537 /* Only used with AUTH_TYPE_DIGEST */
538 g_free(auth
->gssapi_data
);
539 auth
->gssapi_data
= tmp
;
540 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
541 g_free(auth
->opaque
);
543 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
547 if (auth
->type
== AUTH_TYPE_DIGEST
) {
548 /* Throw away old session key */
549 g_free(auth
->opaque
);
553 } else if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
554 g_free(auth
->target
);
556 } else if ((tmp
= parse_attribute("version=", parts
[i
]))) {
557 auth
->version
= atoi(tmp
);
560 // uncomment to revert to previous functionality if version 3+ does not work.
561 // auth->version = 2;
568 static void sipe_canwrite_cb(gpointer data
,
569 SIPE_UNUSED_PARAMETER gint source
,
570 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
572 PurpleConnection
*gc
= data
;
573 struct sipe_account_data
*sip
= gc
->proto_data
;
577 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
579 if (max_write
== 0) {
580 if (sip
->tx_handler
!= 0){
581 purple_input_remove(sip
->tx_handler
);
587 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
589 if (written
< 0 && errno
== EAGAIN
)
591 else if (written
<= 0) {
592 /*TODO: do we really want to disconnect on a failure to write?*/
593 purple_connection_error(gc
, _("Could not write"));
597 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
600 static void sipe_canwrite_cb_ssl(gpointer data
,
601 SIPE_UNUSED_PARAMETER gint src
,
602 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
604 PurpleConnection
*gc
= data
;
605 struct sipe_account_data
*sip
= gc
->proto_data
;
609 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
611 if (max_write
== 0) {
612 if (sip
->tx_handler
!= 0) {
613 purple_input_remove(sip
->tx_handler
);
619 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
621 if (written
< 0 && errno
== EAGAIN
)
623 else if (written
<= 0) {
624 /*TODO: do we really want to disconnect on a failure to write?*/
625 purple_connection_error(gc
, _("Could not write"));
629 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
632 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
634 static void send_later_cb(gpointer data
, gint source
,
635 SIPE_UNUSED_PARAMETER
const gchar
*error
)
637 PurpleConnection
*gc
= data
;
638 struct sipe_account_data
*sip
;
639 struct sip_connection
*conn
;
641 if (!PURPLE_CONNECTION_IS_VALID(gc
))
649 purple_connection_error(gc
, _("Could not connect"));
653 sip
= gc
->proto_data
;
655 sip
->connecting
= FALSE
;
656 sip
->last_keepalive
= time(NULL
);
658 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
660 /* If there is more to write now, we need to register a handler */
661 if (sip
->txbuf
->bufused
> 0)
662 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
664 conn
= connection_create(sip
, source
);
665 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
668 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
670 struct sipe_account_data
*sip
;
672 if (!PURPLE_CONNECTION_IS_VALID(gc
))
674 if (gsc
) purple_ssl_close(gsc
);
678 sip
= gc
->proto_data
;
681 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
682 sip
->connecting
= FALSE
;
683 sip
->last_keepalive
= time(NULL
);
685 connection_create(sip
, gsc
->fd
);
687 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
692 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
693 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
695 PurpleConnection
*gc
= data
;
696 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
697 if (sip
== NULL
) return;
699 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
701 /* If there is more to write now */
702 if (sip
->txbuf
->bufused
> 0) {
703 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
708 static void sendlater(PurpleConnection
*gc
, const char *buf
)
710 struct sipe_account_data
*sip
= gc
->proto_data
;
712 if (!sip
->connecting
) {
713 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
714 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
715 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
717 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
718 purple_connection_error(gc
, _("Could not create socket"));
721 sip
->connecting
= TRUE
;
724 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
725 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
727 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
730 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
732 struct sipe_account_data
*sip
= gc
->proto_data
;
733 time_t currtime
= time(NULL
);
734 int writelen
= strlen(buf
);
737 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sending - %s######\n%s######\n", ctime(&currtime
), tmp
= fix_newlines(buf
));
739 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
740 if (sendto(sip
->fd
, buf
, writelen
, 0, sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
741 purple_debug_info("sipe", "could not send packet\n");
750 if (sip
->tx_handler
) {
755 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
757 ret
= write(sip
->fd
, buf
, writelen
);
761 if (ret
< 0 && errno
== EAGAIN
)
763 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
768 if (ret
< writelen
) {
769 if (!sip
->tx_handler
){
771 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
774 sip
->tx_handler
= purple_input_add(sip
->fd
,
775 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
780 /* XXX: is it OK to do this? You might get part of a request sent
781 with part of another. */
782 if (sip
->txbuf
->bufused
> 0)
783 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
785 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
791 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
793 sendout_pkt(gc
, buf
);
797 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
799 GSList
*tmp
= msg
->headers
;
802 GString
*outstr
= g_string_new("");
803 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
805 name
= ((struct sipnameval
*) (tmp
->data
))->name
;
806 value
= ((struct sipnameval
*) (tmp
->data
))->value
;
807 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
808 tmp
= g_slist_next(tmp
);
810 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
811 sendout_pkt(sip
->gc
, outstr
->str
);
812 g_string_free(outstr
, TRUE
);
816 sipe_make_signature(struct sipe_account_data
*sip
,
819 if (sip
->registrar
.gssapi_context
) {
820 struct sipmsg_breakdown msgbd
;
821 gchar
*signature_input_str
;
823 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
824 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
825 sip
->registrar
.ntlm_num
++;
826 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
827 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
828 if (signature_input_str
!= NULL
) {
829 char *signature_hex
= sip_sec_make_signature(sip
->registrar
.gssapi_context
, signature_input_str
);
830 msg
->signature
= signature_hex
;
831 msg
->rand
= g_strdup(msgbd
.rand
);
832 msg
->num
= g_strdup(msgbd
.num
);
833 g_free(signature_input_str
);
835 sipmsg_breakdown_free(&msgbd
);
839 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
843 if (sip
->registrar
.type
== AUTH_TYPE_UNSET
) {
847 sipe_make_signature(sip
, msg
);
849 if (sip
->registrar
.type
&& sipe_strequal(method
, "REGISTER")) {
850 buf
= auth_header(sip
, &sip
->registrar
, msg
);
852 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
855 } else if (sipe_strequal(method
,"SUBSCRIBE") || sipe_strequal(method
,"SERVICE") || sipe_strequal(method
,"MESSAGE") || sipe_strequal(method
,"INVITE") || sipe_strequal(method
, "ACK") || sipe_strequal(method
, "NOTIFY") || sipe_strequal(method
, "BYE") || sipe_strequal(method
, "INFO") || sipe_strequal(method
, "OPTIONS") || sipe_strequal(method
, "REFER")) {
856 sip
->registrar
.nc
= 3;
857 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
859 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
860 sip
->registrar
.type
= AUTH_TYPE_KERBEROS
;
865 buf
= auth_header(sip
, &sip
->registrar
, msg
);
866 sipmsg_add_header_now_pos(msg
, "Authorization", buf
, 5);
869 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
873 void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
874 const char *text
, const char *body
)
878 GString
*outstr
= g_string_new("");
879 struct sipe_account_data
*sip
= gc
->proto_data
;
882 const gchar
*keepers
[] = { "To", "From", "Call-ID", "CSeq", "Via", "Record-Route", NULL
};
884 /* Can return NULL! */
885 contact
= get_contact(sip
);
887 sipmsg_add_header(msg
, "Contact", contact
);
892 gchar
*len
= g_strdup_printf("%" G_GSIZE_FORMAT
, (gsize
) strlen(body
));
893 sipmsg_add_header(msg
, "Content-Length", len
);
896 sipmsg_add_header(msg
, "Content-Length", "0");
899 msg
->response
= code
;
901 sipmsg_strip_headers(msg
, keepers
);
902 sipmsg_merge_new_headers(msg
);
903 sign_outgoing_message(msg
, sip
, msg
->method
);
905 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
908 name
= ((struct sipnameval
*) (tmp
->data
))->name
;
909 value
= ((struct sipnameval
*) (tmp
->data
))->value
;
911 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
912 tmp
= g_slist_next(tmp
);
914 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
915 sendout_pkt(gc
, outstr
->str
);
916 g_string_free(outstr
, TRUE
);
919 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
921 if (sip
->transactions
) {
922 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
923 purple_debug_info("sipe", "sip->transactions count:%d after removal\n", g_slist_length(sip
->transactions
));
925 if (trans
->msg
) sipmsg_free(trans
->msg
);
926 if (trans
->payload
) {
927 (*trans
->payload
->destroy
)(trans
->payload
->data
);
928 g_free(trans
->payload
);
935 static struct transaction
*
936 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
938 const gchar
*call_id
;
940 struct transaction
*trans
= g_new0(struct transaction
, 1);
942 trans
->time
= time(NULL
);
943 trans
->msg
= (struct sipmsg
*)msg
;
944 call_id
= sipmsg_find_header(trans
->msg
, "Call-ID");
945 cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
946 trans
->key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
947 trans
->callback
= callback
;
948 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
949 purple_debug_info("sipe", "sip->transactions count:%d after addition\n", g_slist_length(sip
->transactions
));
953 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
955 struct transaction
*trans
;
956 GSList
*transactions
= sip
->transactions
;
957 const gchar
*call_id
= sipmsg_find_header(msg
, "Call-ID");
958 const gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
961 if (!call_id
|| !cseq
) {
962 purple_debug(PURPLE_DEBUG_ERROR
, "sipe", "transaction_find: no Call-ID or CSeq!\n");
966 key
= g_strdup_printf("<%s><%s>", call_id
, cseq
);
967 while (transactions
) {
968 trans
= transactions
->data
;
969 if (!g_strcasecmp(trans
->key
, key
)) {
973 transactions
= transactions
->next
;
981 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
982 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
983 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
985 struct sipe_account_data
*sip
= gc
->proto_data
;
986 const char *addh
= "";
989 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
990 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
991 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
992 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
993 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
994 gchar
*route
= g_strdup("");
995 gchar
*epid
= get_epid(sip
);
996 int cseq
= dialog
? ++dialog
->cseq
: 1 /* as Call-Id is new in this case */;
997 struct transaction
*trans
= NULL
;
999 if (dialog
&& dialog
->routes
)
1001 GSList
*iter
= dialog
->routes
;
1006 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
1008 iter
= g_slist_next(iter
);
1012 if (!ourtag
&& !dialog
) {
1016 if (sipe_strequal(method
, "REGISTER")) {
1017 if (sip
->regcallid
) {
1019 callid
= g_strdup(sip
->regcallid
);
1021 sip
->regcallid
= g_strdup(callid
);
1026 if (addheaders
) addh
= addheaders
;
1028 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
1029 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
1030 "From: <sip:%s>%s%s;epid=%s\r\n"
1031 "To: <%s>%s%s%s%s\r\n"
1032 "Max-Forwards: 70\r\n"
1034 "User-Agent: %s\r\n"
1037 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
1039 dialog
&& dialog
->request
? dialog
->request
: url
,
1040 TRANSPORT_DESCRIPTOR
,
1041 purple_network_get_my_ip(-1),
1043 branch
? ";branch=" : "",
1044 branch
? branch
: "",
1046 ourtag
? ";tag=" : "",
1047 ourtag
? ourtag
: "",
1050 theirtag
? ";tag=" : "",
1051 theirtag
? theirtag
: "",
1052 theirepid
? ";epid=" : "",
1053 theirepid
? theirepid
: "",
1056 sipe_get_useragent(sip
),
1060 body
? (gsize
) strlen(body
) : 0,
1064 //printf ("parsing msg buf:\n%s\n\n", buf);
1065 msg
= sipmsg_parse_msg(buf
);
1076 sign_outgoing_message (msg
, sip
, method
);
1078 buf
= sipmsg_to_string (msg
);
1080 /* add to ongoing transactions */
1081 /* ACK isn't supposed to be answered ever. So we do not keep transaction for it. */
1082 if (!sipe_strequal(method
, "ACK")) {
1083 trans
= transactions_add_buf(sip
, msg
, tc
);
1087 sendout_pkt(gc
, buf
);
1094 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
1097 send_soap_request_with_cb(struct sipe_account_data
*sip
,
1100 TransCallback callback
,
1101 struct transaction_payload
*payload
)
1103 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sip
);
1104 gchar
*contact
= get_contact(sip
);
1105 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1106 "Content-Type: application/SOAP+xml\r\n",contact
);
1108 struct transaction
*trans
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1109 trans
->payload
= payload
;
1116 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1118 send_soap_request_with_cb(sip
, NULL
, body
, NULL
, NULL
);
1121 static char *get_contact_register(struct sipe_account_data
*sip
)
1123 char *epid
= get_epid(sip
);
1124 char *uuid
= generateUUIDfromEPID(epid
);
1125 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
);
1131 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1139 if (!sip
->sipdomain
) return;
1141 uri
= sip_uri_from_name(sip
->sipdomain
);
1142 expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1143 to
= sip_uri_self(sip
);
1144 contact
= get_contact_register(sip
);
1145 hdr
= g_strdup_printf("Contact: %s\r\n"
1146 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1147 "Event: registration\r\n"
1148 "Allow-Events: presence\r\n"
1149 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1150 "%s", contact
, expires
);
1154 sip
->registerstatus
= 1;
1156 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1157 process_register_response
);
1164 static void do_register_cb(struct sipe_account_data
*sip
,
1165 SIPE_UNUSED_PARAMETER
void *unused
)
1167 do_register_exp(sip
, -1);
1168 sip
->reregister_set
= FALSE
;
1171 static void do_register(struct sipe_account_data
*sip
)
1173 do_register_exp(sip
, -1);
1177 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1179 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1180 send_soap_request(sip
, body
);
1185 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1188 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1190 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1193 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1197 void sipe_auth_user_cb(void * data
)
1199 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1202 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1207 void sipe_deny_user_cb(void * data
)
1209 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1212 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1217 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1219 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1220 sipe_contact_allow_deny(sip
, name
, TRUE
);
1224 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1226 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1227 sipe_contact_allow_deny(sip
, name
, FALSE
);
1231 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1233 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1234 sipe_contact_set_acl(sip, name, "");
1238 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1242 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1243 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1245 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1247 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1248 if (!watchers
) return;
1250 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1251 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1252 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1253 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1255 // TODO pull out optional displayName to pass as alias
1257 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1258 job
->who
= remote_user
;
1260 purple_account_request_authorization(
1274 xmlnode_free(watchers
);
1279 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1281 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1282 if (!purple_group
) {
1283 purple_group
= purple_group_new(group
->name
);
1284 purple_blist_add_group(purple_group
, NULL
);
1288 group
->purple_group
= purple_group
;
1289 sip
->groups
= g_slist_append(sip
->groups
, group
);
1290 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1292 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1296 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1298 struct sipe_group
*group
;
1304 entry
= sip
->groups
;
1306 group
= entry
->data
;
1307 if (group
->id
== id
) {
1310 entry
= entry
->next
;
1315 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, const gchar
* name
)
1317 struct sipe_group
*group
;
1319 if (!sip
|| !name
) {
1323 entry
= sip
->groups
;
1325 group
= entry
->data
;
1326 if (sipe_strequal(group
->name
, name
)) {
1329 entry
= entry
->next
;
1335 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1338 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1339 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1340 send_soap_request(sip
, body
);
1342 g_free(group
->name
);
1343 group
->name
= g_strdup(name
);
1347 * Only appends if no such value already stored.
1350 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1351 GSList
* res
= list
;
1352 if (!g_slist_find_custom(list
, data
, func
)) {
1353 res
= g_slist_insert_sorted(list
, data
, func
);
1359 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1360 return group1
->id
- group2
->id
;
1364 * Returns string like "2 4 7 8" - group ids buddy belong to.
1367 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1370 //creating array from GList, converting int to gchar*
1371 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1372 GSList
*entry
= buddy
->groups
;
1374 if (!ids_arr
) return NULL
;
1377 struct sipe_group
* group
= entry
->data
;
1378 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1379 entry
= entry
->next
;
1383 res
= g_strjoinv(" ", ids_arr
);
1384 g_strfreev(ids_arr
);
1389 * Sends buddy update to server
1392 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1394 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1395 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1397 if (buddy
&& purple_buddy
) {
1398 const char *alias
= purple_buddy_get_alias(purple_buddy
);
1399 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1402 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1404 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1405 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1407 send_soap_request(sip
, body
);
1414 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1416 if (msg
->response
== 200) {
1417 struct sipe_group
*group
;
1418 struct group_user_context
*ctx
= trans
->payload
->data
;
1422 struct sipe_buddy
*buddy
;
1424 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1429 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1435 group_id
= xmlnode_get_data(node
);
1441 group
= g_new0(struct sipe_group
, 1);
1442 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1444 group
->name
= g_strdup(ctx
->group_name
);
1446 sipe_group_add(sip
, group
);
1448 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1450 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1453 sipe_group_set_user(sip
, ctx
->user_name
);
1461 static void sipe_group_context_destroy(gpointer data
)
1463 struct group_user_context
*ctx
= data
;
1464 g_free(ctx
->group_name
);
1465 g_free(ctx
->user_name
);
1469 static void sipe_group_create (struct sipe_account_data
*sip
, const gchar
*name
, const gchar
* who
)
1471 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
1472 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
1474 ctx
->group_name
= g_strdup(name
);
1475 ctx
->user_name
= g_strdup(who
);
1476 payload
->destroy
= sipe_group_context_destroy
;
1477 payload
->data
= ctx
;
1479 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1480 send_soap_request_with_cb(sip
, NULL
, body
, process_add_group_response
, payload
);
1485 * Data structure for scheduled actions
1488 struct scheduled_action
{
1491 * Format is <Event>[<Data>...]
1492 * Example: <presence><sip:user@domain.com> or <registration>
1495 guint timeout_handler
;
1496 gboolean repetitive
;
1498 GDestroyNotify destroy
;
1499 struct sipe_account_data
*sip
;
1505 * Should return FALSE if repetitive action is not needed
1507 static gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1510 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1511 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1512 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1513 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1514 ret
= sched_action
->repetitive
;
1515 if (sched_action
->destroy
) {
1516 (*sched_action
->destroy
)(sched_action
->payload
);
1518 g_free(sched_action
->name
);
1519 g_free(sched_action
);
1524 * Kills action timer effectively cancelling
1527 * @param name of action
1529 static void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, const gchar
*name
)
1533 if (!sip
->timeouts
|| !name
) return;
1535 entry
= sip
->timeouts
;
1537 struct scheduled_action
*sched_action
= entry
->data
;
1538 if(sipe_strequal(sched_action
->name
, name
)) {
1539 GSList
*to_delete
= entry
;
1540 entry
= entry
->next
;
1541 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1542 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1543 purple_timeout_remove(sched_action
->timeout_handler
);
1544 if (sched_action
->destroy
) {
1545 (*sched_action
->destroy
)(sched_action
->payload
);
1547 g_free(sched_action
->name
);
1548 g_free(sched_action
);
1550 entry
= entry
->next
;
1556 sipe_schedule_action0(const gchar
*name
,
1560 GDestroyNotify destroy
,
1561 struct sipe_account_data
*sip
,
1564 struct scheduled_action
*sched_action
;
1566 /* Make sure each action only exists once */
1567 sipe_cancel_scheduled_action(sip
, name
);
1569 purple_debug_info("sipe","scheduling action %s timeout:%d(%s)\n", name
, timeout
, isSeconds
? "sec" : "msec");
1570 sched_action
= g_new0(struct scheduled_action
, 1);
1571 sched_action
->repetitive
= FALSE
;
1572 sched_action
->name
= g_strdup(name
);
1573 sched_action
->action
= action
;
1574 sched_action
->destroy
= destroy
;
1575 sched_action
->sip
= sip
;
1576 sched_action
->payload
= payload
;
1577 sched_action
->timeout_handler
= isSeconds
? purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
) :
1578 purple_timeout_add(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1579 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1580 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1584 sipe_schedule_action(const gchar
*name
,
1587 GDestroyNotify destroy
,
1588 struct sipe_account_data
*sip
,
1591 sipe_schedule_action0(name
, timeout
, TRUE
, action
, destroy
, sip
, payload
);
1595 * Same as sipe_schedule_action() but timeout is in milliseconds.
1598 sipe_schedule_action_msec(const gchar
*name
,
1601 GDestroyNotify destroy
,
1602 struct sipe_account_data
*sip
,
1605 sipe_schedule_action0(name
, timeout
, FALSE
, action
, destroy
, sip
, payload
);
1609 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1610 time_t calculate_from
);
1613 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
1616 sipe_get_status_by_availability(int avail
,
1620 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
1621 const char *status_id
,
1622 const char *message
,
1623 time_t do_not_publish
[]);
1626 sipe_apply_calendar_status(struct sipe_account_data
*sip
,
1627 struct sipe_buddy
*sbuddy
,
1628 const char *status_id
)
1630 time_t cal_avail_since
;
1631 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
1635 if (!sbuddy
) return;
1637 if (cal_status
< SIPE_CAL_NO_DATA
) {
1638 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_status : %d for %s\n", cal_status
, sbuddy
->name
);
1639 purple_debug_info("sipe", "sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
1642 /* scheduled Cal update call */
1644 status_id
= sbuddy
->last_non_cal_status_id
;
1645 g_free(sbuddy
->activity
);
1646 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
1650 purple_debug_info("sipe", "sipe_apply_calendar_status: status_id is NULL for %s, exiting.\n",
1651 sbuddy
->name
? sbuddy
->name
: "" );
1655 /* adjust to calendar status */
1656 if (cal_status
!= SIPE_CAL_NO_DATA
) {
1657 purple_debug_info("sipe", "sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
1659 if (cal_status
== SIPE_CAL_BUSY
1660 && cal_avail_since
> sbuddy
->user_avail_since
1661 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
1663 status_id
= SIPE_STATUS_ID_BUSY
;
1664 g_free(sbuddy
->activity
);
1665 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
1667 avail
= sipe_get_availability_by_status(status_id
, NULL
);
1669 purple_debug_info("sipe", "sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
1670 if (cal_avail_since
> sbuddy
->activity_since
) {
1671 if (cal_status
== SIPE_CAL_OOF
1672 && avail
>= 15000) /* 12000 in 2007 */
1674 g_free(sbuddy
->activity
);
1675 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
1680 /* then set status_id actually */
1681 purple_debug_info("sipe", "sipe_apply_calendar_status: to %s for %s\n", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
1682 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, status_id
, NULL
);
1684 /* set our account state to the one in roaming (including calendar info) */
1685 self_uri
= sip_uri_self(sip
);
1686 if (sip
->initial_state_published
&& sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
1687 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
1688 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
1691 purple_debug_info("sipe", "sipe_apply_calendar_status: switch to '%s' for the account\n", sip
->status
);
1692 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
1698 sipe_got_user_status(struct sipe_account_data
*sip
,
1700 const char *status_id
)
1702 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
1704 if (!sbuddy
) return;
1706 /* Check if on 2005 system contact's calendar,
1707 * then set/preserve it.
1709 if (!sip
->ocs2007
) {
1710 sipe_apply_calendar_status(sip
, sbuddy
, status_id
);
1712 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
1717 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
1718 struct sipe_buddy
*sbuddy
,
1719 struct sipe_account_data
*sip
)
1721 sipe_apply_calendar_status(sip
, sbuddy
, NULL
);
1725 * Updates contact's status
1726 * based on their calendar information.
1728 * Applicability: 2005 systems
1731 update_calendar_status(struct sipe_account_data
*sip
)
1733 purple_debug_info("sipe", "update_calendar_status() started.\n");
1734 g_hash_table_foreach(sip
->buddies
, (GHFunc
)update_calendar_status_cb
, (gpointer
)sip
);
1736 /* repeat scheduling */
1737 sipe_sched_calendar_status_update(sip
, time(NULL
) + 3*60 /* 3 min */);
1741 * Schedules process of contacts' status update
1742 * based on their calendar information.
1743 * Should be scheduled to the beginning of every
1744 * 15 min interval, like:
1745 * 13:00, 13:15, 13:30, 13:45, etc.
1747 * Applicability: 2005 systems
1750 sipe_sched_calendar_status_update(struct sipe_account_data
*sip
,
1751 time_t calculate_from
)
1753 int interval
= 15*60;
1754 /** start of the beginning of closest 15 min interval. */
1755 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1757 purple_debug_info("sipe", "sipe_sched_calendar_status_update: calculate_from time: %s",
1758 asctime(localtime(&calculate_from
)));
1759 purple_debug_info("sipe", "sipe_sched_calendar_status_update: next start time : %s",
1760 asctime(localtime(&next_start
)));
1762 sipe_schedule_action("<+2005-cal-status>",
1763 (int)(next_start
- time(NULL
)),
1764 (Action
)update_calendar_status
,
1771 * Schedules process of self status publish
1772 * based on own calendar information.
1773 * Should be scheduled to the beginning of every
1774 * 15 min interval, like:
1775 * 13:00, 13:15, 13:30, 13:45, etc.
1777 * Applicability: 2007+ systems
1780 sipe_sched_calendar_status_self_publish(struct sipe_account_data
*sip
,
1781 time_t calculate_from
)
1783 int interval
= 5*60;
1784 /** start of the beginning of closest 5 min interval. */
1785 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
1787 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: calculate_from time: %s",
1788 asctime(localtime(&calculate_from
)));
1789 purple_debug_info("sipe", "sipe_sched_calendar_status_self_publish: next start time : %s",
1790 asctime(localtime(&next_start
)));
1792 sipe_schedule_action("<+2007-cal-status>",
1793 (int)(next_start
- time(NULL
)),
1794 (Action
)publish_calendar_status_self
,
1800 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1802 /** Should be g_free()'d
1805 sipe_get_subscription_key(const gchar
*event
,
1810 if (is_empty(event
)) return NULL
;
1812 if (event
&& sipe_strcase_equal(event
, "presence")) {
1813 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1814 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, with
);
1816 /* @TODO drop participated buddies' just_added flag */
1818 /* Subscription is identified by <event> key */
1819 key
= g_strdup_printf("<%s>", event
);
1825 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
1826 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1828 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
1829 const gchar
*event
= sipmsg_find_header(msg
, "Event");
1832 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
1834 struct sipmsg
*request_msg
= trans
->msg
;
1835 event
= sipmsg_find_header(request_msg
, "Event");
1838 key
= sipe_get_subscription_key(event
, with
);
1840 /* 200 OK; 481 Call Leg Does Not Exist */
1841 if (key
&& (msg
->response
== 200 || msg
->response
== 481)) {
1842 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
1843 g_hash_table_remove(sip
->subscriptions
, key
);
1844 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
1848 /* create/store subscription dialog if not yet */
1849 if (msg
->response
== 200) {
1850 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1851 gchar
*cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
1854 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
1855 g_hash_table_insert(sip
->subscriptions
, g_strdup(key
), subscription
);
1857 subscription
->dialog
.callid
= g_strdup(callid
);
1858 subscription
->dialog
.cseq
= atoi(cseq
);
1859 subscription
->dialog
.with
= g_strdup(with
);
1860 subscription
->event
= g_strdup(event
);
1861 sipe_dialog_parse(&subscription
->dialog
, msg
, TRUE
);
1863 purple_debug_info("sipe", "process_subscribe_response: subscription dialog added for: %s\n", key
);
1872 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1874 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1879 static void sipe_subscribe_resource_uri(const char *name
,
1880 SIPE_UNUSED_PARAMETER gpointer value
,
1881 gchar
**resources_uri
)
1883 gchar
*tmp
= *resources_uri
;
1884 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1888 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1890 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
1891 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
1892 gchar
*tmp
= *resources_uri
;
1894 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
1896 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
1901 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1902 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1903 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1904 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1905 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1908 static void sipe_subscribe_presence_batched_to(struct sipe_account_data
*sip
, gchar
*resources_uri
, gchar
*to
)
1911 gchar
*contact
= get_contact(sip
);
1914 gchar
*require
= "";
1916 gchar
*autoextend
= "";
1917 gchar
*content_type
;
1918 struct sip_dialog
*dialog
;
1921 require
= ", categoryList";
1922 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1923 content_type
= "application/msrtc-adrl-categorylist+xml";
1924 content
= g_strdup_printf(
1925 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1926 "<action name=\"subscribe\" id=\"63792024\">\n"
1927 "<adhocList>\n%s</adhocList>\n"
1928 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1929 "<category name=\"calendarData\"/>\n"
1930 "<category name=\"contactCard\"/>\n"
1931 "<category name=\"note\"/>\n"
1932 "<category name=\"state\"/>\n"
1935 "</batchSub>", sip
->username
, resources_uri
);
1937 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1938 content_type
= "application/adrl+xml";
1939 content
= g_strdup_printf(
1940 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1941 "<create xmlns=\"\">\n%s</create>\n"
1942 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1944 g_free(resources_uri
);
1946 request
= g_strdup_printf(
1947 "Require: adhoclist%s\r\n"
1948 "Supported: eventlist\r\n"
1949 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1950 "Supported: ms-piggyback-first-notify\r\n"
1951 "%sSupported: ms-benotify\r\n"
1952 "Proxy-Require: ms-benotify\r\n"
1953 "Event: presence\r\n"
1954 "Content-Type: %s\r\n"
1955 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1958 /* subscribe to buddy presence */
1959 /* Subscription is identified by ACTION_NAME_PRESENCE key */
1960 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
1961 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
1962 purple_debug_info("sipe", "sipe_subscribe_presence_batched_to: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
1964 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
1972 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
,
1973 SIPE_UNUSED_PARAMETER
void *unused
)
1975 gchar
*to
= sip_uri_self(sip
);
1976 gchar
*resources_uri
= g_strdup("");
1978 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1980 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1983 sipe_subscribe_presence_batched_to(sip
, resources_uri
, to
);
1986 struct presence_batched_routed
{
1991 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
1993 struct presence_batched_routed
*data
= payload
;
1994 GSList
*buddies
= data
->buddies
;
1996 g_free(buddies
->data
);
1997 buddies
= buddies
->next
;
1999 g_slist_free(data
->buddies
);
2004 static void sipe_subscribe_presence_batched_routed(struct sipe_account_data
*sip
, void *payload
)
2006 struct presence_batched_routed
*data
= payload
;
2007 GSList
*buddies
= data
->buddies
;
2008 gchar
*resources_uri
= g_strdup("");
2010 gchar
*tmp
= resources_uri
;
2011 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
2013 buddies
= buddies
->next
;
2015 sipe_subscribe_presence_batched_to(sip
, resources_uri
,
2016 g_strdup(data
->host
));
2020 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
2021 * The user sends a single SUBSCRIBE request to the subscribed contact.
2022 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
2026 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, void *buddy_name
)
2030 gchar
*to
= sip_uri((char *)buddy_name
);
2031 gchar
*tmp
= get_contact(sip
);
2033 gchar
*content
= NULL
;
2034 gchar
*autoextend
= "";
2035 gchar
*content_type
= "";
2036 struct sip_dialog
*dialog
;
2037 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, to
);
2038 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
2040 if (sbuddy
) sbuddy
->just_added
= FALSE
;
2043 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
2045 autoextend
= "Supported: com.microsoft.autoextend\r\n";
2048 request
= g_strdup_printf(
2049 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
2050 "Supported: ms-piggyback-first-notify\r\n"
2051 "%s%sSupported: ms-benotify\r\n"
2052 "Proxy-Require: ms-benotify\r\n"
2053 "Event: presence\r\n"
2054 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
2057 content
= g_strdup_printf(
2058 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
2059 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
2060 "<resource uri=\"%s\"%s\n"
2062 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
2063 "<category name=\"calendarData\"/>\n"
2064 "<category name=\"contactCard\"/>\n"
2065 "<category name=\"note\"/>\n"
2066 "<category name=\"state\"/>\n"
2069 "</batchSub>", sip
->username
, to
, context
);
2074 /* subscribe to buddy presence */
2075 /* Subscription is identified by ACTION_NAME_PRESENCE key */
2076 key
= g_strdup_printf(ACTION_NAME_PRESENCE
, to
);
2077 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2078 purple_debug_info("sipe", "sipe_subscribe_presence_single: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2080 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, dialog
, process_subscribe_response
);
2088 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
2090 purple_debug_info("sipe", "sipe_set_status: status=%s\n", purple_status_get_id(status
));
2092 if (!purple_status_is_active(status
))
2096 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
2101 time_t now
= time(NULL
);
2102 const char *status_id
= purple_status_get_id(status
);
2103 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
2104 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2105 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
2107 /* when other point of presence clears note, but we are keeping
2108 * state if OOF note.
2110 if (do_not_publish
&& !note
&& sip
->ews
&& sip
->ews
->oof_note
) {
2111 purple_debug_info("sipe", "sipe_set_status: enabling publication as OOF note keepers.\n");
2112 do_not_publish
= FALSE
;
2115 purple_debug_info("sipe", "sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d\n",
2116 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
2118 sip
->do_not_publish
[activity
] = 0;
2119 purple_debug_info("sipe", "sipe_set_status: set: sip->do_not_publish[%s]=%d [0]\n",
2120 status_id
, (int)sip
->do_not_publish
[activity
]);
2124 purple_debug_info("sipe", "sipe_set_status: publication was switched off, exiting.\n");
2128 g_free(sip
->status
);
2129 sip
->status
= g_strdup(status_id
);
2131 /* hack to escape apostrof before comparison */
2132 tmp
= note
? purple_strreplace(note
, "'", "'") : NULL
;
2134 /* this will preserve OOF flag as well */
2135 if (!sipe_strequal(tmp
, sip
->note
)) {
2136 sip
->is_oof_note
= FALSE
;
2138 sip
->note
= g_strdup(note
);
2139 sip
->note_since
= time(NULL
);
2143 /* schedule 2 sec to capture idle flag */
2144 action_name
= g_strdup_printf("<%s>", "+set-status");
2145 sipe_schedule_action(action_name
, SIPE_IDLE_SET_DELAY
, (Action
)send_presence_status
, NULL
, sip
, NULL
);
2146 g_free(action_name
);
2151 sipe_set_idle(PurpleConnection
* gc
,
2154 purple_debug_info("sipe", "sipe_set_idle: interval=%d\n", interval
);
2157 struct sipe_account_data
*sip
= gc
->proto_data
;
2160 sip
->idle_switch
= time(NULL
);
2161 purple_debug_info("sipe", "sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
2167 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
,
2168 SIPE_UNUSED_PARAMETER
const char *alias
)
2170 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2171 sipe_group_set_user(sip
, name
);
2175 sipe_group_buddy(PurpleConnection
*gc
,
2177 const char *old_group_name
,
2178 const char *new_group_name
)
2180 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2181 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
2182 struct sipe_group
* old_group
= NULL
;
2183 struct sipe_group
* new_group
;
2185 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
2186 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
2188 if(!buddy
) { // buddy not in roaming list
2192 if (old_group_name
) {
2193 old_group
= sipe_group_find_by_name(sip
, old_group_name
);
2195 new_group
= sipe_group_find_by_name(sip
, new_group_name
);
2198 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
2199 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
2203 sipe_group_create(sip
, new_group_name
, who
);
2205 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
2206 sipe_group_set_user(sip
, who
);
2210 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2212 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2214 /* libpurple can call us with undefined buddy or group */
2215 if (buddy
&& group
) {
2216 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2218 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2219 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
2220 purple_blist_rename_buddy(buddy
, buddy_name
);
2223 /* Prepend sip: if needed */
2224 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
2225 gchar
*buf
= sip_uri_from_name(buddy
->name
);
2226 purple_blist_rename_buddy(buddy
, buf
);
2230 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
2231 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
2232 purple_debug_info("sipe", "sipe_add_buddy: adding %s\n", buddy
->name
);
2233 b
->name
= g_strdup(buddy
->name
);
2234 b
->just_added
= TRUE
;
2235 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
2236 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
2237 /* @TODO should go to callback */
2238 sipe_subscribe_presence_single(sip
, b
->name
);
2240 purple_debug_info("sipe", "sipe_add_buddy: buddy %s already in internal list\n", buddy
->name
);
2245 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
2249 * We are calling g_hash_table_foreach_steal(). That means that no
2250 * key/value deallocation functions are called. Therefore the glib
2251 * hash code does not touch the key (buddy->name) or value (buddy)
2252 * of the to-be-deleted hash node at all. It follows that we
2254 * - MUST free the memory for the key ourselves and
2255 * - ARE allowed to do it in this function
2257 * Conclusion: glib must be broken on the Windows platform if sipe
2258 * crashes with SIGTRAP when closing. You'll have to live
2259 * with the memory leak until this is fixed.
2261 g_free(buddy
->name
);
2263 g_free(buddy
->activity
);
2264 g_free(buddy
->meeting_subject
);
2265 g_free(buddy
->meeting_location
);
2266 g_free(buddy
->note
);
2268 g_free(buddy
->cal_start_time
);
2269 g_free(buddy
->cal_free_busy_base64
);
2270 g_free(buddy
->cal_free_busy
);
2271 g_free(buddy
->last_non_cal_activity
);
2273 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
2275 g_free(buddy
->device_name
);
2276 g_slist_free(buddy
->groups
);
2281 * Unassociates buddy from group first.
2282 * Then see if no groups left, removes buddy completely.
2283 * Otherwise updates buddy groups on server.
2285 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
2287 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2288 struct sipe_buddy
*b
;
2289 struct sipe_group
*g
= NULL
;
2291 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
2294 b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
2298 g
= sipe_group_find_by_name(sip
, group
->name
);
2302 b
->groups
= g_slist_remove(b
->groups
, g
);
2303 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
2306 if (g_slist_length(b
->groups
) < 1) {
2307 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy
->name
);
2308 sipe_cancel_scheduled_action(sip
, action_name
);
2309 g_free(action_name
);
2311 g_hash_table_remove(sip
->buddies
, buddy
->name
);
2314 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
2315 send_soap_request(sip
, body
);
2321 //updates groups on server
2322 sipe_group_set_user(sip
, b
->name
);
2328 sipe_rename_group(PurpleConnection
*gc
,
2329 const char *old_name
,
2331 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
2333 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2334 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, old_name
);
2336 sipe_group_rename(sip
, s_group
, group
->name
);
2338 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
2343 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
2345 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2346 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
2349 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
2350 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
2351 send_soap_request(sip
, body
);
2354 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
2355 g_free(s_group
->name
);
2358 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
2362 /** All statuses need message attribute to pass Note */
2363 static GList
*sipe_status_types(SIPE_UNUSED_PARAMETER PurpleAccount
*acc
)
2365 PurpleStatusType
*type
;
2366 GList
*types
= NULL
;
2368 /* Macros to reduce code repetition.
2369 Translators: noun */
2370 #define SIPE_ADD_STATUS(prim,id,name,user) type = purple_status_type_new_with_attrs( \
2372 TRUE, user, FALSE, \
2373 SIPE_STATUS_ATTR_ID_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), \
2375 types = g_list_append(types, type);
2378 SIPE_ADD_STATUS(PURPLE_STATUS_AVAILABLE
,
2384 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2385 sipe_activity_map
[SIPE_ACTIVITY_BUSY
].status_id
,
2386 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSY
),
2389 /* Do Not Disturb */
2390 SIPE_ADD_STATUS(PURPLE_STATUS_UNAVAILABLE
,
2391 sipe_activity_map
[SIPE_ACTIVITY_DND
].status_id
,
2396 /* Goes first in the list as
2397 * purple picks the first status with the AWAY type
2400 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2406 SIPE_ADD_STATUS(PURPLE_STATUS_AWAY
,
2407 sipe_activity_map
[SIPE_ACTIVITY_BRB
].status_id
,
2408 SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BRB
),
2411 /* Appear Offline */
2412 SIPE_ADD_STATUS(PURPLE_STATUS_INVISIBLE
,
2418 type
= purple_status_type_new(PURPLE_STATUS_OFFLINE
,
2422 types
= g_list_append(types
, type
);
2428 * A callback for g_hash_table_foreach
2431 sipe_buddy_subscribe_cb(char *buddy_name
,
2432 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
2433 struct sipe_account_data
*sip
)
2435 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, buddy_name
);
2436 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
2437 guint time_range
= (g_hash_table_size(sip
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
2438 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
2440 sipe_schedule_action_msec(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(buddy_name
));
2441 g_free(action_name
);
2445 * Removes entries from purple buddy list
2446 * that does not correspond ones in the roaming contact list.
2448 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
2449 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
2450 GSList
*entry
= buddies
;
2451 struct sipe_buddy
*buddy
;
2455 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
2456 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
2459 g
= purple_buddy_get_group(b
);
2460 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
2462 gboolean in_sipe_groups
= FALSE
;
2463 GSList
*entry2
= buddy
->groups
;
2465 struct sipe_group
*group
= entry2
->data
;
2466 if (sipe_strequal(group
->name
, g
->name
)) {
2467 in_sipe_groups
= TRUE
;
2470 entry2
= entry2
->next
;
2472 if(!in_sipe_groups
) {
2473 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
2474 purple_blist_remove_buddy(b
);
2477 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
2478 purple_blist_remove_buddy(b
);
2480 entry
= entry
->next
;
2482 g_slist_free(buddies
);
2485 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2487 int len
= msg
->bodylen
;
2489 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
2492 const gchar
*contacts_delta
;
2493 xmlnode
*group_node
;
2494 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
2498 /* Convert the contact from XML to Purple Buddies */
2499 isc
= xmlnode_from_str(msg
->body
, len
);
2504 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
2505 if (contacts_delta
) {
2506 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2509 if (sipe_strequal(isc
->name
, "contactList")) {
2512 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
2513 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2514 const char *name
= xmlnode_get_attrib(group_node
, "name");
2516 if (g_str_has_prefix(name
, "~")) {
2517 name
= _("Other Contacts");
2519 group
->name
= g_strdup(name
);
2520 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
2522 sipe_group_add(sip
, group
);
2525 // Make sure we have at least one group
2526 if (g_slist_length(sip
->groups
) == 0) {
2527 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
2528 PurpleGroup
*purple_group
;
2529 group
->name
= g_strdup(_("Other Contacts"));
2531 purple_group
= purple_group_new(group
->name
);
2532 purple_blist_add_group(purple_group
, NULL
);
2533 sip
->groups
= g_slist_append(sip
->groups
, group
);
2536 /* Parse contacts */
2537 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
2538 const gchar
*uri
= xmlnode_get_attrib(item
, "uri");
2539 const gchar
*name
= xmlnode_get_attrib(item
, "name");
2541 struct sipe_buddy
*buddy
= NULL
;
2543 gchar
**item_groups
;
2546 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
2547 tmp
= sip_uri_from_name(uri
);
2548 buddy_name
= g_ascii_strdown(tmp
, -1);
2551 /* assign to group Other Contacts if nothing else received */
2552 tmp
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2554 struct sipe_group
*group
= sipe_group_find_by_name(sip
, _("Other Contacts"));
2556 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
2558 item_groups
= g_strsplit(tmp
, " ", 0);
2561 while (item_groups
[i
]) {
2562 struct sipe_group
*group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2564 // If couldn't find the right group for this contact, just put them in the first group we have
2565 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2566 group
= sip
->groups
->data
;
2569 if (group
!= NULL
) {
2570 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2572 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2573 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2575 purple_debug_info("sipe", "Created new buddy %s with alias %s\n", buddy_name
, uri
);
2578 if (sipe_strcase_equal(uri
, purple_buddy_get_alias(b
))) {
2579 if (name
!= NULL
&& strlen(name
) != 0) {
2580 purple_blist_alias_buddy(b
, name
);
2582 purple_debug_info("sipe", "Replaced buddy %s alias with %s\n", buddy_name
, name
);
2587 buddy
= g_new0(struct sipe_buddy
, 1);
2588 buddy
->name
= g_strdup(b
->name
);
2589 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2592 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2594 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2596 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2601 } // while, contact groups
2602 g_strfreev(item_groups
);
2607 sipe_cleanup_local_blist(sip
);
2609 /* Add self-contact if not there yet. 2005 systems. */
2610 /* This will resemble subscription to roaming_self in 2007 systems */
2611 if (!sip
->ocs2007
) {
2612 gchar
*self_uri
= sip_uri_self(sip
);
2613 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, self_uri
);
2616 buddy
= g_new0(struct sipe_buddy
, 1);
2617 buddy
->name
= g_strdup(self_uri
);
2618 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2625 /* subscribe to buddies */
2626 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2627 if (sip
->batched_support
) {
2628 sipe_subscribe_presence_batched(sip
, NULL
);
2630 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_subscribe_cb
, (gpointer
)sip
);
2632 sip
->subscribed_buddies
= TRUE
;
2634 /* for 2005 systems schedule contacts' status update
2635 * based on their calendar information
2637 if (!sip
->ocs2007
) {
2638 sipe_sched_calendar_status_update(sip
, time(NULL
));
2645 * Subscribe roaming contacts
2647 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
)
2649 gchar
*to
= sip_uri_self(sip
);
2650 gchar
*tmp
= get_contact(sip
);
2651 gchar
*hdr
= g_strdup_printf(
2652 "Event: vnd-microsoft-roaming-contacts\r\n"
2653 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2654 "Supported: com.microsoft.autoextend\r\n"
2655 "Supported: ms-benotify\r\n"
2656 "Proxy-Require: ms-benotify\r\n"
2657 "Supported: ms-piggyback-first-notify\r\n"
2658 "Contact: %s\r\n", tmp
);
2661 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2666 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
,
2667 SIPE_UNUSED_PARAMETER
void *unused
)
2670 struct sip_dialog
*dialog
;
2671 gchar
*to
= sip_uri_self(sip
);
2672 gchar
*tmp
= get_contact(sip
);
2673 gchar
*hdr
= g_strdup_printf(
2674 "Event: presence.wpending\r\n"
2675 "Accept: text/xml+msrtc.wpending\r\n"
2676 "Supported: com.microsoft.autoextend\r\n"
2677 "Supported: ms-benotify\r\n"
2678 "Proxy-Require: ms-benotify\r\n"
2679 "Supported: ms-piggyback-first-notify\r\n"
2680 "Contact: %s\r\n", tmp
);
2683 /* Subscription is identified by <event> key */
2684 key
= g_strdup_printf("<%s>", "presence.wpending");
2685 dialog
= (struct sip_dialog
*)g_hash_table_lookup(sip
->subscriptions
, key
);
2686 purple_debug_info("sipe", "sipe_subscribe_presence_wpending: subscription dialog for: %s is %s\n", key
, dialog
? "Not NULL" : "NULL");
2688 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", dialog
, process_subscribe_response
);
2696 * Fires on deregistration event initiated by server.
2697 * [MS-SIPREGE] SIP extension.
2702 // Content-Type: text/registration-event
2703 // subscription-state: terminated;expires=0
2704 // ms-diagnostics-public: 4141;reason="User disabled"
2706 // deregistered;event=rejected
2708 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2710 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2711 gchar
*event
= NULL
;
2712 gchar
*reason
= NULL
;
2713 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
2716 diagnostics
= diagnostics
? diagnostics
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2717 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2719 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2720 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2721 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2722 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2724 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2728 if (diagnostics
!= NULL
) {
2729 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
2730 } else { // for LCS2005
2732 if (event
&& sipe_strcase_equal(event
, "unregistered")) {
2733 error_id
= 4140; // [MS-SIPREGE]
2734 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2735 reason
= g_strdup(_("you are already signed in at another location"));
2736 } else if (event
&& sipe_strcase_equal(event
, "rejected")) {
2738 reason
= g_strdup(_("user disabled")); // [MS-OCER]
2739 } else if (event
&& sipe_strcase_equal(event
, "deactivated")) {
2741 reason
= g_strdup(_("user moved")); // [MS-OCER]
2745 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
2748 sip
->gc
->wants_to_die
= TRUE
;
2749 purple_connection_error(sip
->gc
, warning
);
2754 static void sipe_process_provisioning_v2(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2756 xmlnode
*xn_provision_group_list
;
2759 xn_provision_group_list
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2761 /* provisionGroup */
2762 for (node
= xmlnode_get_child(xn_provision_group_list
, "provisionGroup"); node
; node
= xmlnode_get_next_twin(node
)) {
2763 if (sipe_strequal("ServerConfiguration", xmlnode_get_attrib(node
, "name"))) {
2764 g_free(sip
->focus_factory_uri
);
2765 sip
->focus_factory_uri
= xmlnode_get_data(xmlnode_get_child(node
, "focusFactoryUri"));
2766 purple_debug_info("sipe", "sipe_process_provisioning_v2: sip->focus_factory_uri=%s\n",
2767 sip
->focus_factory_uri
? sip
->focus_factory_uri
: "");
2771 xmlnode_free(xn_provision_group_list
);
2774 /** for 2005 system */
2776 sipe_process_provisioning(struct sipe_account_data
*sip
,
2779 xmlnode
*xn_provision
;
2782 xn_provision
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2783 if ((node
= xmlnode_get_child(xn_provision
, "user"))) {
2784 purple_debug_info("sipe", "sipe_process_provisioning: uri=%s\n", xmlnode_get_attrib(node
, "uri"));
2785 if ((node
= xmlnode_get_child(node
, "line"))) {
2786 const gchar
*line_uri
= xmlnode_get_attrib(node
, "uri");
2787 const gchar
*server
= xmlnode_get_attrib(node
, "server");
2788 purple_debug_info("sipe", "sipe_process_provisioning: line_uri=%s server=%s\n", line_uri
, server
);
2789 sip_csta_open(sip
, line_uri
, server
);
2792 xmlnode_free(xn_provision
);
2795 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2797 const gchar
*contacts_delta
;
2800 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2806 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2809 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2816 free_container(struct sipe_container
*container
)
2820 if (!container
) return;
2822 entry
= container
->members
;
2824 void *data
= entry
->data
;
2825 entry
= g_slist_remove(entry
, data
);
2832 * Finds locally stored MS-PRES container member
2834 static struct sipe_container_member
*
2835 sipe_find_container_member(struct sipe_container
*container
,
2839 struct sipe_container_member
*member
;
2842 if (container
== NULL
|| type
== NULL
) {
2846 entry
= container
->members
;
2848 member
= entry
->data
;
2849 if (!g_strcasecmp(member
->type
, type
)
2850 && ((!member
->value
&& !value
)
2851 || (value
&& member
->value
&& !g_strcasecmp(member
->value
, value
)))
2855 entry
= entry
->next
;
2861 * Finds locally stored MS-PRES container by id
2863 static struct sipe_container
*
2864 sipe_find_container(struct sipe_account_data
*sip
,
2867 struct sipe_container
*container
;
2874 entry
= sip
->containers
;
2876 container
= entry
->data
;
2877 if (id
== container
->id
) {
2880 entry
= entry
->next
;
2894 sipe_find_access_level(struct sipe_account_data
*sip
,
2898 guint containers
[] = {32000, 400, 300, 200, 100};
2901 for (i
= 0; i
< 5; i
++) {
2902 struct sipe_container_member
*member
;
2903 struct sipe_container
*container
= sipe_find_container(sip
, containers
[i
]);
2904 if (!container
) continue;
2906 member
= sipe_find_container_member(container
, type
, value
);
2908 return containers
[i
];
2916 sipe_send_set_container_members(struct sipe_account_data
*sip
,
2918 guint container_version
,
2919 const gchar
* action
,
2923 gchar
*self
= sip_uri_self(sip
);
2924 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
2927 gchar
*body
= g_strdup_printf(
2928 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
2929 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>"
2930 "</setContainerMembers>",
2938 contact
= get_contact(sip
);
2939 hdr
= g_strdup_printf("Contact: %s\r\n"
2940 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
2943 send_sip_request(sip
->gc
, "SERVICE", self
, self
, hdr
, body
, NULL
, NULL
);
2951 free_publication(struct sipe_publication
*publication
)
2953 g_free(publication
->category
);
2954 g_free(publication
->cal_event_hash
);
2955 g_free(publication
->note
);
2957 g_free(publication
->working_hours_xml_str
);
2958 g_free(publication
->fb_start_str
);
2959 g_free(publication
->free_busy_base64
);
2961 g_free(publication
);
2964 /* key is <category><instance><container> */
2966 sipe_is_our_publication(struct sipe_account_data
*sip
,
2971 /* filling keys for our publications if not yet cached */
2972 if (!sip
->our_publication_keys
) {
2973 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
2974 guint machine_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
2975 guint user_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
);
2976 guint calendar_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
2977 guint cal_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
);
2978 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
2979 guint note_oof_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
);
2981 purple_debug_info("sipe", "* Our Publication Instances *\n");
2982 purple_debug_info("sipe", "\tDevice : %u\t0x%08X\n", device_instance
, device_instance
);
2983 purple_debug_info("sipe", "\tMachine State : %u\t0x%08X\n", machine_instance
, machine_instance
);
2984 purple_debug_info("sipe", "\tUser Stare : %u\t0x%08X\n", user_instance
, user_instance
);
2985 purple_debug_info("sipe", "\tCalendar State : %u\t0x%08X\n", calendar_instance
, calendar_instance
);
2986 purple_debug_info("sipe", "\tCalendar OOF State : %u\t0x%08X\n", cal_oof_instance
, cal_oof_instance
);
2987 purple_debug_info("sipe", "\tCalendar FreeBusy : %u\t0x%08X\n", cal_data_instance
, cal_data_instance
);
2988 purple_debug_info("sipe", "\tOOF Note : %u\t0x%08X\n", note_oof_instance
, note_oof_instance
);
2989 purple_debug_info("sipe", "\tNote : %u\n", 0);
2990 purple_debug_info("sipe", "\tCalendar WorkingHours: %u\n", 0);
2993 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2994 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2996 /* state:machineState */
2997 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2998 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2999 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3000 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
3002 /* state:userState */
3003 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3004 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
3005 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3006 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
3008 /* state:calendarState */
3009 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3010 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
3011 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3012 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
3014 /* state:calendarState OOF */
3015 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3016 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
3017 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3018 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
3021 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3022 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
3023 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3024 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
3025 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3026 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
3029 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3030 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
3031 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3032 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
3033 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3034 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
3036 /* calendarData:WorkingHours */
3037 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3038 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
3039 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3040 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
3041 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3042 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
3043 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3044 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
3045 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3046 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
3047 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3048 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
3050 /* calendarData:FreeBusy */
3051 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3052 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
3053 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3054 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
3055 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3056 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
3057 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3058 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
3059 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3060 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
3061 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
3062 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
3064 //purple_debug_info("sipe", "sipe_is_our_publication: sip->our_publication_keys length=%d\n",
3065 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
3068 //purple_debug_info("sipe", "sipe_is_our_publication: key=%s\n", key);
3070 entry
= sip
->our_publication_keys
;
3072 //purple_debug_info("sipe", " sipe_is_our_publication: entry->data=%s\n", entry->data);
3073 if (sipe_strequal(entry
->data
, key
)) {
3076 entry
= entry
->next
;
3081 /** Property names to store in blist.xml */
3082 #define ALIAS_PROP "alias"
3083 #define EMAIL_PROP "email"
3084 #define PHONE_PROP "phone"
3085 #define PHONE_DISPLAY_PROP "phone-display"
3086 #define PHONE_MOBILE_PROP "phone-mobile"
3087 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
3088 #define PHONE_HOME_PROP "phone-home"
3089 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
3090 #define PHONE_OTHER_PROP "phone-other"
3091 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
3092 #define PHONE_CUSTOM1_PROP "phone-custom1"
3093 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
3094 #define SITE_PROP "site"
3095 #define COMPANY_PROP "company"
3096 #define DEPARTMENT_PROP "department"
3097 #define TITLE_PROP "title"
3098 #define OFFICE_PROP "office"
3099 /** implies work address */
3100 #define ADDRESS_STREET_PROP "address-street"
3101 #define ADDRESS_CITY_PROP "address-city"
3102 #define ADDRESS_STATE_PROP "address-state"
3103 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
3104 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
3107 * Tries to figure out user first and last name
3108 * based on Display Name and email properties.
3110 * Allocates memory - must be g_free()'d
3112 * Examples to parse:
3114 * First Last - Company Name
3117 * Last, First (C)(STP) (Company)
3118 * first.last@company.com (preprocessed as "first last")
3119 * first.last.company.com@reuters.net (preprocessed as "first last company com")
3121 * Unusable examples:
3122 * user@company.com (preprocessed as "user")
3123 * first.m.last@company.com (preprocessed as "first m last")
3124 * user.company.com@reuters.net (preprocessed as "user company com")
3127 sipe_get_first_last_names(struct sipe_account_data
*sip
,
3132 PurpleBuddy
*p_buddy
;
3135 const char *first
, *last
;
3138 gboolean has_comma
= FALSE
;
3140 if (!sip
|| !uri
) return;
3142 p_buddy
= purple_find_buddy(sip
->account
, uri
);
3144 if (!p_buddy
) return;
3146 display_name
= g_strdup(purple_buddy_get_alias(p_buddy
));
3147 email
= purple_blist_node_get_string(&p_buddy
->node
, EMAIL_PROP
);
3149 if (!display_name
&& !email
) return;
3151 /* if no display name, make "first last anything_else" out of email */
3152 if (email
&& !display_name
) {
3153 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
3154 display_name
= purple_strreplace((tmp
= display_name
), ".", " ");
3159 has_comma
= (strstr(display_name
, ",") != NULL
);
3160 display_name
= purple_strreplace((tmp
= display_name
), ", ", " ");
3162 display_name
= purple_strreplace((tmp
= display_name
), ",", " ");
3166 parts
= g_strsplit(display_name
, " ", 0);
3168 if (!parts
[0] || !parts
[1]) {
3169 g_free(display_name
);
3183 *first_name
= g_strstrip(g_strdup(first
));
3187 *last_name
= g_strstrip(g_strdup(last
));
3190 g_free(display_name
);
3195 * Update user information
3197 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3198 * @param property_name
3199 * @param property_value may be modified to strip white space
3202 sipe_update_user_info(struct sipe_account_data
*sip
,
3204 const char *property_name
,
3205 char *property_value
)
3207 GSList
*buddies
, *entry
;
3209 if (!property_name
|| strlen(property_name
) == 0) return;
3212 property_value
= g_strstrip(property_value
);
3214 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
3216 const char *prop_str
;
3217 const char *server_alias
;
3218 PurpleBuddy
*p_buddy
= entry
->data
;
3220 /* for Display Name */
3221 if (sipe_strequal(property_name
, ALIAS_PROP
)) {
3222 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
3223 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, property_value
);
3224 purple_blist_alias_buddy(p_buddy
, property_value
);
3227 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3228 if (!is_empty(property_value
) &&
3229 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
3231 purple_blist_server_alias_buddy(p_buddy
, property_value
);
3234 /* for other properties */
3236 if (!is_empty(property_value
)) {
3237 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
3238 if (!prop_str
|| !sipe_strcase_equal(prop_str
, property_value
)) {
3239 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
3244 entry
= entry
->next
;
3246 g_slist_free(buddies
);
3251 * Suitable for both 2005 and 2007 systems.
3253 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
3255 * @param phone may be modified to strip white space
3256 * @param phone_display_string may be modified to strip white space
3259 sipe_update_user_phone(struct sipe_account_data
*sip
,
3261 const gchar
*phone_type
,
3263 gchar
*phone_display_string
)
3265 const char *phone_node
= PHONE_PROP
; /* work phone by default */
3266 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
3268 if(!phone
|| strlen(phone
) == 0) return;
3270 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
3271 phone_node
= PHONE_MOBILE_PROP
;
3272 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
3273 } else if (sipe_strequal(phone_type
, "home")) {
3274 phone_node
= PHONE_HOME_PROP
;
3275 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
3276 } else if (sipe_strequal(phone_type
, "other")) {
3277 phone_node
= PHONE_OTHER_PROP
;
3278 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
3279 } else if (sipe_strequal(phone_type
, "custom1")) {
3280 phone_node
= PHONE_CUSTOM1_PROP
;
3281 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
3284 sipe_update_user_info(sip
, uri
, phone_node
, phone
);
3285 if (phone_display_string
) {
3286 sipe_update_user_info(sip
, uri
, phone_display_node
, phone_display_string
);
3291 sipe_update_calendar(struct sipe_account_data
*sip
)
3293 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
3295 purple_debug_info("sipe", "sipe_update_calendar: started.\n");
3297 if (sipe_strequal(calendar
, "EXCH")) {
3298 sipe_ews_update_calendar(sip
);
3301 /* schedule repeat */
3302 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_INTERVAL
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3304 purple_debug_info("sipe", "sipe_update_calendar: finished.\n");
3308 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
3309 * by using standard Purple's means of signals and saved statuses.
3311 * Thus all UI elements get updated: Status Button with Note, docklet.
3312 * This is ablolutely important as both our status and note can come
3313 * inbound (roaming) or be updated programmatically (e.g. based on our
3317 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
3318 const char *status_id
,
3319 const char *message
,
3320 time_t do_not_publish
[])
3322 PurpleStatus
*status
= purple_account_get_active_status(account
);
3323 gboolean changed
= TRUE
;
3325 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
3326 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
3331 if (purple_savedstatus_is_idleaway()) {
3336 PurpleSavedStatus
*saved_status
;
3337 const PurpleStatusType
*acct_status_type
=
3338 purple_status_type_find_with_id(account
->status_types
, status_id
);
3339 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
3340 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
3342 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
3344 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
3347 /* If this type+message is unique then create a new transient saved status
3348 * Ref: gtkstatusbox.c
3350 if (!saved_status
) {
3352 GList
*active_accts
= purple_accounts_get_all_active();
3354 saved_status
= purple_savedstatus_new(NULL
, primitive
);
3355 purple_savedstatus_set_message(saved_status
, message
);
3357 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
3358 purple_savedstatus_set_substatus(saved_status
,
3359 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
3361 g_list_free(active_accts
);
3364 do_not_publish
[activity
] = time(NULL
);
3365 purple_debug_info("sipe", "sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]\n",
3366 status_id
, (int)do_not_publish
[activity
]);
3368 /* Set the status for each account */
3369 purple_savedstatus_activate(saved_status
);
3373 struct hash_table_delete_payload
{
3374 GHashTable
*hash_table
;
3379 sipe_remove_category_container_publications_cb(const char *name
,
3380 struct sipe_publication
*publication
,
3381 struct hash_table_delete_payload
*payload
)
3383 if (publication
->container
== payload
->container
) {
3384 g_hash_table_remove(payload
->hash_table
, name
);
3388 sipe_remove_category_container_publications(GHashTable
*our_publications
,
3389 const char *category
,
3392 struct hash_table_delete_payload payload
;
3393 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
3395 if (!payload
.hash_table
) return;
3397 payload
.container
= container
;
3398 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
3402 send_publish_category_initial(struct sipe_account_data
*sip
);
3405 * When we receive some self (BE) NOTIFY with a new subscriber
3406 * we sends a setSubscribers request to him [SIP-PRES] 4.8
3409 static void sipe_process_roaming_self(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3416 char *display_name
= NULL
;
3418 GSList
*category_names
= NULL
;
3419 int aggreg_avail
= 0;
3420 static sipe_activity aggreg_activity
= SIPE_ACTIVITY_UNSET
;
3421 gboolean do_update_status
= FALSE
;
3422 gboolean has_note_cleaned
= FALSE
;
3424 purple_debug_info("sipe", "sipe_process_roaming_self\n");
3426 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3429 contact
= get_contact(sip
);
3430 to
= sip_uri_self(sip
);
3434 /* set list of categories participating in this XML */
3435 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3436 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3437 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
3439 purple_debug_info("sipe", "sipe_process_roaming_self: category_names length=%d\n",
3440 category_names
? (int) g_slist_length(category_names
) : -1);
3441 /* drop category information */
3442 if (category_names
) {
3443 GSList
*entry
= category_names
;
3445 GHashTable
*cat_publications
;
3446 const gchar
*category
= entry
->data
;
3447 entry
= entry
->next
;
3448 purple_debug_info("sipe", "sipe_process_roaming_self: dropping category: %s\n", category
);
3449 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
3450 if (cat_publications
) {
3451 g_hash_table_remove(sip
->our_publications
, category
);
3452 purple_debug_info("sipe", " sipe_process_roaming_self: dropped category: %s\n", category
);
3456 g_slist_free(category_names
);
3457 /* filling our categories reflected in roaming data */
3458 for (node
= xmlnode_get_descendant(xml
, "categories", "category", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3460 const gchar
*name
= xmlnode_get_attrib(node
, "name");
3461 guint container
= xmlnode_get_int_attrib(node
, "container", -1);
3462 guint instance
= xmlnode_get_int_attrib(node
, "instance", -1);
3463 guint version
= xmlnode_get_int_attrib(node
, "version", 0);
3464 time_t publish_time
= (tmp
= xmlnode_get_attrib(node
, "publishTime")) ?
3465 sipe_utils_str_to_time(tmp
) : 0;
3467 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
3469 /* Ex. clear note: <category name="note"/> */
3470 if (container
== (guint
)-1) {
3473 do_update_status
= TRUE
;
3477 /* Ex. clear note: <category name="note" container="200"/> */
3478 if (instance
== (guint
)-1) {
3479 if (container
== 200) {
3482 do_update_status
= TRUE
;
3484 purple_debug_info("sipe", "sipe_process_roaming_self: removing publications for: %s/%u\n", name
, container
);
3485 sipe_remove_category_container_publications(
3486 sip
->our_publications
, name
, container
);
3490 /* key is <category><instance><container> */
3491 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
3492 purple_debug_info("sipe", "sipe_process_roaming_self: key=%s version=%d\n", key
, version
);
3494 /* capture all userState publication for later clean up if required */
3495 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
3496 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3498 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "userState")) {
3499 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3500 publication
->category
= g_strdup(name
);
3501 publication
->instance
= instance
;
3502 publication
->container
= container
;
3503 publication
->version
= version
;
3505 if (!sip
->user_state_publications
) {
3506 sip
->user_state_publications
= g_hash_table_new_full(
3507 g_str_hash
, g_str_equal
,
3508 g_free
, (GDestroyNotify
)free_publication
);
3510 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
3511 purple_debug_info("sipe", "sipe_process_roaming_self: added to user_state_publications key=%s version=%d\n",
3516 if (sipe_is_our_publication(sip
, key
)) {
3517 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
3519 publication
->category
= g_strdup(name
);
3520 publication
->instance
= instance
;
3521 publication
->container
= container
;
3522 publication
->version
= version
;
3524 /* filling publication->availability */
3525 if (sipe_strequal(name
, "state")) {
3526 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3527 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3530 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3532 publication
->availability
= atoi(avail_str
);
3536 /* for calendarState */
3537 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "calendarState")) {
3538 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3539 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
3541 event
->start_time
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "startTime"));
3543 if (sipe_strequal(xmlnode_get_attrib(xn_activity
, "token"),
3544 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
3546 event
->is_meeting
= TRUE
;
3549 event
->subject
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingSubject"));
3550 event
->location
= xmlnode_get_data(xmlnode_get_child(xn_state
, "meetingLocation"));
3552 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
3553 purple_debug_info("sipe", "sipe_process_roaming_self: hash=%s\n",
3554 publication
->cal_event_hash
);
3555 sipe_cal_event_free(event
);
3558 /* filling publication->note */
3559 if (sipe_strequal(name
, "note")) {
3560 xmlnode
*xn_body
= xmlnode_get_descendant(node
, "note", "body", NULL
);
3562 if (!has_note_cleaned
) {
3563 has_note_cleaned
= TRUE
;
3567 sip
->note_since
= publish_time
;
3569 do_update_status
= TRUE
;
3572 g_free(publication
->note
);
3573 publication
->note
= NULL
;
3577 publication
->note
= g_markup_escape_text((tmp
= xmlnode_get_data(xn_body
)), -1);
3579 if (publish_time
>= sip
->note_since
) {
3581 sip
->note
= g_strdup(publication
->note
);
3582 sip
->note_since
= publish_time
;
3583 sip
->is_oof_note
= sipe_strequal(xmlnode_get_attrib(xn_body
, "type"), "OOF");
3585 do_update_status
= TRUE
;
3590 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
3591 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
3592 xmlnode
*xn_free_busy
= xmlnode_get_descendant(node
, "calendarData", "freeBusy", NULL
);
3593 xmlnode
*xn_working_hours
= xmlnode_get_descendant(node
, "calendarData", "WorkingHours", NULL
);
3595 publication
->fb_start_str
= g_strdup(xmlnode_get_attrib(xn_free_busy
, "startTime"));
3596 publication
->free_busy_base64
= xmlnode_get_data(xn_free_busy
);
3598 if (xn_working_hours
) {
3599 publication
->working_hours_xml_str
= xmlnode_to_str(xn_working_hours
, NULL
);
3603 if (!cat_publications
) {
3604 cat_publications
= g_hash_table_new_full(
3605 g_str_hash
, g_str_equal
,
3606 g_free
, (GDestroyNotify
)free_publication
);
3607 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
3608 purple_debug_info("sipe", "sipe_process_roaming_self: added GHashTable cat=%s\n", name
);
3610 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
3611 purple_debug_info("sipe", "sipe_process_roaming_self: added key=%s version=%d\n", key
, version
);
3615 /* aggregateState (not an our publication) from 2-nd container */
3616 if (sipe_strequal(name
, "state") && container
== 2) {
3617 xmlnode
*xn_state
= xmlnode_get_child(node
, "state");
3619 if (xn_state
&& sipe_strequal(xmlnode_get_attrib(xn_state
, "type"), "aggregateState")) {
3620 xmlnode
*xn_avail
= xmlnode_get_child(xn_state
, "availability");
3621 xmlnode
*xn_activity
= xmlnode_get_child(xn_state
, "activity");
3624 gchar
*avail_str
= xmlnode_get_data(xn_avail
);
3626 aggreg_avail
= atoi(avail_str
);
3632 const char *activity_token
= xmlnode_get_attrib(xn_activity
, "token");
3634 aggreg_activity
= sipe_get_activity_by_token(activity_token
);
3637 do_update_status
= TRUE
;
3641 /* userProperties published by server from AD */
3642 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
3644 /* line, for Remote Call Control (RCC) */
3645 for (line
= xmlnode_get_descendant(node
, "userProperties", "lines", "line", NULL
); line
; line
= xmlnode_get_next_twin(line
)) {
3646 const gchar
*line_server
= xmlnode_get_attrib(line
, "lineServer");
3647 const gchar
*line_type
= xmlnode_get_attrib(line
, "lineType");
3650 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
3652 line_uri
= xmlnode_get_data(line
);
3654 purple_debug_info("sipe", "sipe_process_roaming_self: line_uri=%s server=%s\n", line_uri
, line_server
);
3655 sip_csta_open(sip
, line_uri
, line_server
);
3663 purple_debug_info("sipe", "sipe_process_roaming_self: sip->our_publications size=%d\n",
3664 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
3667 for (node
= xmlnode_get_descendant(xml
, "containers", "container", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3668 guint id
= xmlnode_get_int_attrib(node
, "id", 0);
3669 struct sipe_container
*container
= sipe_find_container(sip
, id
);
3672 sip
->containers
= g_slist_remove(sip
->containers
, container
);
3673 purple_debug_info("sipe", "sipe_process_roaming_self: removed existing container id=%d v%d\n", container
->id
, container
->version
);
3674 free_container(container
);
3676 container
= g_new0(struct sipe_container
, 1);
3678 container
->version
= xmlnode_get_int_attrib(node
, "version", 0);
3679 sip
->containers
= g_slist_append(sip
->containers
, container
);
3680 purple_debug_info("sipe", "sipe_process_roaming_self: added container id=%d v%d\n", container
->id
, container
->version
);
3682 for (node2
= xmlnode_get_child(node
, "member"); node2
; node2
= xmlnode_get_next_twin(node2
)) {
3683 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
3684 member
->type
= xmlnode_get_attrib(node2
, "type");
3685 member
->value
= xmlnode_get_attrib(node2
, "value");
3686 container
->members
= g_slist_append(container
->members
, member
);
3687 purple_debug_info("sipe", "sipe_process_roaming_self: added container member type=%s value=%s\n",
3688 member
->type
, member
->value
? member
->value
: "");
3692 purple_debug_info("sipe", "sipe_process_roaming_self: sip->access_level_set=%s\n", sip
->access_level_set
? "TRUE" : "FALSE");
3693 if (!sip
->access_level_set
&& xmlnode_get_child(xml
, "containers")) {
3694 int sameEnterpriseAL
= sipe_find_access_level(sip
, "sameEnterprise", NULL
);
3695 int federatedAL
= sipe_find_access_level(sip
, "federated", NULL
);
3696 purple_debug_info("sipe", "sipe_process_roaming_self: sameEnterpriseAL=%d\n", sameEnterpriseAL
);
3697 purple_debug_info("sipe", "sipe_process_roaming_self: federatedAL=%d\n", federatedAL
);
3698 /* initial set-up to let counterparties see your status */
3699 if (sameEnterpriseAL
< 0) {
3700 struct sipe_container
*container
= sipe_find_container(sip
, 200);
3701 guint version
= container
? container
->version
: 0;
3702 sipe_send_set_container_members(sip
, 200, version
, "add", "sameEnterprise", NULL
);
3704 if (federatedAL
< 0) {
3705 struct sipe_container
*container
= sipe_find_container(sip
, 100);
3706 guint version
= container
? container
->version
: 0;
3707 sipe_send_set_container_members(sip
, 100, version
, "add", "federated", NULL
);
3709 sip
->access_level_set
= TRUE
;
3713 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
3715 const char *acknowledged
;
3719 user
= xmlnode_get_attrib(node
, "user"); /* without 'sip:' prefix */
3720 if (!user
) continue;
3721 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
3722 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
3723 uri
= sip_uri_from_name(user
);
3725 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
3727 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
3728 if(sipe_strcase_equal(acknowledged
,"false")){
3729 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
3730 if (!purple_find_buddy(sip
->account
, uri
)) {
3731 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
3734 hdr
= g_strdup_printf(
3736 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
3738 body
= g_strdup_printf(
3739 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
3740 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
3741 "</setSubscribers>", user
);
3743 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
3747 g_free(display_name
);
3754 /* Publish initial state if not yet.
3755 * Assuming this happens on initial responce to subscription to roaming-self
3756 * so we've already updated our roaming data in full.
3759 if (!sip
->initial_state_published
) {
3760 send_publish_category_initial(sip
);
3761 sip
->initial_state_published
= TRUE
;
3763 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
3764 do_update_status
= FALSE
;
3765 } else if (aggreg_avail
) {
3767 g_free(sip
->status
);
3768 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
3769 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
3771 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
3775 if (do_update_status
) {
3776 purple_debug_info("sipe", "sipe_process_roaming_self: switch to '%s' for the account\n", sip
->status
);
3777 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
3783 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
)
3785 gchar
*to
= sip_uri_self(sip
);
3786 gchar
*tmp
= get_contact(sip
);
3787 gchar
*hdr
= g_strdup_printf(
3788 "Event: vnd-microsoft-roaming-ACL\r\n"
3789 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
3790 "Supported: com.microsoft.autoextend\r\n"
3791 "Supported: ms-benotify\r\n"
3792 "Proxy-Require: ms-benotify\r\n"
3793 "Supported: ms-piggyback-first-notify\r\n"
3794 "Contact: %s\r\n", tmp
);
3797 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
3803 * To request for presence information about the user, access level settings that have already been configured by the user
3804 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
3805 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
3808 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
)
3810 gchar
*to
= sip_uri_self(sip
);
3811 gchar
*tmp
= get_contact(sip
);
3812 gchar
*hdr
= g_strdup_printf(
3813 "Event: vnd-microsoft-roaming-self\r\n"
3814 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
3815 "Supported: ms-benotify\r\n"
3816 "Proxy-Require: ms-benotify\r\n"
3817 "Supported: ms-piggyback-first-notify\r\n"
3819 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
3821 gchar
*body
=g_strdup(
3822 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
3823 "<roaming type=\"categories\"/>"
3824 "<roaming type=\"containers\"/>"
3825 "<roaming type=\"subscribers\"/></roamingList>");
3828 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3837 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
)
3839 gchar
*to
= sip_uri_self(sip
);
3840 gchar
*tmp
= get_contact(sip
);
3841 gchar
*hdr
= g_strdup_printf(
3842 "Event: vnd-microsoft-provisioning\r\n"
3843 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
3844 "Supported: com.microsoft.autoextend\r\n"
3845 "Supported: ms-benotify\r\n"
3846 "Proxy-Require: ms-benotify\r\n"
3847 "Supported: ms-piggyback-first-notify\r\n"
3849 "Contact: %s\r\n", tmp
);
3852 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
3857 /** Subscription for provisioning information to help with initial
3858 * configuration. This subscription is a one-time query (denoted by the Expires header,
3859 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
3860 * configuration, meeting policies, and policy settings that Communicator must enforce.
3861 * TODO: for what we need this information.
3864 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
)
3866 gchar
*to
= sip_uri_self(sip
);
3867 gchar
*tmp
= get_contact(sip
);
3868 gchar
*hdr
= g_strdup_printf(
3869 "Event: vnd-microsoft-provisioning-v2\r\n"
3870 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
3871 "Supported: com.microsoft.autoextend\r\n"
3872 "Supported: ms-benotify\r\n"
3873 "Proxy-Require: ms-benotify\r\n"
3874 "Supported: ms-piggyback-first-notify\r\n"
3877 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
3878 gchar
*body
= g_strdup(
3879 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
3880 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
3881 "<provisioningGroup name=\"ucPolicy\"/>"
3882 "</provisioningGroupList>");
3885 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
3892 sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
3893 gpointer value
, gpointer user_data
)
3895 struct sip_subscription
*subscription
= value
;
3896 struct sip_dialog
*dialog
= &subscription
->dialog
;
3897 struct sipe_account_data
*sip
= user_data
;
3898 gchar
*tmp
= get_contact(sip
);
3899 gchar
*hdr
= g_strdup_printf(
3902 "Contact: %s\r\n", subscription
->event
, tmp
);
3905 /* Rate limit to max. 25 requests per seconds */
3906 g_usleep(1000000 / 25);
3908 send_sip_request(sip
->gc
, "SUBSCRIBE", dialog
->with
, dialog
->with
, hdr
, NULL
, dialog
, NULL
);
3912 /* IM Session (INVITE and MESSAGE methods) */
3914 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
3916 get_end_points (struct sipe_account_data
*sip
,
3917 struct sip_session
*session
)
3921 if (session
== NULL
) {
3925 res
= g_strdup_printf("<sip:%s>", sip
->username
);
3927 SIPE_DIALOG_FOREACH
{
3929 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
3932 if (dialog
->theirepid
) {
3934 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
3937 } SIPE_DIALOG_FOREACH_END
;
3943 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_account_data
*sip
,
3945 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3947 gboolean ret
= TRUE
;
3949 if (msg
->response
!= 200) {
3950 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
3954 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
3960 * Asks UA/proxy about its capabilities.
3962 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
3964 gchar
*to
= sip_uri(who
);
3965 gchar
*contact
= get_contact(sip
);
3966 gchar
*request
= g_strdup_printf(
3967 "Accept: application/sdp\r\n"
3968 "Contact: %s\r\n", contact
);
3971 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
3978 sipe_notify_user(struct sipe_account_data
*sip
,
3979 struct sip_session
*session
,
3980 PurpleMessageFlags flags
,
3981 const gchar
*message
)
3983 PurpleConversation
*conv
;
3985 if (!session
->conv
) {
3986 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
3988 conv
= session
->conv
;
3990 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
3994 sipe_present_info(struct sipe_account_data
*sip
,
3995 struct sip_session
*session
,
3996 const gchar
*message
)
3998 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
4002 sipe_present_err(struct sipe_account_data
*sip
,
4003 struct sip_session
*session
,
4004 const gchar
*message
)
4006 sipe_notify_user(sip
, session
, PURPLE_MESSAGE_ERROR
, message
);
4010 sipe_present_message_undelivered_err(struct sipe_account_data
*sip
,
4011 struct sip_session
*session
,
4015 const gchar
*message
)
4017 char *msg
, *msg_tmp
, *msg_tmp2
;
4020 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
4021 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
4023 /* Service unavailable; Server Internal Error; Server Time-out */
4024 if (sip_error
== 606 && sip_warning
== 309) { /* Not acceptable all. */ /* Message contents not allowed by policy */
4025 label
= _("Your message or invitation was not delivered, possibly because it contains a hyperlink or other content that the system administrator has blocked.");
4028 } else if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
4029 label
= _("This message was not delivered to %s because the service is not available");
4030 } else if (sip_error
== 486) { /* Busy Here */
4031 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
4032 } else if (sip_error
== 415) { /* Unsupported media type */
4033 label
= _("This message was not delivered to %s because one or more recipients don't support this type of message");
4035 label
= _("This message was not delivered to %s because one or more recipients are offline");
4038 msg_tmp
= g_strdup_printf( "%s%s\n%s" ,
4039 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""),
4042 sipe_present_err(sip
, session
, msg_tmp
);
4050 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4051 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4053 gboolean ret
= TRUE
;
4054 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4055 struct sip_session
*session
= sipe_session_find_im(sip
, with
);
4056 struct sip_dialog
*dialog
;
4059 struct queued_message
*message
;
4062 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
4067 dialog
= sipe_dialog_find(session
, with
);
4069 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
4074 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4075 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
4077 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4079 if (msg
->response
>= 400) {
4080 PurpleBuddy
*pbuddy
;
4081 const char *alias
= with
;
4082 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
4085 purple_debug_info("sipe", "process_message_response: MESSAGE response >= 400\n");
4088 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
4090 warning
= atoi(parts
[0]);
4095 /* cancel file transfer as rejected by server */
4096 if (msg
->response
== 606 && /* Not acceptable all. */
4097 warning
== 309 && /* Message contents not allowed by policy */
4098 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
4100 GSList
*parsed_body
= sipe_ft_parse_msg_body(msg
->body
);
4101 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4102 sipe_utils_nameval_free(parsed_body
);
4105 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4106 alias
= purple_buddy_get_alias(pbuddy
);
4109 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, warning
, alias
, (message
? message
->body
: NULL
));
4111 /* drop dangling IM sessions: assume that BYE from remote never reached us */
4112 if (msg
->response
== 408 || /* Request timeout */
4113 msg
->response
== 480 || /* Temporarily Unavailable */
4114 msg
->response
== 481) { /* Call/Transaction Does Not Exist */
4115 purple_debug_info("sipe", "process_message_response: assuming dangling IM session, dropping it.\n");
4116 send_sip_request(sip
->gc
, "BYE", with
, with
, NULL
, NULL
, dialog
, NULL
);
4121 const gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
4123 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
->body
));
4124 purple_debug_info("sipe", "process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)\n",
4125 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
4128 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4129 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
4130 key
, g_hash_table_size(session
->unconfirmed_messages
));
4136 if (ret
) sipe_im_process_queue(sip
, session
);
4141 sipe_is_election_finished(struct sip_session
*session
);
4144 sipe_election_result(struct sipe_account_data
*sip
,
4148 process_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
4149 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
4151 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4152 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4153 struct sip_dialog
*dialog
;
4154 struct sip_session
*session
;
4156 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4158 purple_debug_info("sipe", "process_info_response: failed find dialog for callid %s, exiting.", callid
);
4162 if (msg
->response
== 200 && g_str_has_prefix(contenttype
, "application/x-ms-mim")) {
4163 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4164 xmlnode
*xn_request_rm_response
= xmlnode_get_child(xn_action
, "RequestRMResponse");
4165 xmlnode
*xn_set_rm_response
= xmlnode_get_child(xn_action
, "SetRMResponse");
4167 if (xn_request_rm_response
) {
4168 const char *with
= xmlnode_get_attrib(xn_request_rm_response
, "uri");
4169 const char *allow
= xmlnode_get_attrib(xn_request_rm_response
, "allow");
4171 dialog
= sipe_dialog_find(session
, with
);
4173 purple_debug_info("sipe", "process_info_response: failed find dialog for %s, exiting.\n", with
);
4174 xmlnode_free(xn_action
);
4178 if (allow
&& !g_strcasecmp(allow
, "true")) {
4179 purple_debug_info("sipe", "process_info_response: %s has voted PRO\n", with
);
4180 dialog
->election_vote
= 1;
4181 } else if (allow
&& !g_strcasecmp(allow
, "false")) {
4182 purple_debug_info("sipe", "process_info_response: %s has voted CONTRA\n", with
);
4183 dialog
->election_vote
= -1;
4186 if (sipe_is_election_finished(session
)) {
4187 sipe_election_result(sip
, session
);
4190 } else if (xn_set_rm_response
) {
4193 xmlnode_free(xn_action
);
4200 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_dialog
*dialog
, const char *msg
, const char *content_type
)
4204 char *msgtext
= NULL
;
4205 const gchar
*msgr
= "";
4208 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
4212 sipe_parse_html(msg
, &msgformat
, &msgtext
);
4213 purple_debug_info("sipe", "sipe_send_message: msgformat=%s\n", msgformat
);
4215 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4218 msgr
= tmp2
= g_strdup_printf(";msgr=%s", msgr_value
);
4222 msgtext
= g_strdup(msg
);
4225 tmp
= get_contact(sip
);
4226 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
4227 //hdr = g_strdup("Content-Type: text/rtf\r\n");
4228 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
4229 if (content_type
== NULL
)
4230 content_type
= "text/plain";
4232 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
4236 send_sip_request(sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
, hdr
, msgtext
, dialog
, process_message_response
);
4243 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_session
* session
)
4245 GSList
*entry2
= session
->outgoing_message_queue
;
4247 struct queued_message
*msg
= entry2
->data
;
4249 /* for multiparty chat or conference */
4250 if (session
->is_multiparty
|| session
->focus_uri
) {
4251 gchar
*who
= sip_uri_self(sip
);
4252 serv_got_chat_in(sip
->gc
, session
->chat_id
, who
,
4253 PURPLE_MESSAGE_SEND
, msg
->body
, time(NULL
));
4257 SIPE_DIALOG_FOREACH
{
4259 struct queued_message
*message
;
4261 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
4263 message
= g_new0(struct queued_message
,1);
4264 message
->body
= g_strdup(msg
->body
);
4265 if (msg
->content_type
!= NULL
)
4266 message
->content_type
= g_strdup(msg
->content_type
);
4268 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
4269 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
4270 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
4271 key
, g_hash_table_size(session
->unconfirmed_messages
));
4274 sipe_send_message(sip
, dialog
, msg
->body
, msg
->content_type
);
4275 } SIPE_DIALOG_FOREACH_END
;
4277 entry2
= sipe_session_dequeue_message(session
);
4282 sipe_refer_notify(struct sipe_account_data
*sip
,
4283 struct sip_session
*session
,
4290 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4292 hdr
= g_strdup_printf(
4294 "Subscription-State: %s\r\n"
4295 "Content-Type: message/sipfrag\r\n",
4296 status
>= 200 ? "terminated" : "active");
4298 body
= g_strdup_printf(
4299 "SIP/2.0 %d %s\r\n",
4302 send_sip_request(sip
->gc
, "NOTIFY", who
, who
, hdr
, body
, dialog
, NULL
);
4309 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
4311 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
4312 struct sip_session
*session
;
4313 struct sip_dialog
*dialog
;
4316 struct queued_message
*message
;
4317 struct sipmsg
*request_msg
= trans
->msg
;
4319 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4322 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4324 session
= sipe_session_find_im(sip
, with
);
4327 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
4332 dialog
= sipe_dialog_find(session
, with
);
4334 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
4339 sipe_dialog_parse(dialog
, msg
, TRUE
);
4341 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
4342 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
4344 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
4346 if (msg
->response
!= 200) {
4347 PurpleBuddy
*pbuddy
;
4348 const char *alias
= with
;
4349 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
4352 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
4355 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
4357 warning
= atoi(parts
[0]);
4362 /* cancel file transfer as rejected by server */
4363 if (msg
->response
== 606 && /* Not acceptable all. */
4364 warning
== 309 && /* Message contents not allowed by policy */
4365 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
4367 GSList
*parsed_body
= sipe_ft_parse_msg_body(message
->body
);
4368 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4369 sipe_utils_nameval_free(parsed_body
);
4372 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
4373 alias
= purple_buddy_get_alias(pbuddy
);
4377 sipe_present_message_undelivered_err(sip
, session
, msg
->response
, warning
, alias
, message
->body
);
4379 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
4380 sipe_present_err(sip
, session
, tmp_msg
);
4384 sipe_dialog_remove(session
, with
);
4392 send_sip_request(sip
->gc
, "ACK", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4393 dialog
->outgoing_invite
= NULL
;
4394 dialog
->is_established
= TRUE
;
4396 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
4398 sipe_refer_notify(sip
, session
, referred_by
, 200, "OK");
4399 g_free(referred_by
);
4402 /* add user to chat if it is a multiparty session */
4403 if (session
->is_multiparty
) {
4404 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
4406 PURPLE_CBFLAGS_NONE
, TRUE
);
4409 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
4410 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
4411 sipe_session_dequeue_message(session
);
4414 sipe_im_process_queue(sip
, session
);
4416 g_hash_table_remove(session
->unconfirmed_messages
, key
);
4417 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
4418 key
, g_hash_table_size(session
->unconfirmed_messages
));
4427 sipe_invite(struct sipe_account_data
*sip
,
4428 struct sip_session
*session
,
4430 const gchar
*msg_body
,
4431 const gchar
*msg_content_type
,
4432 const gchar
*referred_by
,
4433 const gboolean is_triggered
)
4440 char *ms_text_format
= NULL
;
4441 gchar
*roster_manager
;
4443 gchar
*referred_by_str
;
4444 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
4446 if (dialog
&& dialog
->is_established
) {
4447 purple_debug_info("sipe", "session with %s already has a dialog open\n", who
);
4452 dialog
= sipe_dialog_add(session
);
4453 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
4454 dialog
->with
= g_strdup(who
);
4457 if (!(dialog
->ourtag
)) {
4458 dialog
->ourtag
= gentag();
4464 char *msgtext
= NULL
;
4466 const gchar
*msgr
= "";
4468 struct queued_message
*message
;
4471 if (!g_str_has_prefix(msg_content_type
, "text/x-msmsgsinvite")) {
4475 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
4476 purple_debug_info("sipe", "sipe_invite: msgformat=%s\n", msgformat
);
4478 msgr_value
= sipmsg_get_msgr_string(msgformat
);
4481 msgr
= tmp
= g_strdup_printf(";msgr=%s", msgr_value
);
4485 msgtext
= g_strdup(msg_body
);
4488 base64_msg
= g_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
4489 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
,
4490 msg_content_type
? msg_content_type
: "text/plain",
4497 message
= g_new0(struct queued_message
,1);
4498 message
->body
= g_strdup(msg_body
);
4499 if (msg_content_type
!= NULL
)
4500 message
->content_type
= g_strdup(msg_content_type
);
4502 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
4503 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
4504 purple_debug_info("sipe", "sipe_invite: added message %s to unconfirmed_messages(count=%d)\n",
4505 key
, g_hash_table_size(session
->unconfirmed_messages
));
4509 contact
= get_contact(sip
);
4510 end_points
= get_end_points(sip
, session
);
4511 self
= sip_uri_self(sip
);
4512 roster_manager
= g_strdup_printf(
4513 "Roster-Manager: %s\r\n"
4514 "EndPoints: %s\r\n",
4517 referred_by_str
= referred_by
?
4519 "Referred-By: %s\r\n",
4522 hdr
= g_strdup_printf(
4523 "Supported: ms-sender\r\n"
4529 "Content-Type: application/sdp\r\n",
4530 sipe_strcase_equal(session
->roster_manager
, self
) ? roster_manager
: "",
4532 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
4533 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
4535 ms_text_format
? ms_text_format
: "");
4536 g_free(ms_text_format
);
4539 body
= g_strdup_printf(
4541 "o=- 0 0 IN IP4 %s\r\n"
4545 "m=%s %d sip null\r\n"
4546 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
4547 purple_network_get_my_ip(-1),
4548 purple_network_get_my_ip(-1),
4549 sip
->ocs2007
? "message" : "x-ms-message",
4552 dialog
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
4553 to
, to
, hdr
, body
, dialog
, process_invite_response
);
4556 g_free(roster_manager
);
4558 g_free(referred_by_str
);
4565 sipe_refer(struct sipe_account_data
*sip
,
4566 struct sip_session
*session
,
4571 gchar
*epid
= get_epid(sip
);
4572 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
4573 session
->roster_manager
);
4574 const char *ourtag
= dialog
&& dialog
->ourtag
? dialog
->ourtag
: NULL
;
4576 contact
= get_contact(sip
);
4577 hdr
= g_strdup_printf(
4579 "Refer-to: <%s>\r\n"
4580 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
4581 "Require: com.microsoft.rtc-multiparty\r\n",
4585 ourtag
? ";tag=" : "",
4586 ourtag
? ourtag
: "",
4590 send_sip_request(sip
->gc
, "REFER",
4591 session
->roster_manager
, session
->roster_manager
, hdr
, NULL
, dialog
, NULL
);
4598 sipe_send_election_request_rm(struct sipe_account_data
*sip
,
4599 struct sip_dialog
*dialog
,
4602 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4604 gchar
*body
= g_strdup_printf(
4605 "<?xml version=\"1.0\"?>\r\n"
4606 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4607 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
4608 sip
->username
, bid
);
4610 send_sip_request(sip
->gc
, "INFO",
4611 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4617 sipe_send_election_set_rm(struct sipe_account_data
*sip
,
4618 struct sip_dialog
*dialog
)
4620 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
4622 gchar
*body
= g_strdup_printf(
4623 "<?xml version=\"1.0\"?>\r\n"
4624 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4625 "<SetRM uri=\"sip:%s\"/></action>\r\n",
4628 send_sip_request(sip
->gc
, "INFO",
4629 dialog
->with
, dialog
->with
, hdr
, body
, dialog
, process_info_response
);
4635 sipe_session_close(struct sipe_account_data
*sip
,
4636 struct sip_session
* session
)
4638 if (session
&& session
->focus_uri
) {
4639 sipe_conf_immcu_closed(sip
, session
);
4640 conf_session_close(sip
, session
);
4644 SIPE_DIALOG_FOREACH
{
4645 /* @TODO slow down BYE message sending rate */
4646 /* @see single subscription code */
4647 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
4648 } SIPE_DIALOG_FOREACH_END
;
4650 sipe_session_remove(sip
, session
);
4655 sipe_session_close_all(struct sipe_account_data
*sip
)
4658 while ((entry
= sip
->sessions
) != NULL
) {
4659 sipe_session_close(sip
, entry
->data
);
4664 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
4666 struct sipe_account_data
*sip
= gc
->proto_data
;
4668 purple_debug_info("sipe", "conversation with %s closed\n", who
);
4669 sipe_session_close(sip
, sipe_session_find_im(sip
, who
));
4673 sipe_chat_leave (PurpleConnection
*gc
, int id
)
4675 struct sipe_account_data
*sip
= gc
->proto_data
;
4676 struct sip_session
*session
= sipe_session_find_chat_by_id(sip
, id
);
4678 sipe_session_close(sip
, session
);
4681 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
4682 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4684 struct sipe_account_data
*sip
= gc
->proto_data
;
4685 struct sip_session
*session
;
4686 struct sip_dialog
*dialog
;
4687 gchar
*uri
= sip_uri(who
);
4689 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
4691 session
= sipe_session_find_or_add_im(sip
, uri
);
4692 dialog
= sipe_dialog_find(session
, uri
);
4694 // Queue the message
4695 sipe_session_enqueue_message(session
, what
, NULL
);
4697 if (dialog
&& !dialog
->outgoing_invite
) {
4698 sipe_im_process_queue(sip
, session
);
4699 } else if (!dialog
|| !dialog
->outgoing_invite
) {
4700 // Need to send the INVITE to get the outgoing dialog setup
4701 sipe_invite(sip
, session
, uri
, what
, NULL
, NULL
, FALSE
);
4708 static int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
4709 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
4711 struct sipe_account_data
*sip
= gc
->proto_data
;
4712 struct sip_session
*session
;
4714 purple_debug_info("sipe", "sipe_chat_send what='%s'\n", what
);
4716 session
= sipe_session_find_chat_by_id(sip
, id
);
4718 // Queue the message
4719 if (session
&& session
->dialogs
) {
4720 sipe_session_enqueue_message(session
,what
,NULL
);
4721 sipe_im_process_queue(sip
, session
);
4723 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
4724 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
4726 purple_debug_info("sipe", "sipe_chat_send: chat_name='%s'\n", chat_name
? chat_name
: "NULL");
4727 purple_debug_info("sipe", "sipe_chat_send: proto_chat_id='%s'\n", proto_chat_id
? proto_chat_id
: "NULL");
4730 struct sip_session
*session
= sipe_session_add_chat(sip
);
4732 session
->is_multiparty
= FALSE
;
4733 session
->focus_uri
= g_strdup(proto_chat_id
);
4734 sipe_session_enqueue_message(session
, what
, NULL
);
4735 sipe_invite_conf_focus(sip
, session
);
4742 /* End IM Session (INVITE and MESSAGE methods) */
4744 static void process_incoming_info(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4746 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4747 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4749 struct sip_session
*session
;
4751 purple_debug_info("sipe", "process_incoming_info: \n%s\n", msg
->body
? msg
->body
: "");
4753 /* Call Control protocol */
4754 if (g_str_has_prefix(contenttype
, "application/csta+xml"))
4756 process_incoming_info_csta(sip
, msg
);
4760 from
= parse_from(sipmsg_find_header(msg
, "From"));
4761 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4763 session
= sipe_session_find_im(sip
, from
);
4770 if (g_str_has_prefix(contenttype
, "application/x-ms-mim"))
4772 xmlnode
*xn_action
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4773 xmlnode
*xn_request_rm
= xmlnode_get_child(xn_action
, "RequestRM");
4774 xmlnode
*xn_set_rm
= xmlnode_get_child(xn_action
, "SetRM");
4776 sipmsg_add_header(msg
, "Content-Type", "application/x-ms-mim");
4778 if (xn_request_rm
) {
4779 //const char *rm = xmlnode_get_attrib(xn_request_rm, "uri");
4780 int bid
= xmlnode_get_int_attrib(xn_request_rm
, "bid", 0);
4781 gchar
*body
= g_strdup_printf(
4782 "<?xml version=\"1.0\"?>\r\n"
4783 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4784 "<RequestRMResponse uri=\"sip:%s\" allow=\"%s\"/></action>\r\n",
4786 session
->bid
< bid
? "true" : "false");
4787 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4789 } else if (xn_set_rm
) {
4791 const char *rm
= xmlnode_get_attrib(xn_set_rm
, "uri");
4792 g_free(session
->roster_manager
);
4793 session
->roster_manager
= g_strdup(rm
);
4795 body
= g_strdup_printf(
4796 "<?xml version=\"1.0\"?>\r\n"
4797 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
4798 "<SetRMResponse uri=\"sip:%s\"/></action>\r\n",
4800 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
4803 xmlnode_free(xn_action
);
4808 /* looks like purple lacks typing notification for chat */
4809 if (!session
->is_multiparty
&& !session
->focus_uri
) {
4810 xmlnode
*xn_keyboard_activity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4811 const char *status
= xmlnode_get_attrib(xmlnode_get_child(xn_keyboard_activity
, "status"),
4813 if (sipe_strequal(status
, "type")) {
4814 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4815 } else if (sipe_strequal(status
, "idle")) {
4816 serv_got_typing_stopped(sip
->gc
, from
);
4818 xmlnode_free(xn_keyboard_activity
);
4821 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4826 static void process_incoming_bye(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4828 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4829 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4830 struct sip_session
*session
;
4831 struct sip_dialog
*dialog
;
4833 /* collect dialog identification
4834 * we need callid, ourtag and theirtag to unambiguously identify dialog
4836 /* take data before 'msg' will be modified by send_sip_response */
4837 dialog
= g_new0(struct sip_dialog
, 1);
4838 dialog
->callid
= g_strdup(callid
);
4839 dialog
->cseq
= parse_cseq(sipmsg_find_header(msg
, "CSeq"));
4840 dialog
->with
= g_strdup(from
);
4841 sipe_dialog_parse(dialog
, msg
, FALSE
);
4843 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4845 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4847 session
= sipe_session_find_im(sip
, from
);
4850 sipe_dialog_free(dialog
);
4855 if (session
->roster_manager
&& !g_strcasecmp(from
, session
->roster_manager
)) {
4856 g_free(session
->roster_manager
);
4857 session
->roster_manager
= NULL
;
4860 /* This what BYE is essentially for - terminating dialog */
4861 sipe_dialog_remove_3(session
, dialog
);
4862 sipe_dialog_free(dialog
);
4863 if (session
->focus_uri
&& !g_strcasecmp(from
, session
->im_mcu_uri
)) {
4864 sipe_conf_immcu_closed(sip
, session
);
4865 } else if (session
->is_multiparty
) {
4866 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
), from
, NULL
);
4872 static void process_incoming_refer(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4874 gchar
*self
= sip_uri_self(sip
);
4875 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4876 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4877 gchar
*refer_to
= parse_from(sipmsg_find_header(msg
, "Refer-to"));
4878 gchar
*referred_by
= g_strdup(sipmsg_find_header(msg
, "Referred-By"));
4879 struct sip_session
*session
;
4880 struct sip_dialog
*dialog
;
4882 session
= sipe_session_find_chat_by_callid(sip
, callid
);
4883 dialog
= sipe_dialog_find(session
, from
);
4885 if (!session
|| !dialog
|| !session
->roster_manager
|| !sipe_strcase_equal(session
->roster_manager
, self
)) {
4886 send_sip_response(sip
->gc
, msg
, 500, "Server Internal Error", NULL
);
4888 send_sip_response(sip
->gc
, msg
, 202, "Accepted", NULL
);
4890 sipe_invite(sip
, session
, refer_to
, NULL
, NULL
, referred_by
, FALSE
);
4896 g_free(referred_by
);
4900 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
4902 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
4903 struct sip_session
*session
;
4904 struct sip_dialog
*dialog
;
4906 if (state
== PURPLE_NOT_TYPING
)
4909 session
= sipe_session_find_im(sip
, who
);
4910 dialog
= sipe_dialog_find(session
, who
);
4912 if (session
&& dialog
&& dialog
->is_established
) {
4913 send_sip_request(gc
, "INFO", who
, who
,
4914 "Content-Type: application/xml\r\n",
4915 SIPE_SEND_TYPING
, dialog
, NULL
);
4917 return SIPE_TYPING_SEND_TIMEOUT
;
4920 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
4922 GSList
*tmp
= sip
->transactions
;
4923 time_t currtime
= time(NULL
);
4925 struct transaction
*trans
= tmp
->data
;
4927 purple_debug_info("sipe", "have open transaction age: %ld\n", (long int)currtime
-trans
->time
);
4928 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
4931 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
4933 sendout_sipmsg(sip
, trans
->msg
);
4940 static void do_reauthenticate_cb(struct sipe_account_data
*sip
,
4941 SIPE_UNUSED_PARAMETER
void *unused
)
4943 /* register again when security token expires */
4944 /* we have to start a new authentication as the security token
4945 * is almost expired by sending a not signed REGISTER message */
4946 purple_debug_info("sipe", "do a full reauthentication\n");
4947 sipe_auth_free(&sip
->registrar
);
4948 sipe_auth_free(&sip
->proxy
);
4949 sip
->registerstatus
= 0;
4951 sip
->reauthenticate_set
= FALSE
;
4955 sipe_process_incoming_x_msmsgsinvite(struct sipe_account_data
*sip
,
4957 GSList
*parsed_body
)
4959 gboolean found
= FALSE
;
4962 const gchar
*invitation_command
= sipe_utils_nameval_find(parsed_body
, "Invitation-Command");
4964 if (sipe_strequal(invitation_command
, "INVITE")) {
4965 sipe_ft_incoming_transfer(sip
->gc
->account
, msg
, parsed_body
);
4967 } else if (sipe_strequal(invitation_command
, "CANCEL")) {
4968 sipe_ft_incoming_cancel(sip
->gc
->account
, parsed_body
);
4970 } else if (sipe_strequal(invitation_command
, "ACCEPT")) {
4971 sipe_ft_incoming_accept(sip
->gc
->account
, parsed_body
);
4978 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
4981 const gchar
*contenttype
;
4982 gboolean found
= FALSE
;
4984 from
= parse_from(sipmsg_find_header(msg
, "From"));
4988 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
4990 contenttype
= sipmsg_find_header(msg
, "Content-Type");
4991 if (g_str_has_prefix(contenttype
, "text/plain")
4992 || g_str_has_prefix(contenttype
, "text/html")
4993 || g_str_has_prefix(contenttype
, "multipart/related")
4994 || g_str_has_prefix(contenttype
, "multipart/alternative"))
4996 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
4997 gchar
*html
= get_html_message(contenttype
, msg
->body
);
4999 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
5001 session
= sipe_session_find_im(sip
, from
);
5004 if (session
&& session
->focus_uri
) { /* a conference */
5005 gchar
*tmp
= parse_from(sipmsg_find_header(msg
, "Ms-Sender"));
5006 gchar
*sender
= parse_from(tmp
);
5008 serv_got_chat_in(sip
->gc
, session
->chat_id
, sender
,
5009 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5011 } else if (session
&& session
->is_multiparty
) { /* a multiparty chat */
5012 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5013 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5015 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5018 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5021 } else if (g_str_has_prefix(contenttype
, "application/im-iscomposing+xml")) {
5022 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5027 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
5032 state
= xmlnode_get_child(isc
, "state");
5035 purple_debug_info("sipe", "process_incoming_message: no state found\n");
5041 statedata
= xmlnode_get_data(state
);
5043 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
5044 else serv_got_typing_stopped(sip
->gc
, from
);
5049 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5051 } else if (g_str_has_prefix(contenttype
, "text/x-msmsgsinvite")) {
5052 GSList
*body
= sipe_ft_parse_msg_body(msg
->body
);
5053 found
= sipe_process_incoming_x_msmsgsinvite(sip
, msg
, body
);
5054 sipe_utils_nameval_free(body
);
5056 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
5060 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5061 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
5063 session
= sipe_session_find_im(sip
, from
);
5066 gchar
*errmsg
= g_strdup_printf(_("Received a message with unrecognized contents from %s"),
5068 sipe_present_err(sip
, session
, errmsg
);
5072 purple_debug_info("sipe", "got unknown mime-type '%s'\n", contenttype
);
5073 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
5078 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5082 const gchar
*oldHeader
;
5084 gboolean is_multiparty
= FALSE
;
5085 gboolean is_triggered
= FALSE
;
5086 gboolean was_multiparty
= TRUE
;
5087 gboolean just_joined
= FALSE
;
5089 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
5090 const gchar
*roster_manager
= sipmsg_find_header(msg
, "Roster-Manager");
5091 const gchar
*end_points_hdr
= sipmsg_find_header(msg
, "EndPoints");
5092 const gchar
*trig_invite
= sipmsg_find_header(msg
, "TriggeredInvite");
5093 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
5094 GSList
*end_points
= NULL
;
5096 struct sip_session
*session
;
5097 const gchar
*ms_text_format
;
5099 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? tmp
= fix_newlines(msg
->body
) : "");
5102 /* Invitation to join conference */
5103 if (g_str_has_prefix(content_type
, "application/ms-conf-invite+xml")) {
5104 process_incoming_invite_conf(sip
, msg
);
5108 /* Only accept text invitations */
5109 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
5110 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
5114 // TODO There *must* be a better way to clean up the To header to add a tag...
5115 purple_debug_info("sipe", "Adding a Tag to the To Header on Invite Request...\n");
5116 oldHeader
= sipmsg_find_header(msg
, "To");
5118 newHeader
= g_strdup_printf("%s;tag=%s", oldHeader
, newTag
);
5119 sipmsg_remove_header_now(msg
, "To");
5120 sipmsg_add_header_now(msg
, "To", newHeader
);
5123 if (end_points_hdr
) {
5124 end_points
= sipmsg_parse_endpoints_header(end_points_hdr
);
5126 if (g_slist_length(end_points
) > 2) {
5127 is_multiparty
= TRUE
;
5130 if (trig_invite
&& !g_strcasecmp(trig_invite
, "TRUE")) {
5131 is_triggered
= TRUE
;
5132 is_multiparty
= TRUE
;
5135 session
= sipe_session_find_chat_by_callid(sip
, callid
);
5136 /* Convert to multiparty */
5137 if (session
&& is_multiparty
&& !session
->is_multiparty
) {
5138 g_free(session
->with
);
5139 session
->with
= NULL
;
5140 was_multiparty
= FALSE
;
5141 session
->is_multiparty
= TRUE
;
5142 session
->chat_id
= rand();
5145 if (!session
&& is_multiparty
) {
5146 session
= sipe_session_find_or_add_chat_by_callid(sip
, callid
);
5149 from
= parse_from(sipmsg_find_header(msg
, "From"));
5151 session
= sipe_session_find_or_add_im(sip
, from
);
5155 g_free(session
->callid
);
5156 session
->callid
= g_strdup(callid
);
5158 session
->is_multiparty
= is_multiparty
;
5159 if (roster_manager
) {
5160 session
->roster_manager
= g_strdup(roster_manager
);
5164 if (is_multiparty
&& end_points
) {
5165 gchar
*to
= parse_from(sipmsg_find_header(msg
, "To"));
5166 GSList
*entry
= end_points
;
5168 struct sip_dialog
*dialog
;
5169 struct sipendpoint
*end_point
= entry
->data
;
5170 entry
= entry
->next
;
5172 if (!g_strcasecmp(from
, end_point
->contact
) ||
5173 !g_strcasecmp(to
, end_point
->contact
))
5176 dialog
= sipe_dialog_find(session
, end_point
->contact
);
5178 g_free(dialog
->theirepid
);
5179 dialog
->theirepid
= end_point
->epid
;
5180 end_point
->epid
= NULL
;
5182 dialog
= sipe_dialog_add(session
);
5184 dialog
->callid
= g_strdup(session
->callid
);
5185 dialog
->with
= end_point
->contact
;
5186 end_point
->contact
= NULL
;
5187 dialog
->theirepid
= end_point
->epid
;
5188 end_point
->epid
= NULL
;
5192 /* send triggered INVITE */
5193 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, NULL
, TRUE
);
5200 GSList
*entry
= end_points
;
5202 struct sipendpoint
*end_point
= entry
->data
;
5203 entry
= entry
->next
;
5204 g_free(end_point
->contact
);
5205 g_free(end_point
->epid
);
5208 g_slist_free(end_points
);
5212 struct sip_dialog
*dialog
= sipe_dialog_find(session
, from
);
5214 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
5215 sipe_dialog_parse_routes(dialog
, msg
, FALSE
);
5217 dialog
= sipe_dialog_add(session
);
5219 dialog
->callid
= g_strdup(session
->callid
);
5220 dialog
->with
= g_strdup(from
);
5221 sipe_dialog_parse(dialog
, msg
, FALSE
);
5223 if (!dialog
->ourtag
) {
5224 dialog
->ourtag
= newTag
;
5231 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
5235 if (is_multiparty
&& !session
->conv
) {
5236 gchar
*chat_title
= sipe_chat_get_name(callid
);
5237 gchar
*self
= sip_uri_self(sip
);
5238 /* create prpl chat */
5239 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_title
);
5240 session
->chat_title
= g_strdup(chat_title
);
5241 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
5243 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5245 PURPLE_CBFLAGS_NONE
, FALSE
);
5250 if (is_multiparty
&& !was_multiparty
) {
5251 /* add current IM counterparty to chat */
5252 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5253 sipe_dialog_first(session
)->with
, NULL
,
5254 PURPLE_CBFLAGS_NONE
, FALSE
);
5257 /* add inviting party to chat */
5258 if (just_joined
&& session
->conv
) {
5259 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
5261 PURPLE_CBFLAGS_NONE
, TRUE
);
5264 /* ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk= */
5266 /* This used only in 2005 official client, not 2007 or Reuters.
5267 Disabled for most cases as interfering with audit of messages which only is applied to regular MESSAGEs.
5268 Only enabled for 2005 multiparty chats as otherwise the first message got lost completely.
5270 /* also enabled for 2005 file transfer. Didn't work otherwise. */
5271 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
5272 if (is_multiparty
||
5273 (ms_text_format
&& g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite")) )
5275 if (ms_text_format
) {
5276 if (g_str_has_prefix(ms_text_format
, "text/x-msmsgsinvite"))
5278 gchar
*tmp
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
5281 gchar
*body
= (gchar
*) g_base64_decode(tmp
, &len
);
5283 GSList
*parsed_body
= sipe_ft_parse_msg_body(body
);
5285 sipe_process_incoming_x_msmsgsinvite(sip
, msg
, parsed_body
);
5286 sipe_utils_nameval_free(parsed_body
);
5287 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5291 else if (g_str_has_prefix(ms_text_format
, "text/plain") || g_str_has_prefix(ms_text_format
, "text/html"))
5293 /* please do not optimize logic inside as this code may be re-enabled for other cases */
5294 gchar
*html
= get_html_message(ms_text_format
, NULL
);
5296 if (is_multiparty
) {
5297 serv_got_chat_in(sip
->gc
, session
->chat_id
, from
,
5298 PURPLE_MESSAGE_RECV
, html
, time(NULL
));
5300 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
5303 sipmsg_add_header(msg
, "Supported", "ms-text-format"); /* accepts received message */
5311 sipmsg_add_header(msg
, "Supported", "com.microsoft.rtc-multiparty");
5312 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5313 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5315 body
= g_strdup_printf(
5317 "o=- 0 0 IN IP4 %s\r\n"
5321 "m=%s %d sip sip:%s\r\n"
5322 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5323 purple_network_get_my_ip(-1),
5324 purple_network_get_my_ip(-1),
5325 sip
->ocs2007
? "message" : "x-ms-message",
5328 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5332 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
5336 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, REFER, BENOTIFY");
5337 sipmsg_add_header(msg
, "User-Agent", sipe_get_useragent(sip
));
5338 sipmsg_add_header(msg
, "Content-Type", "application/sdp");
5340 body
= g_strdup_printf(
5342 "o=- 0 0 IN IP4 0.0.0.0\r\n"
5344 "c=IN IP4 0.0.0.0\r\n"
5346 "m=%s %d sip sip:%s\r\n"
5347 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
5348 sip
->ocs2007
? "message" : "x-ms-message",
5351 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
5356 sipe_get_auth_scheme_name(struct sipe_account_data
*sip
)
5358 const char *res
= "NTLM";
5359 #ifdef HAVE_KERBEROS
5360 if (purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
5364 (void) sip
; /* make compiler happy */
5369 static void sipe_connection_cleanup(struct sipe_account_data
*);
5370 static void create_connection(struct sipe_account_data
*, gchar
*, int);
5372 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
5373 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5376 const gchar
*expires_header
;
5378 GSList
*hdr
= msg
->headers
;
5379 struct sipnameval
*elem
;
5381 expires_header
= sipmsg_find_header(msg
, "Expires");
5382 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
5383 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
5385 switch (msg
->response
) {
5388 sip
->registerstatus
= 0;
5390 const gchar
*contact_hdr
;
5395 const gchar
*server_hdr
= sipmsg_find_header(msg
, "Server");
5396 const char *auth_scheme
;
5398 if (!sip
->reregister_set
) {
5399 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
5400 sipe_schedule_action(action_name
, expires
, do_register_cb
, NULL
, sip
, NULL
);
5401 g_free(action_name
);
5402 sip
->reregister_set
= TRUE
;
5405 sip
->registerstatus
= 3;
5407 if (server_hdr
&& !sip
->server_version
) {
5408 sip
->server_version
= g_strdup(server_hdr
);
5413 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5414 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5417 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
);
5418 fill_auth(tmp
, &sip
->registrar
);
5421 if (!sip
->reauthenticate_set
) {
5422 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
5423 guint reauth_timeout
;
5424 if (sip
->registrar
.type
== AUTH_TYPE_KERBEROS
&& sip
->registrar
.expires
> 0) {
5425 /* assuming normal Kerberos ticket expiration of about 8-10 hours */
5426 reauth_timeout
= sip
->registrar
.expires
- 300;
5428 /* NTLM: we have to reauthenticate as our security token expires
5429 after eight hours (be five minutes early) */
5430 reauth_timeout
= (8 * 3600) - 300;
5432 sipe_schedule_action(action_name
, reauth_timeout
, do_reauthenticate_cb
, NULL
, sip
, NULL
);
5433 g_free(action_name
);
5434 sip
->reauthenticate_set
= TRUE
;
5437 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
5439 epid
= get_epid(sip
);
5440 uuid
= generateUUIDfromEPID(epid
);
5443 // There can be multiple Contact headers (one per location where the user is logged in) so
5444 // make sure to only get the one for this uuid
5445 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
5446 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
5447 if (valid_contact
) {
5448 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
5449 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
5450 g_free(valid_contact
);
5453 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
5458 g_free(sip
->contact
);
5460 sip
->contact
= g_strdup_printf("<%s>", gruu
);
5463 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
5464 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
);
5466 sip
->ocs2007
= FALSE
;
5467 sip
->batched_support
= FALSE
;
5472 if (sipe_strcase_equal(elem
->name
, "Supported")) {
5473 if (sipe_strcase_equal(elem
->value
, "msrtc-event-categories")) {
5474 /* We interpret this as OCS2007+ indicator */
5475 sip
->ocs2007
= TRUE
;
5476 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s (indicates OCS2007+)\n", elem
->value
);
5478 if (sipe_strcase_equal(elem
->value
, "adhoclist")) {
5479 sip
->batched_support
= TRUE
;
5480 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s\n", elem
->value
);
5483 if (sipe_strcase_equal(elem
->name
, "Allow-Events")){
5484 gchar
**caps
= g_strsplit(elem
->value
,",",0);
5487 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
5488 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
5493 hdr
= g_slist_next(hdr
);
5496 /* rejoin open chats to be able to use them by continue to send messages */
5497 purple_conversation_foreach(sipe_rejoin_chat
);
5500 if (!sip
->subscribed
) { //do it just once, not every re-register
5502 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-contacts",
5503 (GCompareFunc
)g_ascii_strcasecmp
)) {
5504 sipe_subscribe_roaming_contacts(sip
);
5507 /* For 2007+ it does not make sence to subscribe to:
5508 * vnd-microsoft-roaming-ACL
5509 * vnd-microsoft-provisioning (not v2)
5511 * These are for backward compatibility.
5515 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-self",
5516 (GCompareFunc
)g_ascii_strcasecmp
)) {
5517 sipe_subscribe_roaming_self(sip
);
5519 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning-v2",
5520 (GCompareFunc
)g_ascii_strcasecmp
)) {
5521 sipe_subscribe_roaming_provisioning_v2(sip
);
5524 /* For 2005- servers */
5527 //sipe_options_request(sip, sip->sipdomain);
5529 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-roaming-ACL",
5530 (GCompareFunc
)g_ascii_strcasecmp
)) {
5531 sipe_subscribe_roaming_acl(sip
);
5533 if (g_slist_find_custom(sip
->allow_events
, "vnd-microsoft-provisioning",
5534 (GCompareFunc
)g_ascii_strcasecmp
)) {
5535 sipe_subscribe_roaming_provisioning(sip
);
5537 if (g_slist_find_custom(sip
->allow_events
, "presence.wpending",
5538 (GCompareFunc
)g_ascii_strcasecmp
)) {
5539 sipe_subscribe_presence_wpending(sip
, msg
);
5542 /* For 2007+ we publish our initial statuses and calendar data only after
5543 * received our existing publications in sipe_process_roaming_self()
5544 * Only in this case we know versions of current publications made
5547 /* For 2005- we publish our initial statuses only after
5548 * received our existing UserInfo data in response to
5549 * self subscription.
5550 * Only in this case we won't override existing UserInfo data
5551 * set earlier or by other client on our behalf.
5555 sip
->subscribed
= TRUE
;
5558 timeout
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "ms-keep-alive"),
5559 "timeout=", ";", NULL
);
5560 if (timeout
!= NULL
) {
5561 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
5562 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
5563 sip
->keepalive_timeout
);
5567 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\n", sip
->cseq
);
5572 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
5574 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
5575 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
5579 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
5582 tmp
= g_strsplit(parts
[0], ":", 0);
5583 hostname
= g_strdup(tmp
[0]);
5584 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
5588 tmp
= g_strsplit(parts
[i
], "=", 0);
5590 if (g_strcasecmp("transport", tmp
[0]) == 0) {
5591 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
5592 transport
= SIPE_TRANSPORT_TCP
;
5593 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
5594 transport
= SIPE_TRANSPORT_UDP
;
5603 /* Close old connection */
5604 sipe_connection_cleanup(sip
);
5606 /* Create new connection */
5607 sip
->transport
= transport
;
5608 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
5609 hostname
, port
, TRANSPORT_DESCRIPTOR
);
5610 create_connection(sip
, hostname
, port
);
5616 if (sip
->registerstatus
!= 2) {
5617 const char *auth_scheme
;
5618 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
5619 if (sip
->registrar
.retries
> 3) {
5620 sip
->gc
->wants_to_die
= TRUE
;
5621 purple_connection_error(sip
->gc
, _("Authentication failed"));
5625 auth_scheme
= sipe_get_auth_scheme_name(sip
);
5626 tmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
5628 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\n", tmp
? tmp
: "");
5630 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
5631 sip
->gc
->wants_to_die
= TRUE
;
5632 purple_connection_error(sip
->gc
, tmp2
);
5636 fill_auth(tmp
, &sip
->registrar
);
5637 sip
->registerstatus
= 2;
5638 if (sip
->account
->disconnecting
) {
5639 do_register_exp(sip
, 0);
5647 const gchar
*diagnostics
= sipmsg_find_header(msg
, "Warning");
5648 gchar
**reason
= NULL
;
5650 if (diagnostics
!= NULL
) {
5652 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
5654 reason
= g_strsplit(diagnostics
, "\"", 0);
5656 warning
= g_strdup_printf(_("You have been rejected by the server: %s"),
5657 (reason
&& reason
[1]) ? reason
[1] : _("no reason given"));
5660 sip
->gc
->wants_to_die
= TRUE
;
5661 purple_connection_error(sip
->gc
, warning
);
5668 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5669 gchar
*reason
= NULL
;
5671 if (diagnostics
!= NULL
) {
5672 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5674 warning
= g_strdup_printf(_("Not found: %s. Please contact your Administrator"),
5675 diagnostics
? (reason
? reason
: _("no reason given")) :
5676 _("SIP is either not enabled for the destination URI or it does not exist"));
5679 sip
->gc
->wants_to_die
= TRUE
;
5680 purple_connection_error(sip
->gc
, warning
);
5686 case 504: /* Server time-out */
5688 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
5689 gchar
*reason
= NULL
;
5691 if (diagnostics
!= NULL
) {
5692 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
5694 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
5697 sip
->gc
->wants_to_die
= TRUE
;
5698 purple_connection_error(sip
->gc
, warning
);
5708 * Returns 2005-style activity and Availability.
5710 * @param status Sipe statis id.
5713 sipe_get_act_avail_by_status_2005(const char *status
,
5717 int avail
= 300; /* online */
5718 int act
= 400; /* Available */
5720 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
5722 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
5724 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
5726 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
5728 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
5730 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
5731 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
5733 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
5734 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
5735 avail
= 0; /* offline */
5738 act
= 400; /* Available */
5741 if (activity
) *activity
= act
;
5742 if (availability
) *availability
= avail
;
5748 * @param activity 2005 aggregated activity. Ex.: 600
5749 * @param availablity 2005 aggregated availablity. Ex.: 300
5752 sipe_get_status_by_act_avail_2005(const int activity
,
5753 const int availablity
,
5754 char **activity_desc
)
5756 const char *status_id
= NULL
;
5757 const char *act
= NULL
;
5759 if (activity
< 150) {
5760 status_id
= SIPE_STATUS_ID_AWAY
;
5761 } else if (activity
< 200) {
5762 //status_id = SIPE_STATUS_ID_LUNCH;
5763 status_id
= SIPE_STATUS_ID_AWAY
;
5764 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
5765 } else if (activity
< 300) {
5766 //status_id = SIPE_STATUS_ID_IDLE;
5767 status_id
= SIPE_STATUS_ID_AWAY
;
5768 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5769 } else if (activity
< 400) {
5770 status_id
= SIPE_STATUS_ID_BRB
;
5771 } else if (activity
< 500) {
5772 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5773 } else if (activity
< 600) {
5774 //status_id = SIPE_STATUS_ID_ON_PHONE;
5775 status_id
= SIPE_STATUS_ID_BUSY
;
5776 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
5777 } else if (activity
< 700) {
5778 status_id
= SIPE_STATUS_ID_BUSY
;
5779 } else if (activity
< 800) {
5780 status_id
= SIPE_STATUS_ID_AWAY
;
5782 status_id
= SIPE_STATUS_ID_AVAILABLE
;
5785 if (availablity
< 100)
5786 status_id
= SIPE_STATUS_ID_OFFLINE
;
5788 if (activity_desc
&& act
) {
5789 g_free(*activity_desc
);
5790 *activity_desc
= g_strdup(act
);
5797 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
5800 sipe_get_status_by_availability(int avail
,
5801 char** activity_desc
)
5804 const char *act
= NULL
;
5807 status
= SIPE_STATUS_ID_OFFLINE
;
5808 } else if (avail
< 4500) {
5809 status
= SIPE_STATUS_ID_AVAILABLE
;
5810 } else if (avail
< 6000) {
5811 //status = SIPE_STATUS_ID_IDLE;
5812 status
= SIPE_STATUS_ID_AVAILABLE
;
5813 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
5814 } else if (avail
< 7500) {
5815 status
= SIPE_STATUS_ID_BUSY
;
5816 } else if (avail
< 9000) {
5817 //status = SIPE_STATUS_ID_BUSYIDLE;
5818 status
= SIPE_STATUS_ID_BUSY
;
5819 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
5820 } else if (avail
< 12000) {
5821 status
= SIPE_STATUS_ID_DND
;
5822 } else if (avail
< 15000) {
5823 status
= SIPE_STATUS_ID_BRB
;
5824 } else if (avail
< 18000) {
5825 status
= SIPE_STATUS_ID_AWAY
;
5827 status
= SIPE_STATUS_ID_OFFLINE
;
5830 if (activity_desc
&& act
) {
5831 g_free(*activity_desc
);
5832 *activity_desc
= g_strdup(act
);
5839 * Returns 2007-style availability value
5841 * @param sipe_status_id (in)
5842 * @param activity_token (out) Must be g_free()'d after use if consumed.
5845 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
5848 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
5850 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
5851 availability
= 15500;
5852 if (!activity_token
|| !(*activity_token
)) {
5853 activity
= SIPE_ACTIVITY_AWAY
;
5855 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
5856 availability
= 12500;
5857 activity
= SIPE_ACTIVITY_BRB
;
5858 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
5859 availability
= 9500;
5860 activity
= SIPE_ACTIVITY_DND
;
5861 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
5862 availability
= 6500;
5863 if (!activity_token
|| !(*activity_token
)) {
5864 activity
= SIPE_ACTIVITY_BUSY
;
5866 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
5867 availability
= 3500;
5868 activity
= SIPE_ACTIVITY_ONLINE
;
5869 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
5872 // Offline or invisible
5873 availability
= 18500;
5874 activity
= SIPE_ACTIVITY_OFFLINE
;
5877 if (activity_token
) {
5878 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
5880 return availability
;
5883 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
5886 sipe_xml
*xn_categories
;
5887 const sipe_xml
*xn_category
;
5888 const char *status
= NULL
;
5889 gboolean do_update_status
= FALSE
;
5890 gboolean has_note_cleaned
= FALSE
;
5891 gboolean has_free_busy_cleaned
= FALSE
;
5893 xn_categories
= sipe_xml_parse(data
, len
);
5894 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
5896 for (xn_category
= sipe_xml_child(xn_categories
, "category");
5898 xn_category
= sipe_xml_twin(xn_category
) )
5900 const sipe_xml
*xn_node
;
5902 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
5903 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
5904 sipe_utils_str_to_time(tmp
) : 0;
5907 if (sipe_strequal(attrVar
, "contactCard"))
5909 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
5912 const sipe_xml
*node
;
5913 /* identity - Display Name and email */
5914 node
= sipe_xml_child(card
, "identity");
5916 char* display_name
= sipe_xml_data(
5917 sipe_xml_child(node
, "name/displayName"));
5918 char* email
= sipe_xml_data(
5919 sipe_xml_child(node
, "email"));
5921 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
5922 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
5924 g_free(display_name
);
5928 node
= sipe_xml_child(card
, "company");
5930 char* company
= sipe_xml_data(node
);
5931 sipe_update_user_info(sip
, uri
, COMPANY_PROP
, company
);
5935 node
= sipe_xml_child(card
, "department");
5937 char* department
= sipe_xml_data(node
);
5938 sipe_update_user_info(sip
, uri
, DEPARTMENT_PROP
, department
);
5942 node
= sipe_xml_child(card
, "title");
5944 char* title
= sipe_xml_data(node
);
5945 sipe_update_user_info(sip
, uri
, TITLE_PROP
, title
);
5949 node
= sipe_xml_child(card
, "office");
5951 char* office
= sipe_xml_data(node
);
5952 sipe_update_user_info(sip
, uri
, OFFICE_PROP
, office
);
5956 node
= sipe_xml_child(card
, "url");
5958 char* site
= sipe_xml_data(node
);
5959 sipe_update_user_info(sip
, uri
, SITE_PROP
, site
);
5963 for (node
= sipe_xml_child(card
, "phone");
5965 node
= sipe_xml_twin(node
))
5967 const char *phone_type
= sipe_xml_attribute(node
, "type");
5968 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
5969 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
5971 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, phone_display_string
);
5974 g_free(phone_display_string
);
5977 for (node
= sipe_xml_child(card
, "address");
5979 node
= sipe_xml_twin(node
))
5981 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
5982 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
5983 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
5984 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
5985 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
5986 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
5988 sipe_update_user_info(sip
, uri
, ADDRESS_STREET_PROP
, street
);
5989 sipe_update_user_info(sip
, uri
, ADDRESS_CITY_PROP
, city
);
5990 sipe_update_user_info(sip
, uri
, ADDRESS_STATE_PROP
, state
);
5991 sipe_update_user_info(sip
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
5992 sipe_update_user_info(sip
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
5998 g_free(country_code
);
6006 else if (sipe_strequal(attrVar
, "note"))
6009 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6011 if (!has_note_cleaned
) {
6012 has_note_cleaned
= TRUE
;
6014 g_free(sbuddy
->note
);
6015 sbuddy
->note
= NULL
;
6016 sbuddy
->is_oof_note
= FALSE
;
6017 sbuddy
->note_since
= publish_time
;
6019 do_update_status
= TRUE
;
6021 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
6022 /* clean up in case no 'note' element is supplied
6023 * which indicate note removal in client
6025 g_free(sbuddy
->note
);
6026 sbuddy
->note
= NULL
;
6027 sbuddy
->is_oof_note
= FALSE
;
6028 sbuddy
->note_since
= publish_time
;
6030 xn_node
= sipe_xml_child(xn_category
, "note/body");
6033 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
6035 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
6036 sbuddy
->note_since
= publish_time
;
6038 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s), note(%s)\n",
6039 uri
, sbuddy
->note
? sbuddy
->note
: "");
6041 /* to trigger UI refresh in case no status info is supplied in this update */
6042 do_update_status
= TRUE
;
6047 else if(sipe_strequal(attrVar
, "state"))
6051 const sipe_xml
*xn_availability
;
6052 const sipe_xml
*xn_activity
;
6053 const sipe_xml
*xn_meeting_subject
;
6054 const sipe_xml
*xn_meeting_location
;
6055 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6057 xn_node
= sipe_xml_child(xn_category
, "state");
6058 if (!xn_node
) continue;
6059 xn_availability
= sipe_xml_child(xn_node
, "availability");
6060 if (!xn_availability
) continue;
6061 xn_activity
= sipe_xml_child(xn_node
, "activity");
6062 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
6063 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
6065 tmp
= sipe_xml_data(xn_availability
);
6066 availability
= atoi(tmp
);
6069 /* activity, meeting_subject, meeting_location */
6074 g_free(sbuddy
->activity
);
6075 sbuddy
->activity
= NULL
;
6077 const char *token
= sipe_xml_attribute(xn_activity
, "token");
6078 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
6081 if (!is_empty(token
)) {
6082 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
6084 /* from custom element */
6086 char *custom
= sipe_xml_data(xn_custom
);
6088 if (!is_empty(custom
)) {
6089 sbuddy
->activity
= custom
;
6095 /* meeting_subject */
6096 g_free(sbuddy
->meeting_subject
);
6097 sbuddy
->meeting_subject
= NULL
;
6098 if (xn_meeting_subject
) {
6099 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
6101 if (!is_empty(meeting_subject
)) {
6102 sbuddy
->meeting_subject
= meeting_subject
;
6103 meeting_subject
= NULL
;
6105 g_free(meeting_subject
);
6107 /* meeting_location */
6108 g_free(sbuddy
->meeting_location
);
6109 sbuddy
->meeting_location
= NULL
;
6110 if (xn_meeting_location
) {
6111 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
6113 if (!is_empty(meeting_location
)) {
6114 sbuddy
->meeting_location
= meeting_location
;
6115 meeting_location
= NULL
;
6117 g_free(meeting_location
);
6120 status
= sipe_get_status_by_availability(availability
, &tmp
);
6121 if (sbuddy
->activity
&& tmp
) {
6122 char *tmp2
= sbuddy
->activity
;
6124 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
6128 sbuddy
->activity
= tmp
;
6132 do_update_status
= TRUE
;
6135 else if(sipe_strequal(attrVar
, "calendarData"))
6137 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sip
->buddies
, uri
) : NULL
;
6138 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
6139 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
6141 if (sbuddy
&& xn_free_busy
) {
6142 if (!has_free_busy_cleaned
) {
6143 has_free_busy_cleaned
= TRUE
;
6145 g_free(sbuddy
->cal_start_time
);
6146 sbuddy
->cal_start_time
= NULL
;
6148 g_free(sbuddy
->cal_free_busy_base64
);
6149 sbuddy
->cal_free_busy_base64
= NULL
;
6151 g_free(sbuddy
->cal_free_busy
);
6152 sbuddy
->cal_free_busy
= NULL
;
6154 sbuddy
->cal_free_busy_published
= publish_time
;
6157 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
6158 g_free(sbuddy
->cal_start_time
);
6159 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
6161 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
6164 g_free(sbuddy
->cal_free_busy_base64
);
6165 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
6167 g_free(sbuddy
->cal_free_busy
);
6168 sbuddy
->cal_free_busy
= NULL
;
6170 sbuddy
->cal_free_busy_published
= publish_time
;
6172 purple_debug_info("sipe", "process_incoming_notify_rlmi: startTime=%s granularity=%d cal_free_busy_base64=\n%s\n", sbuddy
->cal_start_time
, sbuddy
->cal_granularity
, sbuddy
->cal_free_busy_base64
);
6176 if (sbuddy
&& xn_working_hours
) {
6177 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
6182 if (do_update_status
) {
6183 if (!status
) { /* no status category in this update, using contact's current status */
6184 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6185 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
6186 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
6187 status
= purple_status_get_id(pstatus
);
6190 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", status
);
6191 sipe_got_user_status(sip
, uri
, status
);
6194 sipe_xml_free(xn_categories
);
6197 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
, GSList
*server
, struct sipe_account_data
*sip
)
6199 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6200 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: pool(%s)\n", host
);
6201 payload
->host
= g_strdup(host
);
6202 payload
->buddies
= server
;
6203 sipe_subscribe_presence_batched_routed(sip
, payload
);
6204 sipe_subscribe_presence_batched_routed_free(payload
);
6207 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6210 xmlnode
*xn_resource
;
6211 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
6216 xn_list
= xmlnode_from_str(data
, len
);
6218 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
6220 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
6222 const char *uri
, *state
;
6223 xmlnode
*xn_instance
;
6225 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
6226 if (!xn_instance
) continue;
6228 uri
= xmlnode_get_attrib(xn_resource
, "uri");
6229 state
= xmlnode_get_attrib(xn_instance
, "state");
6230 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n", uri
, state
);
6232 if (strstr(state
, "resubscribe")) {
6233 const char *poolFqdn
= xmlnode_get_attrib(xn_instance
, "poolFqdn");
6235 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
6236 gchar
*user
= g_strdup(uri
);
6237 host
= g_strdup(poolFqdn
);
6238 server
= g_hash_table_lookup(servers
, host
);
6239 server
= g_slist_append(server
, user
);
6240 g_hash_table_insert(servers
, host
, server
);
6242 sipe_subscribe_presence_single(sip
, (void *) uri
);
6247 /* Send out any deferred poolFqdn subscriptions */
6248 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sip
);
6249 g_hash_table_destroy(servers
);
6251 xmlnode_free(xn_list
);
6254 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6258 gchar
*activity
= NULL
;
6260 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
6261 gboolean isonline
= FALSE
;
6262 xmlnode
*display_name_node
;
6264 pidf
= xmlnode_from_str(data
, len
);
6266 purple_debug_info("sipe", "process_incoming_notify_pidf: no parseable pidf:%s\n",data
);
6270 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
6272 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6273 basicstatus
= xmlnode_get_child(status
, "basic");
6278 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic found\n");
6283 getbasic
= xmlnode_get_data(basicstatus
);
6285 purple_debug_info("sipe", "process_incoming_notify_pidf: no basic data found\n");
6290 purple_debug_info("sipe", "process_incoming_notify_pidf: basic-status(%s)\n", getbasic
);
6291 if (strstr(getbasic
, "open")) {
6296 uri
= sip_uri(xmlnode_get_attrib(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
6298 display_name_node
= xmlnode_get_child(pidf
, "display-name");
6299 if (display_name_node
) {
6300 char * display_name
= xmlnode_get_data(display_name_node
);
6302 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6303 g_free(display_name
);
6306 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
6307 if ((status
= xmlnode_get_child(tuple
, "status"))) {
6308 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
6309 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
6310 activity
= xmlnode_get_data(basicstatus
);
6311 purple_debug_info("sipe", "process_incoming_notify_pidf: activity(%s)\n", activity
);
6318 const gchar
* status_id
= NULL
;
6320 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
6321 status_id
= SIPE_STATUS_ID_BUSY
;
6322 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
6323 status_id
= SIPE_STATUS_ID_AWAY
;
6328 status_id
= SIPE_STATUS_ID_AVAILABLE
;
6331 purple_debug_info("sipe", "process_incoming_notify_pidf: status_id(%s)\n", status_id
);
6332 sipe_got_user_status(sip
, uri
, status_id
);
6334 sipe_got_user_status(sip
, uri
, SIPE_STATUS_ID_OFFLINE
);
6344 sipe_user_info_has_updated(struct sipe_account_data
*sip
,
6345 xmlnode
*xn_userinfo
)
6349 g_free(sip
->user_states
);
6350 sip
->user_states
= NULL
;
6351 if ((xn_states
= xmlnode_get_child(xn_userinfo
, "states")) != NULL
) {
6352 sip
->user_states
= xmlnode_to_str(xn_states
, NULL
);
6353 /* this is a hack-around to remove added newline after inner element,
6354 * state in this case, where it shouldn't be.
6355 * After several use of xmlnode_to_str, amount of added newlines
6356 * grows significantly.
6358 purple_str_strip_char(sip
->user_states
, '\n');
6359 //purple_str_strip_char(sip->user_states, '\r');
6362 /* Publish initial state if not yet.
6363 * Assuming this happens on initial responce to self subscription
6364 * so we've already updated our UserInfo.
6366 if (!sip
->initial_state_published
) {
6367 send_presence_soap(sip
, FALSE
);
6369 sipe_schedule_action("<+update-calendar>", UPDATE_CALENDAR_DELAY
, (Action
)sipe_update_calendar
, NULL
, sip
, NULL
);
6373 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
6375 char *activity
= NULL
;
6377 const char *status_id
= NULL
;
6380 char *self_uri
= sip_uri_self(sip
);
6383 const char *device_name
= NULL
;
6384 const char *cal_start_time
= NULL
;
6385 const char *cal_granularity
= NULL
;
6386 char *cal_free_busy_base64
= NULL
;
6387 struct sipe_buddy
*sbuddy
;
6389 xmlnode
*xn_presentity
;
6390 xmlnode
*xn_availability
;
6391 xmlnode
*xn_activity
;
6392 xmlnode
*xn_display_name
;
6394 xmlnode
*xn_phone_number
;
6395 xmlnode
*xn_userinfo
;
6399 xmlnode
*xn_contact
;
6401 char *free_activity
;
6403 const char *user_avail_nil
;
6405 time_t user_avail_since
= 0;
6406 time_t activity_since
= 0;
6408 /* fix for Reuters environment on Linux */
6409 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
6411 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
6412 xn_presentity
= xmlnode_from_str(tmp_data
, strlen(tmp_data
));
6415 xn_presentity
= xmlnode_from_str(data
, len
);
6418 xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
6419 xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
6420 xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
6421 xn_email
= xmlnode_get_child(xn_presentity
, "email");
6422 xn_phone_number
= xmlnode_get_child(xn_presentity
, "phoneNumber");
6423 xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
6424 xn_oof
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "oof") : NULL
;
6425 xn_state
= xn_userinfo
? xmlnode_get_descendant(xn_userinfo
, "states", "state", NULL
): NULL
;
6426 user_avail
= xn_state
? xmlnode_get_int_attrib(xn_state
, "avail", 0) : 0;
6427 user_avail_since
= xn_state
? sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since")) : 0;
6428 user_avail_nil
= xn_state
? xmlnode_get_attrib(xn_state
, "nil") : NULL
;
6429 xn_contact
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "contact") : NULL
;
6430 xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
6431 note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
6433 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
6435 user_avail_since
= 0;
6438 free_activity
= NULL
;
6440 name
= xmlnode_get_attrib(xn_presentity
, "uri"); /* without 'sip:' prefix */
6441 uri
= sip_uri_from_name(name
);
6442 avl
= xmlnode_get_int_attrib(xn_availability
, "aggregate", 0);
6443 epid
= xmlnode_get_attrib(xn_availability
, "epid");
6444 act
= xmlnode_get_int_attrib(xn_activity
, "aggregate", 0);
6446 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
6447 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
6448 if (user_avail
> res_avail
) {
6449 res_avail
= user_avail
;
6450 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
6453 if (xn_display_name
) {
6454 char *display_name
= g_strdup(xmlnode_get_attrib(xn_display_name
, "displayName"));
6455 char *email
= xn_email
? g_strdup(xmlnode_get_attrib(xn_email
, "email")) : NULL
;
6456 char *phone_label
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "label")) : NULL
;
6457 char *phone_number
= xn_phone_number
? g_strdup(xmlnode_get_attrib(xn_phone_number
, "number")) : NULL
;
6458 char *tel_uri
= sip_to_tel_uri(phone_number
);
6460 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, display_name
);
6461 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
6462 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
6463 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
6466 g_free(phone_label
);
6467 g_free(phone_number
);
6469 g_free(display_name
);
6474 for (node
= xmlnode_get_child(xn_contact
, "tel"); node
; node
= xmlnode_get_next_twin(node
))
6476 /* Ex.: <tel type="work">tel:+3222220000</tel> */
6477 const char *phone_type
= xmlnode_get_attrib(node
, "type");
6478 char* phone
= xmlnode_get_data(node
);
6480 sipe_update_user_phone(sip
, uri
, phone_type
, phone
, NULL
);
6486 /* devicePresence */
6487 for (node
= xmlnode_get_descendant(xn_presentity
, "devices", "devicePresence", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
6488 xmlnode
*xn_device_name
;
6489 xmlnode
*xn_calendar_info
;
6494 if (sipe_strequal(xmlnode_get_attrib(node
, "epid"), epid
)) {
6495 xn_device_name
= xmlnode_get_child(node
, "deviceName");
6496 device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
6500 xn_calendar_info
= xmlnode_get_child(node
, "calendarInfo");
6501 if (xn_calendar_info
) {
6502 const char *cal_start_time_tmp
= xmlnode_get_attrib(xn_calendar_info
, "startTime");
6504 if (cal_start_time
) {
6505 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
6506 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
6508 if (cal_start_time_t_tmp
> cal_start_time_t
) {
6509 cal_start_time
= cal_start_time_tmp
;
6510 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6511 g_free(cal_free_busy_base64
);
6512 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6514 purple_debug_info("sipe", "process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s\n", cal_start_time
, cal_granularity
, cal_free_busy_base64
);
6517 cal_start_time
= cal_start_time_tmp
;
6518 cal_granularity
= xmlnode_get_attrib(xn_calendar_info
, "granularity");
6519 g_free(cal_free_busy_base64
);
6520 cal_free_busy_base64
= xmlnode_get_data(xn_calendar_info
);
6522 purple_debug_info("sipe", "process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s\n", cal_start_time
, cal_granularity
, cal_free_busy_base64
);
6527 xn_state
= xmlnode_get_descendant(node
, "states", "state", NULL
);
6529 int dev_avail
= xmlnode_get_int_attrib(xn_state
, "avail", 0);
6530 time_t dev_avail_since
= sipe_utils_str_to_time(xmlnode_get_attrib(xn_state
, "since"));
6532 state
= xmlnode_get_data(xn_state
);
6533 if (dev_avail_since
> user_avail_since
&&
6534 dev_avail
>= res_avail
)
6536 res_avail
= dev_avail
;
6537 if (!is_empty(state
))
6539 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
6541 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
6542 } else if (sipe_strequal(state
, "presenting")) {
6544 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
6549 activity_since
= dev_avail_since
;
6551 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
6558 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
6560 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
6564 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
6567 g_free(sbuddy
->activity
);
6568 sbuddy
->activity
= activity
;
6571 sbuddy
->activity_since
= activity_since
;
6573 sbuddy
->user_avail
= user_avail
;
6574 sbuddy
->user_avail_since
= user_avail_since
;
6576 g_free(sbuddy
->note
);
6577 sbuddy
->note
= NULL
;
6578 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
6580 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
6582 g_free(sbuddy
->device_name
);
6583 sbuddy
->device_name
= NULL
;
6584 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
6586 if (!is_empty(cal_free_busy_base64
)) {
6587 g_free(sbuddy
->cal_start_time
);
6588 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
6590 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
6592 g_free(sbuddy
->cal_free_busy_base64
);
6593 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
6594 cal_free_busy_base64
= NULL
;
6596 g_free(sbuddy
->cal_free_busy
);
6597 sbuddy
->cal_free_busy
= NULL
;
6600 sbuddy
->last_non_cal_status_id
= status_id
;
6601 g_free(sbuddy
->last_non_cal_activity
);
6602 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
6604 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
6605 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
6607 sip
->is_oof_note
= sbuddy
->is_oof_note
;
6610 sip
->note
= g_strdup(sbuddy
->note
);
6612 sip
->note_since
= time(NULL
);
6615 g_free(sip
->status
);
6616 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
6619 g_free(cal_free_busy_base64
);
6622 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", status_id
);
6623 sipe_got_user_status(sip
, uri
, status_id
);
6625 if (!sip
->ocs2007
&& sipe_strcase_equal(self_uri
, uri
)) {
6626 sipe_user_info_has_updated(sip
, xn_userinfo
);
6630 xmlnode_free(xn_presentity
);
6635 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
6637 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6639 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
6641 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
6642 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
6644 const char *content
= msg
->body
;
6645 unsigned length
= msg
->bodylen
;
6646 PurpleMimeDocument
*mime
= NULL
;
6648 if (strstr(ctype
, "multipart"))
6650 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6651 const char *content_type
;
6653 mime
= purple_mime_document_parse(doc
);
6654 parts
= purple_mime_document_get_parts(mime
);
6656 content
= purple_mime_part_get_data(parts
->data
);
6657 length
= purple_mime_part_get_length(parts
->data
);
6658 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
6659 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
6661 process_incoming_notify_rlmi_resub(sip
, content
, length
);
6663 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
6665 process_incoming_notify_msrtc(sip
, content
, length
);
6669 process_incoming_notify_rlmi(sip
, content
, length
);
6671 parts
= parts
->next
;
6677 purple_mime_document_free(mime
);
6680 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
6682 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
6684 else if(strstr(ctype
, "application/rlmi+xml"))
6686 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
6689 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
6691 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
6695 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
6699 static void sipe_process_presence_timeout(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gchar
*who
, int timeout
)
6701 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
6702 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6704 purple_debug_info("sipe", "sipe_process_presence_timeout: Content-Type: %s\n", ctype
? ctype
: "");
6707 strstr(ctype
, "multipart") &&
6708 (strstr(ctype
, "application/rlmi+xml") ||
6709 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
6710 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
6711 PurpleMimeDocument
*mime
= purple_mime_document_parse(doc
);
6712 GList
*parts
= purple_mime_document_get_parts(mime
);
6713 GSList
*buddies
= NULL
;
6714 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
6717 xmlnode
*xml
= xmlnode_from_str(purple_mime_part_get_data(parts
->data
),
6718 purple_mime_part_get_length(parts
->data
));
6720 if (xml
&& !sipe_strequal(xml
->name
, "list")) {
6721 gchar
*uri
= sip_uri(xmlnode_get_attrib(xml
, "uri"));
6723 buddies
= g_slist_append(buddies
, uri
);
6727 parts
= parts
->next
;
6730 if (mime
) purple_mime_document_free(mime
);
6732 payload
->host
= g_strdup(who
);
6733 payload
->buddies
= buddies
;
6734 sipe_schedule_action(action_name
, timeout
,
6735 sipe_subscribe_presence_batched_routed
,
6736 sipe_subscribe_presence_batched_routed_free
,
6738 purple_debug_info("sipe", "Resubscription multiple contacts with batched support & route(%s) in %d\n", who
, timeout
);
6741 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6742 purple_debug_info("sipe", "Resubscription single contact with batched support(%s) in %d\n", who
, timeout
);
6744 g_free(action_name
);
6748 * Dispatcher for all incoming subscription information
6749 * whether it comes from NOTIFY, BENOTIFY requests or
6750 * piggy-backed to subscription's OK responce.
6752 * @param request whether initiated from BE/NOTIFY request or OK-response message.
6753 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
6755 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
6757 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
6758 const gchar
*event
= sipmsg_find_header(msg
, "Event");
6759 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
6762 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n",
6764 tmp
= fix_newlines(msg
->body
));
6766 purple_debug_info("sipe", "process_incoming_notify: subscription_state: %s\n", subscription_state
? subscription_state
: "");
6768 /* implicit subscriptions */
6769 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
6770 sipe_process_imdn(sip
, msg
);
6774 /* for one off subscriptions (send with Expire: 0) */
6775 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
6777 sipe_process_provisioning_v2(sip
, msg
);
6779 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
6781 sipe_process_provisioning(sip
, msg
);
6783 else if (sipe_strcase_equal(event
, "presence"))
6785 sipe_process_presence(sip
, msg
);
6787 else if (sipe_strcase_equal(event
, "registration-notify"))
6789 sipe_process_registration_notify(sip
, msg
);
6792 if (!subscription_state
|| strstr(subscription_state
, "active"))
6794 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
6796 sipe_process_roaming_contacts(sip
, msg
);
6798 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
6800 sipe_process_roaming_self(sip
, msg
);
6802 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
6804 sipe_process_roaming_acl(sip
, msg
);
6806 else if (sipe_strcase_equal(event
, "presence.wpending"))
6808 sipe_process_presence_wpending(sip
, msg
);
6810 else if (sipe_strcase_equal(event
, "conference"))
6812 sipe_process_conference(sip
, msg
);
6817 /* The server sends status 'terminated' */
6818 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
6819 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
6820 gchar
*key
= sipe_get_subscription_key(event
, who
);
6822 purple_debug_info("sipe", "process_incoming_notify: server says that subscription to %s was terminated.\n", who
);
6825 if (g_hash_table_lookup(sip
->subscriptions
, key
)) {
6826 g_hash_table_remove(sip
->subscriptions
, key
);
6827 purple_debug_info("sipe", "process_subscribe_response: subscription dialog removed for: %s\n", key
);
6833 if (!request
&& event
) {
6834 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
6835 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
6836 purple_debug_info("sipe", "process_incoming_notify: subscription expires:%d\n", timeout
);
6839 /* 2 min ahead of expiration */
6840 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
6842 if (sipe_strcase_equal(event
, "presence.wpending") &&
6843 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
6845 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
6846 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_wpending
, NULL
, sip
, NULL
);
6847 g_free(action_name
);
6849 else if (sipe_strcase_equal(event
, "presence") &&
6850 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
6852 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
6853 gchar
*action_name
= g_strdup_printf(ACTION_NAME_PRESENCE
, who
);
6855 if (sip
->batched_support
) {
6856 sipe_process_presence_timeout(sip
, msg
, who
, timeout
);
6859 sipe_schedule_action(action_name
, timeout
, sipe_subscribe_presence_single
, g_free
, sip
, g_strdup(who
));
6860 purple_debug_info("sipe", "Resubscription single contact (%s) in %d\n", who
, timeout
);
6862 g_free(action_name
);
6868 /* The client responses on received a NOTIFY message */
6869 if (request
&& !benotify
)
6871 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
6876 * Whether user manually changed status or
6877 * it was changed automatically due to user
6878 * became inactive/active again
6881 sipe_is_user_state(struct sipe_account_data
*sip
)
6884 time_t now
= time(NULL
);
6886 purple_debug_info("sipe", "sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
6887 purple_debug_info("sipe", "sipe_is_user_state: now : %s", asctime(localtime(&now
)));
6889 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
6891 purple_debug_info("sipe", "sipe_is_user_state: res = %s\n", res
? "USER" : "MACHINE");
6896 send_presence_soap0(struct sipe_account_data
*sip
,
6897 gboolean do_publish_calendar
,
6898 gboolean do_reset_status
)
6900 struct sipe_ews
* ews
= sip
->ews
;
6901 int availability
= 0;
6906 gchar
*res_note
= NULL
;
6907 gchar
*res_oof
= NULL
;
6908 const gchar
*note_pub
= NULL
;
6909 gchar
*states
= NULL
;
6910 gchar
*calendar_data
= NULL
;
6911 gchar
*epid
= get_epid(sip
);
6912 time_t now
= time(NULL
);
6913 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
6914 const gchar
*oof_note
= ews
? sipe_ews_get_oof_note(ews
) : NULL
;
6915 const char *user_input
;
6916 gboolean pub_oof
= ews
&& oof_note
&& (!sip
->note
|| ews
->updated
> sip
->note_since
);
6918 if (oof_note
&& sip
->note
) {
6919 purple_debug_info("sipe", "ews->oof_start : %s", asctime(localtime(&(ews
->oof_start
))));
6920 purple_debug_info("sipe", "sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
6923 purple_debug_info("sipe", "sip->note : %s", sip
->note
? sip
->note
: "");
6925 if (!sip
->initial_state_published
||
6928 g_free(sip
->status
);
6929 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
6932 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
6936 note_pub
= oof_note
;
6937 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
6938 ews
->published
= TRUE
;
6939 } else if (sip
->note
) {
6940 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in ews already */
6943 sip
->is_oof_note
= FALSE
;
6944 sip
->note_since
= 0;
6946 note_pub
= sip
->note
;
6947 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
6953 /* to protocol internal plain text format */
6954 tmp
= purple_markup_strip_html(note_pub
);
6955 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
6960 if (!do_reset_status
) {
6961 if (sipe_is_user_state(sip
) && !do_publish_calendar
&& sip
->initial_state_published
)
6963 gchar
*activity_token
= NULL
;
6964 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
6966 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
6971 g_free(activity_token
);
6973 else /* preserve existing publication */
6975 if (sip
->user_states
) {
6976 states
= g_strdup(sip
->user_states
);
6980 /* do nothing - then User state will be erased */
6982 sip
->initial_state_published
= TRUE
;
6985 if (ews
&& (!is_empty(ews
->legacy_dn
) || !is_empty(ews
->email
)) && ews
->fb_start
&& !is_empty(ews
->free_busy
))
6987 char *fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
6988 char *free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
6989 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
6990 !is_empty(ews
->legacy_dn
) ? ews
->legacy_dn
: ews
->email
,
6993 g_free(fb_start_str
);
6994 g_free(free_busy_base64
);
6997 user_input
= !sipe_is_user_state(sip
) && sip
->status
!= SIPE_STATUS_ID_AVAILABLE
? "idle" : "active";
6999 /* forming resulting XML */
7000 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
7004 (tmp
= g_ascii_strup(g_get_host_name(), -1)),
7005 res_note
? res_note
: "",
7006 res_oof
? res_oof
: "",
7007 states
? states
: "",
7008 calendar_data
? calendar_data
: "",
7017 g_free(calendar_data
);
7019 send_soap_request(sip
, body
);
7022 g_free(since_time_str
);
7027 send_presence_soap(struct sipe_account_data
*sip
,
7028 gboolean do_publish_calendar
)
7030 return send_presence_soap0(sip
, do_publish_calendar
, FALSE
);
7035 process_send_presence_category_publish_response(struct sipe_account_data
*sip
,
7037 struct transaction
*trans
)
7039 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
7041 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
7047 gboolean has_device_publication
= FALSE
;
7049 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
7051 /* test if version mismatch fault */
7052 fault_code
= xmlnode_get_data(xmlnode_get_child(xml
, "Faultcode"));
7053 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
7054 purple_debug_info("sipe", "process_send_presence_category_publish_response: unsupported fault code:%s returning.\n", fault_code
);
7061 /* accumulating information about faulty versions */
7062 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
7063 for (node
= xmlnode_get_descendant(xml
, "details", "operation", NULL
);
7065 node
= xmlnode_get_next_twin(node
))
7067 const gchar
*index
= xmlnode_get_attrib(node
, "index");
7068 const gchar
*curVersion
= xmlnode_get_attrib(node
, "curVersion");
7070 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
7071 purple_debug_info("sipe", "fault added: index:%s curVersion:%s\n", index
, curVersion
);
7075 /* here we are parsing own request to figure out what publication
7076 * referensed here only by index went wrong
7078 xml
= xmlnode_from_str(trans
->msg
->body
, trans
->msg
->bodylen
);
7081 for (node
= xmlnode_get_descendant(xml
, "publications", "publication", NULL
),
7082 index_our
= 1; /* starts with 1 - our first publication */
7084 node
= xmlnode_get_next_twin(node
), index_our
++)
7086 gchar
*idx
= g_strdup_printf("%d", index_our
);
7087 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
7088 const gchar
*categoryName
= xmlnode_get_attrib(node
, "categoryName");
7091 if (sipe_strequal("device", categoryName
)) {
7092 has_device_publication
= TRUE
;
7095 if (curVersion
) { /* fault exist on this index */
7096 const gchar
*container
= xmlnode_get_attrib(node
, "container");
7097 const gchar
*instance
= xmlnode_get_attrib(node
, "instance");
7098 /* key is <category><instance><container> */
7099 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
7100 struct sipe_publication
*publication
=
7101 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, categoryName
), key
);
7103 purple_debug_info("sipe", "key is %s\n", key
);
7106 purple_debug_info("sipe", "Updating %s with version %s. Was %d before.\n",
7107 key
, curVersion
, publication
->version
);
7108 /* updating publication's version to the correct one */
7109 publication
->version
= atoi(curVersion
);
7115 g_hash_table_destroy(faults
);
7117 /* rebublishing with right versions */
7118 if (has_device_publication
) {
7119 send_publish_category_initial(sip
);
7121 send_presence_status(sip
);
7128 * Returns 'device' XML part for publication.
7129 * Must be g_free'd after use.
7132 sipe_publish_get_category_device(struct sipe_account_data
*sip
)
7136 gchar
*epid
= get_epid(sip
);
7137 gchar
*uuid
= generateUUIDfromEPID(epid
);
7138 guint device_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_DEVICE
);
7139 /* key is <category><instance><container> */
7140 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
7141 struct sipe_publication
*publication
=
7142 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
7147 uri
= sip_uri_self(sip
);
7148 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
7150 publication
? publication
->version
: 0,
7153 "00:00:00+01:00", /* @TODO make timezone real*/
7164 * A service method - use
7165 * - send_publish_get_category_state_machine and
7166 * - send_publish_get_category_state_user instead.
7167 * Must be g_free'd after use.
7170 sipe_publish_get_category_state(struct sipe_account_data
*sip
,
7171 gboolean is_user_state
)
7173 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
7174 guint instance
= is_user_state
? sipe_get_pub_instance(sip
, SIPE_PUB_STATE_USER
) :
7175 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_MACHINE
);
7176 /* key is <category><instance><container> */
7177 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7178 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7179 struct sipe_publication
*publication_2
=
7180 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7181 struct sipe_publication
*publication_3
=
7182 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7187 if (publication_2
&& (publication_2
->availability
== availability
))
7189 purple_debug_info("sipe", "sipe_publish_get_category_state: state has NOT changed. Exiting.\n");
7190 return NULL
; /* nothing to update */
7193 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
7195 publication_2
? publication_2
->version
: 0,
7198 publication_3
? publication_3
->version
: 0,
7203 * Only Busy and OOF calendar event are published.
7204 * Different instances are used for that.
7206 * Must be g_free'd after use.
7209 sipe_publish_get_category_state_calendar(struct sipe_account_data
*sip
,
7210 struct sipe_cal_event
*event
,
7214 gchar
*start_time_str
;
7215 int availability
= 0;
7218 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
7219 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR_OOF
) :
7220 sipe_get_pub_instance(sip
, SIPE_PUB_STATE_CALENDAR
);
7222 /* key is <category><instance><container> */
7223 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
7224 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
7225 struct sipe_publication
*publication_2
=
7226 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
7227 struct sipe_publication
*publication_3
=
7228 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
7233 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
7234 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7235 "Exiting as no publication and no event for cal_satus:%d\n", cal_satus
);
7241 (publication_3
->availability
== availability
) &&
7242 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
7245 purple_debug_info("sipe", "sipe_publish_get_category_state_calendar: "
7246 "cal state has NOT changed for cal_satus:%d. Exiting.\n", cal_satus
);
7247 return NULL
; /* nothing to update */
7252 (event
->cal_status
== SIPE_CAL_BUSY
||
7253 event
->cal_status
== SIPE_CAL_OOF
))
7255 gchar
*availability_xml_str
= NULL
;
7256 gchar
*activity_xml_str
= NULL
;
7258 if (event
->cal_status
== SIPE_CAL_BUSY
) {
7259 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
7262 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
7263 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7264 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
7265 "minAvailability=\"6500\"",
7266 "maxAvailability=\"8999\"");
7267 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
7268 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
7269 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
7270 "minAvailability=\"12000\"",
7273 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
7275 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
7277 publication_2
? publication_2
->version
: 0,
7280 availability_xml_str
? availability_xml_str
: "",
7281 activity_xml_str
? activity_xml_str
: "",
7282 event
->subject
? event
->subject
: "",
7283 event
->location
? event
->location
: "",
7286 publication_3
? publication_3
->version
: 0,
7289 availability_xml_str
? availability_xml_str
: "",
7290 activity_xml_str
? activity_xml_str
: "",
7291 event
->subject
? event
->subject
: "",
7292 event
->location
? event
->location
: ""
7294 g_free(start_time_str
);
7295 g_free(availability_xml_str
);
7296 g_free(activity_xml_str
);
7299 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
7301 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
7303 publication_2
? publication_2
->version
: 0,
7306 publication_3
? publication_3
->version
: 0
7314 * Returns 'machineState' XML part for publication.
7315 * Must be g_free'd after use.
7318 sipe_publish_get_category_state_machine(struct sipe_account_data
*sip
)
7320 return sipe_publish_get_category_state(sip
, FALSE
);
7324 * Returns 'userState' XML part for publication.
7325 * Must be g_free'd after use.
7328 sipe_publish_get_category_state_user(struct sipe_account_data
*sip
)
7330 return sipe_publish_get_category_state(sip
, TRUE
);
7334 * Returns 'note' XML part for publication.
7335 * Must be g_free'd after use.
7337 * Protocol format for Note is plain text.
7339 * @param note a note in Sipe internal HTML format
7340 * @param note_type either personal or OOF
7343 sipe_publish_get_category_note(struct sipe_account_data
*sip
,
7344 const char *note
, /* html */
7345 const char *note_type
,
7349 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sip
, SIPE_PUB_NOTE_OOF
) : 0;
7350 /* key is <category><instance><container> */
7351 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
7352 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
7353 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
7355 struct sipe_publication
*publication_note_200
=
7356 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
7357 struct sipe_publication
*publication_note_300
=
7358 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
7359 struct sipe_publication
*publication_note_400
=
7360 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
7362 char *tmp
= note
? purple_markup_strip_html(note
) : NULL
;
7363 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
7364 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
7365 char *res
, *tmp1
, *tmp2
, *tmp3
;
7366 char *start_time_attr
;
7367 char *end_time_attr
;
7371 g_free(key_note_200
);
7372 g_free(key_note_300
);
7373 g_free(key_note_400
);
7375 /* we even need to republish empty note */
7376 if (sipe_strequal(n1
, n2
))
7378 purple_debug_info("sipe", "sipe_publish_get_category_note: note has NOT changed. Exiting.\n");
7380 return NULL
; /* nothing to update */
7383 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
7386 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
7390 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7393 publication_note_200
? publication_note_200
->version
: 0,
7395 start_time_attr
? start_time_attr
: "",
7396 end_time_attr
? end_time_attr
: "",
7399 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7402 publication_note_300
? publication_note_300
->version
: 0,
7404 start_time_attr
? start_time_attr
: "",
7405 end_time_attr
? end_time_attr
: "",
7408 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
7411 publication_note_400
? publication_note_400
->version
: 0,
7413 start_time_attr
? start_time_attr
: "",
7414 end_time_attr
? end_time_attr
: "",
7417 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7421 publication_note_200
? publication_note_200
->version
: 0,
7423 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7427 publication_note_200
? publication_note_200
->version
: 0,
7429 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
7433 publication_note_200
? publication_note_200
->version
: 0,
7436 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
7438 g_free(start_time_attr
);
7439 g_free(end_time_attr
);
7449 * Returns 'calendarData' XML part with WorkingHours for publication.
7450 * Must be g_free'd after use.
7453 sipe_publish_get_category_cal_working_hours(struct sipe_account_data
*sip
)
7455 struct sipe_ews
* ews
= sip
->ews
;
7457 /* key is <category><instance><container> */
7458 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
7459 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
7460 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
7461 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
7462 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
7463 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
7465 struct sipe_publication
*publication_cal_1
=
7466 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7467 struct sipe_publication
*publication_cal_100
=
7468 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7469 struct sipe_publication
*publication_cal_200
=
7470 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7471 struct sipe_publication
*publication_cal_300
=
7472 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7473 struct sipe_publication
*publication_cal_400
=
7474 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7475 struct sipe_publication
*publication_cal_32000
=
7476 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7478 const char *n1
= ews
? ews
->working_hours_xml_str
: NULL
;
7479 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
7482 g_free(key_cal_100
);
7483 g_free(key_cal_200
);
7484 g_free(key_cal_300
);
7485 g_free(key_cal_400
);
7486 g_free(key_cal_32000
);
7488 if (!ews
|| is_empty(ews
->email
) || is_empty(ews
->working_hours_xml_str
)) {
7489 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: no data to publish, exiting\n");
7493 if (sipe_strequal(n1
, n2
))
7495 purple_debug_info("sipe", "sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.\n");
7496 return NULL
; /* nothing to update */
7499 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
7501 publication_cal_1
? publication_cal_1
->version
: 0,
7503 ews
->working_hours_xml_str
,
7505 publication_cal_100
? publication_cal_100
->version
: 0,
7507 publication_cal_200
? publication_cal_200
->version
: 0,
7509 ews
->working_hours_xml_str
,
7511 publication_cal_300
? publication_cal_300
->version
: 0,
7513 ews
->working_hours_xml_str
,
7514 /* 400 - Personal */
7515 publication_cal_400
? publication_cal_400
->version
: 0,
7517 ews
->working_hours_xml_str
,
7518 /* 32000 - Blocked */
7519 publication_cal_32000
? publication_cal_32000
->version
: 0
7524 * Returns 'calendarData' XML part with FreeBusy for publication.
7525 * Must be g_free'd after use.
7528 sipe_publish_get_category_cal_free_busy(struct sipe_account_data
*sip
)
7530 struct sipe_ews
* ews
= sip
->ews
;
7531 guint cal_data_instance
= sipe_get_pub_instance(sip
, SIPE_PUB_CALENDAR_DATA
);
7533 char *free_busy_base64
;
7538 /* key is <category><instance><container> */
7539 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
7540 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
7541 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
7542 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
7543 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
7544 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
7546 struct sipe_publication
*publication_cal_1
=
7547 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
7548 struct sipe_publication
*publication_cal_100
=
7549 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
7550 struct sipe_publication
*publication_cal_200
=
7551 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
7552 struct sipe_publication
*publication_cal_300
=
7553 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
7554 struct sipe_publication
*publication_cal_400
=
7555 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
7556 struct sipe_publication
*publication_cal_32000
=
7557 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
7560 g_free(key_cal_100
);
7561 g_free(key_cal_200
);
7562 g_free(key_cal_300
);
7563 g_free(key_cal_400
);
7564 g_free(key_cal_32000
);
7566 if (!ews
|| is_empty(ews
->email
) || !ews
->fb_start
|| is_empty(ews
->free_busy
)) {
7567 purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: no data to publish, exiting\n");
7571 fb_start_str
= sipe_utils_time_to_str(ews
->fb_start
);
7572 free_busy_base64
= sipe_cal_get_freebusy_base64(ews
->free_busy
);
7574 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
7575 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
7577 /* we will rebuplish the same data to refresh publication time,
7578 * so if data from multiple sources, most recent will be choosen
7580 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
7582 // purple_debug_info("sipe", "sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.\n");
7583 // g_free(fb_start_str);
7584 // g_free(free_busy_base64);
7585 // return NULL; /* nothing to update */
7588 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
7591 publication_cal_1
? publication_cal_1
->version
: 0,
7594 publication_cal_100
? publication_cal_100
->version
: 0,
7597 publication_cal_200
? publication_cal_200
->version
: 0,
7603 publication_cal_300
? publication_cal_300
->version
: 0,
7607 /* 400 - Personal */
7609 publication_cal_400
? publication_cal_400
->version
: 0,
7613 /* 32000 - Blocked */
7615 publication_cal_32000
? publication_cal_32000
->version
: 0
7618 g_free(fb_start_str
);
7619 g_free(free_busy_base64
);
7623 static void send_presence_publish(struct sipe_account_data
*sip
, const char *publications
)
7630 uri
= sip_uri_self(sip
);
7631 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
7635 tmp
= get_contact(sip
);
7636 hdr
= g_strdup_printf("Contact: %s\r\n"
7637 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
7639 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
7648 send_publish_category_initial(struct sipe_account_data
*sip
)
7650 gchar
*pub_device
= sipe_publish_get_category_device(sip
);
7652 gchar
*publications
;
7654 g_free(sip
->status
);
7655 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
7657 pub_machine
= sipe_publish_get_category_state_machine(sip
);
7658 publications
= g_strdup_printf("%s%s",
7660 pub_machine
? pub_machine
: "");
7662 g_free(pub_machine
);
7664 send_presence_publish(sip
, publications
);
7665 g_free(publications
);
7669 send_presence_category_publish(struct sipe_account_data
*sip
)
7671 gchar
*pub_state
= sipe_is_user_state(sip
) ?
7672 sipe_publish_get_category_state_user(sip
) :
7673 sipe_publish_get_category_state_machine(sip
);
7674 gchar
*pub_note
= sipe_publish_get_category_note(sip
,
7676 sip
->is_oof_note
? "OOF" : "personal",
7679 gchar
*publications
;
7681 if (!pub_state
&& !pub_note
) {
7682 purple_debug_info("sipe", "send_presence_category_publish: nothing has changed. Exiting.\n");
7686 publications
= g_strdup_printf("%s%s",
7687 pub_state
? pub_state
: "",
7688 pub_note
? pub_note
: "");
7693 send_presence_publish(sip
, publications
);
7694 g_free(publications
);
7698 * Publishes self status
7699 * based on own calendar information.
7704 publish_calendar_status_self(struct sipe_account_data
*sip
)
7706 struct sipe_cal_event
* event
= NULL
;
7707 gchar
*pub_cal_working_hours
= NULL
;
7708 gchar
*pub_cal_free_busy
= NULL
;
7709 gchar
*pub_calendar
= NULL
;
7710 gchar
*pub_calendar2
= NULL
;
7711 gchar
*pub_oof_note
= NULL
;
7712 const gchar
*oof_note
;
7713 time_t oof_start
= 0;
7717 purple_debug_info("sipe", "publish_calendar_status_self() no calendar data.\n");
7721 purple_debug_info("sipe", "publish_calendar_status_self() started.\n");
7722 if (sip
->ews
->cal_events
) {
7723 event
= sipe_cal_get_event(sip
->ews
->cal_events
, time(NULL
));
7727 purple_debug_info("sipe", "publish_calendar_status_self: current event is NULL\n");
7729 char *desc
= sipe_cal_event_describe(event
);
7730 purple_debug_info("sipe", "publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
7736 OOF publish, Busy clean
7738 OOF clean, Busy publish
7740 OOF clean, Busy clean
7742 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
7743 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_OOF
);
7744 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7745 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
7746 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7747 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, event
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7749 pub_calendar
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_OOF
);
7750 pub_calendar2
= sipe_publish_get_category_state_calendar(sip
, NULL
, sip
->ews
->email
, SIPE_CAL_BUSY
);
7753 oof_note
= sipe_ews_get_oof_note(sip
->ews
);
7754 if (sipe_strequal("Scheduled", sip
->ews
->oof_state
)) {
7755 oof_start
= sip
->ews
->oof_start
;
7756 oof_end
= sip
->ews
->oof_end
;
7758 pub_oof_note
= sipe_publish_get_category_note(sip
, oof_note
, "OOF", oof_start
, oof_end
);
7760 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sip
);
7761 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sip
);
7763 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
7764 purple_debug_info("sipe", "publish_calendar_status_self: nothing has changed.\n");
7766 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
7767 pub_cal_working_hours
? pub_cal_working_hours
: "",
7768 pub_cal_free_busy
? pub_cal_free_busy
: "",
7769 pub_calendar
? pub_calendar
: "",
7770 pub_calendar2
? pub_calendar2
: "",
7771 pub_oof_note
? pub_oof_note
: "");
7773 send_presence_publish(sip
, publications
);
7774 g_free(publications
);
7777 g_free(pub_cal_working_hours
);
7778 g_free(pub_cal_free_busy
);
7779 g_free(pub_calendar
);
7780 g_free(pub_calendar2
);
7781 g_free(pub_oof_note
);
7783 /* repeat scheduling */
7784 sipe_sched_calendar_status_self_publish(sip
, time(NULL
));
7787 static void send_presence_status(struct sipe_account_data
*sip
)
7789 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
7791 if (!status
) return;
7793 purple_debug_info("sipe", "send_presence_status: status: %s (%s)\n",
7794 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
7795 sipe_is_user_state(sip
) ? "USER" : "MACHINE");
7798 send_presence_category_publish(sip
);
7800 send_presence_soap(sip
, FALSE
);
7804 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
7806 gboolean found
= FALSE
;
7807 const char *method
= msg
->method
? msg
->method
: "NOT FOUND";
7808 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,method
);
7809 if (msg
->response
== 0) { /* request */
7810 if (sipe_strequal(method
, "MESSAGE")) {
7811 process_incoming_message(sip
, msg
);
7813 } else if (sipe_strequal(method
, "NOTIFY")) {
7814 purple_debug_info("sipe","send->process_incoming_notify\n");
7815 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
7817 } else if (sipe_strequal(method
, "BENOTIFY")) {
7818 purple_debug_info("sipe","send->process_incoming_benotify\n");
7819 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
7821 } else if (sipe_strequal(method
, "INVITE")) {
7822 process_incoming_invite(sip
, msg
);
7824 } else if (sipe_strequal(method
, "REFER")) {
7825 process_incoming_refer(sip
, msg
);
7827 } else if (sipe_strequal(method
, "OPTIONS")) {
7828 process_incoming_options(sip
, msg
);
7830 } else if (sipe_strequal(method
, "INFO")) {
7831 process_incoming_info(sip
, msg
);
7833 } else if (sipe_strequal(method
, "ACK")) {
7834 // ACK's don't need any response
7836 } else if (sipe_strequal(method
, "SUBSCRIBE")) {
7837 // LCS 2005 sends us these - just respond 200 OK
7839 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
7840 } else if (sipe_strequal(method
, "BYE")) {
7841 process_incoming_bye(sip
, msg
);
7844 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
7846 } else { /* response */
7847 struct transaction
*trans
= transactions_find(sip
, msg
);
7849 if (msg
->response
== 407) {
7850 gchar
*resend
, *auth
;
7853 if (sip
->proxy
.retries
> 30) return;
7854 sip
->proxy
.retries
++;
7855 /* do proxy authentication */
7857 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
7859 fill_auth(ptmp
, &sip
->proxy
);
7860 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
7861 sipmsg_remove_header_now(trans
->msg
, "Proxy-Authorization");
7862 sipmsg_add_header_now_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
7864 resend
= sipmsg_to_string(trans
->msg
);
7865 /* resend request */
7866 sendout_pkt(sip
->gc
, resend
);
7869 if (msg
->response
< 200) {
7870 /* ignore provisional response */
7871 purple_debug_info("sipe", "got provisional (%d) response, ignoring\n", msg
->response
);
7873 sip
->proxy
.retries
= 0;
7874 if (sipe_strequal(trans
->msg
->method
, "REGISTER")) {
7875 if (msg
->response
== 401)
7877 sip
->registrar
.retries
++;
7881 sip
->registrar
.retries
= 0;
7883 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\n", sip
->cseq
);
7885 if (msg
->response
== 401) {
7886 gchar
*resend
, *auth
, *ptmp
;
7887 const char* auth_scheme
;
7889 if (sip
->registrar
.retries
> 4) return;
7890 sip
->registrar
.retries
++;
7892 auth_scheme
= sipe_get_auth_scheme_name(sip
);
7893 ptmp
= sipmsg_find_auth_header(msg
, auth_scheme
);
7895 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\n", ptmp
? ptmp
: "");
7897 char *tmp2
= g_strconcat(_("Incompatible authentication scheme chosen"), ": ", auth_scheme
, NULL
);
7898 sip
->gc
->wants_to_die
= TRUE
;
7899 purple_connection_error(sip
->gc
, tmp2
);
7904 fill_auth(ptmp
, &sip
->registrar
);
7905 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
7906 sipmsg_remove_header_now(trans
->msg
, "Authorization");
7907 sipmsg_add_header_now_pos(trans
->msg
, "Authorization", auth
, 5);
7909 resend
= sipmsg_to_string(trans
->msg
);
7910 /* resend request */
7911 sendout_pkt(sip
->gc
, resend
);
7916 if (trans
->callback
) {
7917 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\n");
7918 /* call the callback to process response*/
7919 (trans
->callback
)(sip
, msg
, trans
);
7922 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\n", sip
->cseq
);
7923 transactions_remove(sip
, trans
);
7929 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
7933 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", method
, msg
->response
);
7937 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
7946 /* according to the RFC remove CRLF at the beginning */
7947 while (*cur
== '\r' || *cur
== '\n') {
7950 if (cur
!= conn
->inbuf
) {
7951 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
7952 conn
->inbufused
= strlen(conn
->inbuf
);
7955 /* Received a full Header? */
7956 sip
->processing_input
= TRUE
;
7957 while (sip
->processing_input
&&
7958 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
7959 time_t currtime
= time(NULL
);
7962 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), tmp
= fix_newlines(conn
->inbuf
));
7964 msg
= sipmsg_parse_header(conn
->inbuf
);
7967 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
7968 if (msg
&& restlen
>= msg
->bodylen
) {
7969 dummy
= g_malloc(msg
->bodylen
+ 1);
7970 memcpy(dummy
, cur
, msg
->bodylen
);
7971 dummy
[msg
->bodylen
] = '\0';
7973 cur
+= msg
->bodylen
;
7974 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
7975 conn
->inbufused
= strlen(conn
->inbuf
);
7978 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n", restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
7985 purple_debug_info("sipe", "body:\n%s", msg->body);
7988 // Verify the signature before processing it
7989 if (sip
->registrar
.gssapi_context
) {
7990 struct sipmsg_breakdown msgbd
;
7991 gchar
*signature_input_str
;
7994 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
7995 signature_input_str
= sipmsg_breakdown_get_string(sip
->registrar
.version
, &msgbd
);
7997 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
7999 if (rspauth
!= NULL
) {
8000 if (!sip_sec_verify_signature(sip
->registrar
.gssapi_context
, signature_input_str
, rspauth
)) {
8001 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
8002 process_input_message(sip
, msg
);
8004 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid.\n");
8005 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
8006 sip
->gc
->wants_to_die
= TRUE
;
8008 } else if (msg
->response
== 401) {
8009 purple_connection_error(sip
->gc
, _("Authentication failed"));
8010 sip
->gc
->wants_to_die
= TRUE
;
8012 g_free(signature_input_str
);
8015 sipmsg_breakdown_free(&msgbd
);
8017 process_input_message(sip
, msg
);
8024 static void sipe_udp_process(gpointer data
, gint source
,
8025 SIPE_UNUSED_PARAMETER PurpleInputCondition con
)
8027 PurpleConnection
*gc
= data
;
8028 struct sipe_account_data
*sip
= gc
->proto_data
;
8031 static char buffer
[65536];
8032 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
8033 time_t currtime
= time(NULL
);
8036 purple_debug_info("sipe", "received - %s######\n%s\n#######\n", ctime(&currtime
), buffer
);
8037 msg
= sipmsg_parse_msg(buffer
);
8038 if (msg
) process_input_message(sip
, msg
);
8042 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
8044 struct sipe_account_data
*sip
= gc
->proto_data
;
8045 PurpleSslConnection
*gsc
= sip
->gsc
;
8047 purple_debug_error("sipe", "%s",debug
);
8048 purple_connection_error(gc
, msg
);
8050 /* Invalidate this connection. Next send will open a new one */
8052 connection_remove(sip
, gsc
->fd
);
8053 purple_ssl_close(gsc
);
8059 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8060 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8062 PurpleConnection
*gc
= data
;
8063 struct sipe_account_data
*sip
;
8064 struct sip_connection
*conn
;
8066 gboolean firstread
= TRUE
;
8068 /* NOTE: This check *IS* necessary */
8069 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
8070 purple_ssl_close(gsc
);
8074 sip
= gc
->proto_data
;
8075 conn
= connection_find(sip
, gsc
->fd
);
8077 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
8078 gc
->wants_to_die
= TRUE
;
8079 purple_connection_error(gc
, _("Connection not found. Please try to connect again"));
8083 /* Read all available data from the SSL connection */
8085 /* Increase input buffer size as needed */
8086 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8087 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8088 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8089 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
8092 /* Try to read as much as there is space left in the buffer */
8093 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
8094 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
8096 if (len
< 0 && errno
== EAGAIN
) {
8097 /* Try again later */
8099 } else if (len
< 0) {
8100 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
8102 } else if (firstread
&& (len
== 0)) {
8103 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
8107 conn
->inbufused
+= len
;
8110 /* Equivalence indicates that there is possibly more data to read */
8111 } while (len
== readlen
);
8113 conn
->inbuf
[conn
->inbufused
] = '\0';
8114 process_input(sip
, conn
);
8118 static void sipe_input_cb(gpointer data
, gint source
,
8119 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8121 PurpleConnection
*gc
= data
;
8122 struct sipe_account_data
*sip
= gc
->proto_data
;
8124 struct sip_connection
*conn
= connection_find(sip
, source
);
8126 purple_debug_error("sipe", "Connection not found!\n");
8130 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
8131 conn
->inbuflen
+= SIMPLE_BUF_INC
;
8132 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
8135 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
8137 if (len
< 0 && errno
== EAGAIN
)
8139 else if (len
<= 0) {
8140 purple_debug_info("sipe", "sipe_input_cb: read error\n");
8141 connection_remove(sip
, source
);
8142 if (sip
->fd
== source
) sip
->fd
= -1;
8146 conn
->inbufused
+= len
;
8147 conn
->inbuf
[conn
->inbufused
] = '\0';
8149 process_input(sip
, conn
);
8152 /* Callback for new connections on incoming TCP port */
8153 static void sipe_newconn_cb(gpointer data
, gint source
,
8154 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8156 PurpleConnection
*gc
= data
;
8157 struct sipe_account_data
*sip
= gc
->proto_data
;
8158 struct sip_connection
*conn
;
8160 int newfd
= accept(source
, NULL
, NULL
);
8162 conn
= connection_create(sip
, newfd
);
8164 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8167 static void login_cb(gpointer data
, gint source
,
8168 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
8170 PurpleConnection
*gc
= data
;
8171 struct sipe_account_data
*sip
;
8172 struct sip_connection
*conn
;
8174 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8182 purple_connection_error(gc
, _("Could not connect"));
8186 sip
= gc
->proto_data
;
8188 sip
->last_keepalive
= time(NULL
);
8190 conn
= connection_create(sip
, source
);
8194 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
8197 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
,
8198 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
8200 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
8201 if (sip
== NULL
) return;
8206 static guint
sipe_ht_hash_nick(const char *nick
)
8208 char *lc
= g_utf8_strdown(nick
, -1);
8209 guint bucket
= g_str_hash(lc
);
8215 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
8217 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
8220 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
8222 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8224 sip
->listen_data
= NULL
;
8226 if (listenfd
== -1) {
8227 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8233 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
8234 sip
->listenfd
= sip
->fd
;
8236 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
8238 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
8242 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
,
8243 SIPE_UNUSED_PARAMETER
const char *error_message
)
8245 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8247 sip
->query_data
= NULL
;
8249 if (!hosts
|| !hosts
->data
) {
8250 purple_connection_error(sip
->gc
, _("Could not resolve hostname"));
8254 hosts
= g_slist_remove(hosts
, hosts
->data
);
8255 g_free(sip
->serveraddr
);
8256 sip
->serveraddr
= hosts
->data
;
8257 hosts
= g_slist_remove(hosts
, hosts
->data
);
8259 void *tmp
= hosts
->data
;
8260 hosts
= g_slist_remove(hosts
, tmp
);
8261 hosts
= g_slist_remove(hosts
, tmp
);
8265 /* create socket for incoming connections */
8266 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
8267 sipe_udp_host_resolved_listen_cb
, sip
);
8268 if (sip
->listen_data
== NULL
) {
8269 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8274 static const struct sipe_service_data
*current_service
= NULL
;
8276 static void sipe_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
8277 PurpleSslErrorType error
,
8280 PurpleConnection
*gc
= data
;
8281 struct sipe_account_data
*sip
;
8283 /* If the connection is already disconnected, we don't need to do anything else */
8284 if (!PURPLE_CONNECTION_IS_VALID(gc
))
8287 sip
= gc
->proto_data
;
8288 current_service
= sip
->service_data
;
8289 if (current_service
) {
8290 purple_debug_info("sipe", "current_service: transport '%s' service '%s'\n",
8291 current_service
->transport
? current_service
->transport
: "NULL",
8292 current_service
->service
? current_service
->service
: "NULL");
8299 case PURPLE_SSL_CONNECT_FAILED
:
8300 purple_connection_error(gc
, _("Connection failed"));
8302 case PURPLE_SSL_HANDSHAKE_FAILED
:
8303 purple_connection_error(gc
, _("SSL handshake failed"));
8305 case PURPLE_SSL_CERTIFICATE_INVALID
:
8306 purple_connection_error(gc
, _("SSL certificate invalid"));
8312 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
8314 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
8315 PurpleProxyConnectData
*connect_data
;
8317 sip
->listen_data
= NULL
;
8319 sip
->listenfd
= listenfd
;
8320 if (sip
->listenfd
== -1) {
8321 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
8325 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
8326 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
8327 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
8328 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
8329 sipe_newconn_cb
, sip
->gc
);
8330 purple_debug_info("sipe", "connecting to %s port %d\n",
8331 sip
->realhostname
, sip
->realport
);
8332 /* open tcp connection to the server */
8333 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
8334 sip
->realport
, login_cb
, sip
->gc
);
8336 if (connect_data
== NULL
) {
8337 purple_connection_error(sip
->gc
, _("Could not create socket"));
8341 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
8343 PurpleAccount
*account
= sip
->account
;
8344 PurpleConnection
*gc
= sip
->gc
;
8347 port
= (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
8350 sip
->realhostname
= hostname
;
8351 sip
->realport
= port
;
8353 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
8356 /* TODO: is there a good default grow size? */
8357 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
8358 sip
->txbuf
= purple_circ_buffer_new(0);
8360 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
8362 if (!purple_ssl_is_supported()) {
8363 gc
->wants_to_die
= TRUE
;
8364 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor"));
8368 purple_debug_info("sipe", "using SSL\n");
8370 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
8371 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
8372 if (sip
->gsc
== NULL
) {
8373 purple_connection_error(gc
, _("Could not create SSL context"));
8376 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
8378 purple_debug_info("sipe", "using UDP\n");
8380 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
8381 if (sip
->query_data
== NULL
) {
8382 purple_connection_error(gc
, _("Could not resolve hostname"));
8386 purple_debug_info("sipe", "using TCP\n");
8387 /* create socket for incoming connections */
8388 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
8389 sipe_tcp_connect_listen_cb
, sip
);
8390 if (sip
->listen_data
== NULL
) {
8391 purple_connection_error(gc
, _("Could not create listen socket"));
8397 /* Service list for autodection */
8398 static const struct sipe_service_data service_autodetect
[] = {
8399 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8400 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8401 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8402 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8406 /* Service list for SSL/TLS */
8407 static const struct sipe_service_data service_tls
[] = {
8408 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
8409 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
8413 /* Service list for TCP */
8414 static const struct sipe_service_data service_tcp
[] = {
8415 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
8416 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
8420 /* Service list for UDP */
8421 static const struct sipe_service_data service_udp
[] = {
8422 { "sip", "udp", SIPE_TRANSPORT_UDP
},
8426 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
8427 static void resolve_next_service(struct sipe_account_data
*sip
,
8428 const struct sipe_service_data
*start
)
8431 sip
->service_data
= start
;
8433 sip
->service_data
++;
8434 if (sip
->service_data
->service
== NULL
) {
8436 /* Try connecting to the SIP hostname directly */
8437 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
8438 if (sip
->auto_transport
) {
8439 // If SSL is supported, default to using it; OCS servers aren't configured
8440 // by default to accept TCP
8441 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
8442 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8443 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
8446 hostname
= g_strdup(sip
->sipdomain
);
8447 create_connection(sip
, hostname
, 0);
8452 /* Try to resolve next service */
8453 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
8454 sip
->service_data
->transport
,
8459 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
8461 struct sipe_account_data
*sip
= data
;
8463 sip
->srv_query_data
= NULL
;
8465 /* find the host to connect to */
8467 gchar
*hostname
= g_strdup(resp
->hostname
);
8468 int port
= resp
->port
;
8469 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
8473 sip
->transport
= sip
->service_data
->type
;
8475 create_connection(sip
, hostname
, port
);
8477 resolve_next_service(sip
, NULL
);
8481 static void sipe_login(PurpleAccount
*account
)
8483 PurpleConnection
*gc
;
8484 struct sipe_account_data
*sip
;
8485 gchar
**signinname_login
, **userserver
;
8486 const char *transport
;
8489 const char *username
= purple_account_get_username(account
);
8490 gc
= purple_account_get_connection(account
);
8492 purple_debug_info("sipe", "sipe_login: username '%s'\n", username
);
8494 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
8495 gc
->wants_to_die
= TRUE
;
8496 purple_connection_error(gc
, _("SIP Exchange user name contains invalid characters"));
8500 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
8501 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
8502 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
8504 sip
->account
= account
;
8505 sip
->reregister_set
= FALSE
;
8506 sip
->reauthenticate_set
= FALSE
;
8507 sip
->subscribed
= FALSE
;
8508 sip
->subscribed_buddies
= FALSE
;
8509 sip
->initial_state_published
= FALSE
;
8511 /* username format: <username>,[<optional login>] */
8512 signinname_login
= g_strsplit(username
, ",", 2);
8513 purple_debug_info("sipe", "sipe_login: signinname[0] '%s'\n", signinname_login
[0]);
8515 /* ensure that username format is name@domain */
8516 if (!strchr(signinname_login
[0], '@') || g_str_has_prefix(signinname_login
[0], "@") || g_str_has_suffix(signinname_login
[0], "@")) {
8517 g_strfreev(signinname_login
);
8518 gc
->wants_to_die
= TRUE
;
8519 purple_connection_error(gc
, _("User name should be a valid SIP URI\nExample: user@company.com"));
8522 sip
->username
= g_strdup(signinname_login
[0]);
8524 /* ensure that email format is name@domain if provided */
8525 email
= purple_account_get_string(sip
->account
, "email", NULL
);
8526 if (!is_empty(email
) &&
8527 (!strchr(email
, '@') || g_str_has_prefix(email
, "@") || g_str_has_suffix(email
, "@")))
8529 gc
->wants_to_die
= TRUE
;
8530 purple_connection_error(gc
, _("Email address should be valid if provided\nExample: user@company.com"));
8533 sip
->email
= !is_empty(email
) ? g_strdup(email
) : g_strdup(sip
->username
);
8535 /* login name specified? */
8536 if (signinname_login
[1] && strlen(signinname_login
[1])) {
8537 gchar
**domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
8538 gboolean has_domain
= domain_user
[1] != NULL
;
8539 purple_debug_info("sipe", "sipe_login: signinname[1] '%s'\n", signinname_login
[1]);
8540 sip
->authdomain
= has_domain
? g_strdup(domain_user
[0]) : NULL
;
8541 sip
->authuser
= g_strdup(domain_user
[has_domain
? 1 : 0]);
8542 purple_debug_info("sipe", "sipe_login: auth domain '%s' user '%s'\n",
8543 sip
->authdomain
? sip
->authdomain
: "", sip
->authuser
);
8544 g_strfreev(domain_user
);
8547 userserver
= g_strsplit(signinname_login
[0], "@", 2);
8548 purple_debug_info("sipe", "sipe_login: user '%s' server '%s'\n", userserver
[0], userserver
[1]);
8549 purple_connection_set_display_name(gc
, userserver
[0]);
8550 sip
->sipdomain
= g_strdup(userserver
[1]);
8551 g_strfreev(userserver
);
8552 g_strfreev(signinname_login
);
8554 if (strchr(sip
->username
, ' ') != NULL
) {
8555 gc
->wants_to_die
= TRUE
;
8556 purple_connection_error(gc
, _("SIP Exchange user name contains whitespace"));
8560 sip
->password
= g_strdup(purple_connection_get_password(gc
));
8562 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
8563 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8564 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
8565 sip
->subscriptions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
8566 g_free
, (GDestroyNotify
)sipe_subscription_free
);
8568 sip
->filetransfers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,g_free
,NULL
);
8570 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
8572 g_free(sip
->status
);
8573 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
8575 sip
->auto_transport
= FALSE
;
8576 transport
= purple_account_get_string(account
, "transport", "auto");
8577 userserver
= g_strsplit(purple_account_get_string(account
, "server", ""), ":", 2);
8578 if (userserver
[0]) {
8579 /* Use user specified server[:port] */
8583 port
= atoi(userserver
[1]);
8585 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login: user specified SIP server %s:%d\n",
8586 userserver
[0], port
);
8588 if (sipe_strequal(transport
, "auto")) {
8589 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
8590 } else if (sipe_strequal(transport
, "tls")) {
8591 sip
->transport
= SIPE_TRANSPORT_TLS
;
8592 } else if (sipe_strequal(transport
, "tcp")) {
8593 sip
->transport
= SIPE_TRANSPORT_TCP
;
8595 sip
->transport
= SIPE_TRANSPORT_UDP
;
8598 create_connection(sip
, g_strdup(userserver
[0]), port
);
8600 /* Server auto-discovery */
8601 if (sipe_strequal(transport
, "auto")) {
8602 sip
->auto_transport
= TRUE
;
8603 if (current_service
&& current_service
->transport
!= NULL
&& current_service
->service
!= NULL
){
8605 resolve_next_service(sip
, current_service
);
8607 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
8609 } else if (sipe_strequal(transport
, "tls")) {
8610 resolve_next_service(sip
, service_tls
);
8611 } else if (sipe_strequal(transport
, "tcp")) {
8612 resolve_next_service(sip
, service_tcp
);
8614 resolve_next_service(sip
, service_udp
);
8617 g_strfreev(userserver
);
8620 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
8622 connection_free_all(sip
);
8627 if (sip
->query_data
!= NULL
)
8628 purple_dnsquery_destroy(sip
->query_data
);
8629 sip
->query_data
= NULL
;
8631 if (sip
->srv_query_data
!= NULL
)
8632 purple_srv_cancel(sip
->srv_query_data
);
8633 sip
->srv_query_data
= NULL
;
8635 if (sip
->listen_data
!= NULL
)
8636 purple_network_listen_cancel(sip
->listen_data
);
8637 sip
->listen_data
= NULL
;
8639 if (sip
->gsc
!= NULL
)
8640 purple_ssl_close(sip
->gsc
);
8643 sipe_auth_free(&sip
->registrar
);
8644 sipe_auth_free(&sip
->proxy
);
8647 purple_circ_buffer_destroy(sip
->txbuf
);
8650 g_free(sip
->realhostname
);
8651 sip
->realhostname
= NULL
;
8653 g_free(sip
->server_version
);
8654 sip
->server_version
= NULL
;
8657 purple_input_remove(sip
->listenpa
);
8659 if (sip
->tx_handler
)
8660 purple_input_remove(sip
->tx_handler
);
8661 sip
->tx_handler
= 0;
8662 if (sip
->resendtimeout
)
8663 purple_timeout_remove(sip
->resendtimeout
);
8664 sip
->resendtimeout
= 0;
8665 if (sip
->timeouts
) {
8666 GSList
*entry
= sip
->timeouts
;
8668 struct scheduled_action
*sched_action
= entry
->data
;
8669 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
8670 purple_timeout_remove(sched_action
->timeout_handler
);
8671 if (sched_action
->destroy
) {
8672 (*sched_action
->destroy
)(sched_action
->payload
);
8674 g_free(sched_action
->name
);
8675 g_free(sched_action
);
8676 entry
= entry
->next
;
8679 g_slist_free(sip
->timeouts
);
8681 if (sip
->allow_events
) {
8682 GSList
*entry
= sip
->allow_events
;
8684 g_free(entry
->data
);
8685 entry
= entry
->next
;
8688 g_slist_free(sip
->allow_events
);
8690 if (sip
->containers
) {
8691 GSList
*entry
= sip
->containers
;
8693 free_container((struct sipe_container
*)entry
->data
);
8694 entry
= entry
->next
;
8697 g_slist_free(sip
->containers
);
8700 g_free(sip
->contact
);
8701 sip
->contact
= NULL
;
8703 g_free(sip
->regcallid
);
8704 sip
->regcallid
= NULL
;
8706 if (sip
->serveraddr
)
8707 g_free(sip
->serveraddr
);
8708 sip
->serveraddr
= NULL
;
8710 if (sip
->focus_factory_uri
)
8711 g_free(sip
->focus_factory_uri
);
8712 sip
->focus_factory_uri
= NULL
;
8715 sip
->processing_input
= FALSE
;
8718 sipe_ews_free(sip
->ews
);
8724 * A callback for g_hash_table_foreach_remove
8726 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
8727 SIPE_UNUSED_PARAMETER gpointer user_data
)
8729 sipe_free_buddy((struct sipe_buddy
*) buddy
);
8731 /* We must return TRUE as the key/value have already been deleted */
8735 static void sipe_close(PurpleConnection
*gc
)
8737 struct sipe_account_data
*sip
= gc
->proto_data
;
8740 /* leave all conversations */
8741 sipe_session_close_all(sip
);
8742 sipe_session_remove_all(sip
);
8745 sip_csta_close(sip
);
8748 if (PURPLE_CONNECTION_IS_CONNECTED(sip
->gc
)) {
8749 /* unsubscribe all */
8750 g_hash_table_foreach(sip
->subscriptions
, sipe_unsubscribe_cb
, sip
);
8753 do_register_exp(sip
, 0);
8756 sipe_connection_cleanup(sip
);
8757 g_free(sip
->sipdomain
);
8758 g_free(sip
->username
);
8760 g_free(sip
->password
);
8761 g_free(sip
->authdomain
);
8762 g_free(sip
->authuser
);
8763 g_free(sip
->status
);
8765 g_free(sip
->user_states
);
8767 g_hash_table_foreach_steal(sip
->buddies
, sipe_buddy_remove
, NULL
);
8768 g_hash_table_destroy(sip
->buddies
);
8769 g_hash_table_destroy(sip
->our_publications
);
8770 g_hash_table_destroy(sip
->user_state_publications
);
8771 g_hash_table_destroy(sip
->subscriptions
);
8772 g_hash_table_destroy(sip
->filetransfers
);
8775 GSList
*entry
= sip
->groups
;
8777 struct sipe_group
*group
= entry
->data
;
8778 g_free(group
->name
);
8780 entry
= entry
->next
;
8783 g_slist_free(sip
->groups
);
8785 if (sip
->our_publication_keys
) {
8786 GSList
*entry
= sip
->our_publication_keys
;
8788 g_free(entry
->data
);
8789 entry
= entry
->next
;
8792 g_slist_free(sip
->our_publication_keys
);
8794 while (sip
->transactions
)
8795 transactions_remove(sip
, sip
->transactions
->data
);
8797 g_free(gc
->proto_data
);
8798 gc
->proto_data
= NULL
;
8801 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
8802 SIPE_UNUSED_PARAMETER
void *user_data
)
8804 PurpleAccount
*acct
= purple_connection_get_account(gc
);
8805 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
8806 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
8808 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
8809 purple_conversation_present(conv
);
8813 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
8814 SIPE_UNUSED_PARAMETER
void *user_data
)
8817 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
8818 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
8821 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,
8822 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
8824 PurpleNotifySearchResults
*results
;
8825 PurpleNotifySearchColumn
*column
;
8826 xmlnode
*searchResults
;
8828 int match_count
= 0;
8829 gboolean more
= FALSE
;
8832 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
8834 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
8835 if (!searchResults
) {
8836 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
8840 results
= purple_notify_searchresults_new();
8842 if (results
== NULL
) {
8843 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
8844 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
8846 xmlnode_free(searchResults
);
8850 column
= purple_notify_searchresults_column_new(_("User name"));
8851 purple_notify_searchresults_column_add(results
, column
);
8853 column
= purple_notify_searchresults_column_new(_("Name"));
8854 purple_notify_searchresults_column_add(results
, column
);
8856 column
= purple_notify_searchresults_column_new(_("Company"));
8857 purple_notify_searchresults_column_add(results
, column
);
8859 column
= purple_notify_searchresults_column_new(_("Country"));
8860 purple_notify_searchresults_column_add(results
, column
);
8862 column
= purple_notify_searchresults_column_new(_("Email"));
8863 purple_notify_searchresults_column_add(results
, column
);
8865 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
8868 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
8869 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
8870 g_strfreev(uri_parts
);
8872 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
8873 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
8874 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
8875 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
8877 purple_notify_searchresults_row_add(results
, row
);
8881 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
8882 char *data
= xmlnode_get_data(mrow
);
8883 more
= (g_strcasecmp(data
, "true") == 0);
8887 secondary
= g_strdup_printf(
8888 dngettext(PACKAGE_NAME
,
8889 "Found %d contact%s:",
8890 "Found %d contacts%s:", match_count
),
8891 match_count
, more
? _(" (more matched your query)") : "");
8893 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
8894 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
8895 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
8898 xmlnode_free(searchResults
);
8902 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
8904 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
8905 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
8911 PurpleRequestField
*field
= entries
->data
;
8912 const char *id
= purple_request_field_get_id(field
);
8913 const char *value
= purple_request_field_string_get_value(field
);
8915 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
8917 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
8918 } while ((entries
= g_list_next(entries
)) != NULL
);
8922 struct sipe_account_data
*sip
= gc
->proto_data
;
8923 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
8924 gchar
*query
= g_strjoinv(NULL
, attrs
);
8925 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
8926 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
8927 send_soap_request_with_cb(sip
, domain_uri
, body
,
8928 (TransCallback
) process_search_contact_response
, NULL
);
8937 static void sipe_show_find_contact(PurplePluginAction
*action
)
8939 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8940 PurpleRequestFields
*fields
;
8941 PurpleRequestFieldGroup
*group
;
8942 PurpleRequestField
*field
;
8944 fields
= purple_request_fields_new();
8945 group
= purple_request_field_group_new(NULL
);
8946 purple_request_fields_add_group(fields
, group
);
8948 field
= purple_request_field_string_new("givenName", _("First name"), NULL
, FALSE
);
8949 purple_request_field_group_add_field(group
, field
);
8950 field
= purple_request_field_string_new("sn", _("Last name"), NULL
, FALSE
);
8951 purple_request_field_group_add_field(group
, field
);
8952 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
8953 purple_request_field_group_add_field(group
, field
);
8954 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
8955 purple_request_field_group_add_field(group
, field
);
8957 purple_request_fields(gc
,
8959 _("Search for a contact"),
8960 _("Enter the information for the person you wish to find. Empty fields will be ignored."),
8962 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
8964 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
8967 static void sipe_show_about_plugin(PurplePluginAction
*action
)
8969 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
8970 char *tmp
= g_strdup_printf(
8972 * Non-translatable parts, like markup, are hard-coded
8973 * into the format string. This requires more translatable
8974 * texts but it makes the translations less error prone.
8976 "<b><font size=\"+1\">SIPE " PACKAGE_VERSION
" </font></b><br/>"
8979 "<li> - MS Office Communications Server 2007 R2</li><br/>"
8980 "<li> - MS Office Communications Server 2007</li><br/>"
8981 "<li> - MS Live Communications Server 2005</li><br/>"
8982 "<li> - MS Live Communications Server 2003</li><br/>"
8983 "<li> - Reuters Messaging</li><br/>"
8985 /* 2 */ "%s: <a href=\"" PACKAGE_URL
"\">" PACKAGE_URL
"</a><br/>"
8986 /* 3,4 */ "%s: <a href=\"http://sourceforge.net/projects/sipe/forums/forum/688534\">%s</a><br/>"
8987 /* 5,6 */ "%s: <a href=\"" PACKAGE_BUGREPORT
"\">%s</a><br/>"
8988 /* 7 */ "%s: <a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a><br/>"
8989 /* 8 */ "%s: GPLv2+<br/>"
8993 " - Reuters Messaging network<br/>"
8994 " - Deutsche Bank<br/>"
8995 " - Merrill Lynch<br/>"
9004 " - Alcatel-Lucent<br/>"
9007 /* 10,11 */ "%s<a href=\"https://transifex.net/projects/p/pidgin-sipe/c/mob-branch/\">Transifex.net</a>%s.<br/>"
9009 /* 12 */ "<b>%s:</b><br/>"
9010 " - Anibal Avelar<br/>"
9011 " - Gabriel Burt<br/>"
9012 " - Stefan Becker<br/>"
9014 " - Jakub Adam<br/>"
9015 " - Tomáš Hrabčík<br/>"
9019 /* The next 13 texts make up the SIPE about note text */
9020 /* About note, part 1/13: introduction */
9021 _("A third-party plugin implementing extended version of SIP/SIMPLE used by various products"),
9022 /* About note, part 2/13: home page URL (label) */
9024 /* About note, part 3/13: support forum URL (label) */
9026 /* About note, part 4/13: support forum name (hyperlink text) */
9028 /* About note, part 5/13: bug tracker URL (label) */
9029 _("Report Problems"),
9030 /* About note, part 6/13: bug tracker URL (hyperlink text) */
9032 /* About note, part 7/13: translation service URL (label) */
9034 /* About note, part 8/13: license type (label) */
9036 /* About note, part 9/13: known users */
9037 _("We support users in such organizations as"),
9038 /* About note, part 10/13: translation request, text before Transifex.net URL */
9039 /* append a space if text is not empty */
9040 _("Please help us to translate SIPE to your native language here at "),
9041 /* About note, part 11/13: translation request, text after Transifex.net URL */
9042 /* start with a space if text is not empty */
9043 _(" using convenient web interface"),
9044 /* About note, part 12/13: author list (header) */
9046 /* About note, part 13/13: Localization credit */
9047 /* PLEASE NOTE: do *NOT* simply translate the english original */
9048 /* but write something similar to the following sentence: */
9049 /* "Localization for <language name> (<language code>): <name>" */
9050 _("Original texts in English (en): SIPE developers")
9052 purple_notify_formatted(gc
, NULL
, " ", NULL
, tmp
, NULL
, NULL
);
9056 static void sipe_republish_calendar(PurplePluginAction
*action
)
9058 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9059 struct sipe_account_data
*sip
= gc
->proto_data
;
9061 sipe_update_calendar(sip
);
9064 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
9068 struct sipe_publication
*publication
= value
;
9070 g_string_append_printf( str
,
9071 SIPE_PUB_XML_PUBLICATION_CLEAR
,
9072 publication
->category
,
9073 publication
->instance
,
9074 publication
->container
,
9075 publication
->version
,
9079 static void sipe_reset_status(PurplePluginAction
*action
)
9081 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
9082 struct sipe_account_data
*sip
= gc
->proto_data
;
9084 if (sip
->ocs2007
) /* 2007+ */
9086 GString
* str
= g_string_new(NULL
);
9087 gchar
*publications
;
9089 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
9090 purple_debug_info("sipe", "sipe_reset_status: no userState publications, exiting.\n");
9094 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
9095 publications
= g_string_free(str
, FALSE
);
9097 send_presence_publish(sip
, publications
);
9098 g_free(publications
);
9102 send_presence_soap0(sip
, FALSE
, TRUE
);
9106 GList
*sipe_actions(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
,
9109 PurpleConnection
*gc
= (PurpleConnection
*)context
;
9110 struct sipe_account_data
*sip
= gc
->proto_data
;
9112 PurplePluginAction
*act
;
9113 const char* calendar
= purple_account_get_string(sip
->account
, "calendar", "EXCH");
9115 act
= purple_plugin_action_new(_("About SIPE plugin..."), sipe_show_about_plugin
);
9116 menu
= g_list_prepend(menu
, act
);
9118 act
= purple_plugin_action_new(_("Contact search..."), sipe_show_find_contact
);
9119 menu
= g_list_prepend(menu
, act
);
9121 if (sipe_strequal(calendar
, "EXCH")) {
9122 act
= purple_plugin_action_new(_("Republish Calendar"), sipe_republish_calendar
);
9123 menu
= g_list_prepend(menu
, act
);
9126 act
= purple_plugin_action_new(_("Reset status"), sipe_reset_status
);
9127 menu
= g_list_prepend(menu
, act
);
9129 menu
= g_list_reverse(menu
);
9134 static void dummy_permit_deny(SIPE_UNUSED_PARAMETER PurpleConnection
*gc
)
9138 static gboolean
sipe_plugin_load(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9144 static gboolean
sipe_plugin_unload(SIPE_UNUSED_PARAMETER PurplePlugin
*plugin
)
9150 static char *sipe_status_text(PurpleBuddy
*buddy
)
9152 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9153 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9154 const char *status_id
= purple_status_get_id(status
);
9155 struct sipe_account_data
*sip
= (struct sipe_account_data
*)buddy
->account
->gc
->proto_data
;
9156 struct sipe_buddy
*sbuddy
;
9159 if (!sip
) return NULL
; /* happens on pidgin exit */
9161 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9163 const char *activity_str
= sbuddy
->activity
?
9165 sipe_strequal(status_id
, SIPE_STATUS_ID_BUSY
) || sipe_strequal(status_id
, SIPE_STATUS_ID_BRB
) ?
9166 purple_status_get_name(status
) : NULL
;
9168 if (activity_str
&& sbuddy
->note
)
9170 text
= g_strdup_printf("%s - <i>%s</i>", activity_str
, sbuddy
->note
);
9172 else if (activity_str
)
9174 text
= g_strdup(activity_str
);
9176 else if (sbuddy
->note
)
9178 text
= g_strdup_printf("<i>%s</i>", sbuddy
->note
);
9185 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, SIPE_UNUSED_PARAMETER gboolean full
)
9187 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
9188 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
9189 struct sipe_account_data
*sip
;
9190 struct sipe_buddy
*sbuddy
;
9192 gboolean is_oof_note
= FALSE
;
9193 char *activity
= NULL
;
9194 char *calendar
= NULL
;
9195 char *meeting_subject
= NULL
;
9196 char *meeting_location
= NULL
;
9198 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
9199 if (sip
) //happens on pidgin exit
9201 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
9204 note
= sbuddy
->note
;
9205 is_oof_note
= sbuddy
->is_oof_note
;
9206 activity
= sbuddy
->activity
;
9207 calendar
= sipe_cal_get_description(sbuddy
);
9208 meeting_subject
= sbuddy
->meeting_subject
;
9209 meeting_location
= sbuddy
->meeting_location
;
9214 if (purple_presence_is_online(presence
))
9216 const char *status_str
= activity
? activity
: purple_status_get_name(status
);
9218 purple_notify_user_info_add_pair(user_info
, _("Status"), status_str
);
9220 if (purple_presence_is_online(presence
) &&
9221 !is_empty(calendar
))
9223 purple_notify_user_info_add_pair(user_info
, _("Calendar"), calendar
);
9226 if (!is_empty(meeting_location
))
9228 purple_notify_user_info_add_pair(user_info
, _("Meeting in"), meeting_location
);
9230 if (!is_empty(meeting_subject
))
9232 purple_notify_user_info_add_pair(user_info
, _("Meeting about"), meeting_subject
);
9237 char *tmp
= g_strdup_printf("<i>%s</i>", note
);
9238 purple_debug_info("sipe", "sipe_tooltip_text: %s note: '%s'\n", buddy
->name
, note
);
9240 purple_notify_user_info_add_pair(user_info
, is_oof_note
? _("Out of office note") : _("Note"), tmp
);
9246 #if PURPLE_VERSION_CHECK(2,5,0)
9248 sipe_get_account_text_table(SIPE_UNUSED_PARAMETER PurpleAccount
*account
)
9251 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
9252 g_hash_table_insert(table
, "login_label", (gpointer
)_("user@company.com"));
9257 static PurpleBuddy
*
9258 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
9261 const gchar
*server_alias
, *email
;
9262 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
9264 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
9266 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
9268 server_alias
= purple_buddy_get_server_alias(buddy
);
9270 purple_blist_server_alias_buddy(clone
, server_alias
);
9273 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9275 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
9278 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
9280 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
9285 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
9287 PurpleBuddy
*buddy
, *b
;
9288 PurpleConnection
*gc
;
9289 PurpleGroup
* group
= purple_find_group(group_name
);
9291 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
9293 buddy
= (PurpleBuddy
*)node
;
9295 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
9296 gc
= purple_account_get_connection(buddy
->account
);
9298 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
9300 purple_blist_add_buddy_clone(group
, buddy
);
9303 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
9307 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
9309 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9311 purple_debug_info("sipe", "sipe_buddy_menu_chat_new_cb: buddy->name=%s\n", buddy
->name
);
9313 /* 2007+ conference */
9316 sipe_conf_add(sip
, buddy
->name
);
9318 else /* 2005- multiparty chat */
9320 gchar
*self
= sip_uri_self(sip
);
9321 struct sip_session
*session
;
9323 session
= sipe_session_add_chat(sip
);
9324 session
->chat_title
= sipe_chat_get_name(session
->callid
);
9325 session
->roster_manager
= g_strdup(self
);
9327 session
->conv
= serv_got_joined_chat(buddy
->account
->gc
, session
->chat_id
, session
->chat_title
);
9328 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session
->conv
), self
);
9329 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
), self
, NULL
, PURPLE_CBFLAGS_NONE
, FALSE
);
9330 sipe_invite(sip
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
9337 sipe_is_election_finished(struct sip_session
*session
)
9339 gboolean res
= TRUE
;
9341 SIPE_DIALOG_FOREACH
{
9342 if (dialog
->election_vote
== 0) {
9346 } SIPE_DIALOG_FOREACH_END
;
9349 session
->is_voting_in_progress
= FALSE
;
9355 sipe_election_start(struct sipe_account_data
*sip
,
9356 struct sip_session
*session
)
9358 int election_timeout
;
9360 if (session
->is_voting_in_progress
) {
9361 purple_debug_info("sipe", "sipe_election_start: other election is in progress, exiting.\n");
9364 session
->is_voting_in_progress
= TRUE
;
9366 session
->bid
= rand();
9368 purple_debug_info("sipe", "sipe_election_start: RM election has initiated. Our bid=%d\n", session
->bid
);
9370 SIPE_DIALOG_FOREACH
{
9371 /* reset election_vote for each chat participant */
9372 dialog
->election_vote
= 0;
9374 /* send RequestRM to each chat participant*/
9375 sipe_send_election_request_rm(sip
, dialog
, session
->bid
);
9376 } SIPE_DIALOG_FOREACH_END
;
9378 election_timeout
= 15; /* sec */
9379 sipe_schedule_action("<+election-result>", election_timeout
, sipe_election_result
, NULL
, sip
, session
);
9383 * @param who a URI to whom to invite to chat
9386 sipe_invite_to_chat(struct sipe_account_data
*sip
,
9387 struct sip_session
*session
,
9391 if (session
->focus_uri
)
9393 sipe_invite_conf(sip
, session
, who
);
9395 else /* a multi-party chat */
9397 gchar
*self
= sip_uri_self(sip
);
9398 if (session
->roster_manager
) {
9399 if (sipe_strcase_equal(session
->roster_manager
, self
)) {
9400 sipe_invite(sip
, session
, who
, NULL
, NULL
, NULL
, FALSE
);
9402 sipe_refer(sip
, session
, who
);
9405 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite: no RM available\n");
9407 session
->pending_invite_queue
= slist_insert_unique_sorted(
9408 session
->pending_invite_queue
, g_strdup(who
), (GCompareFunc
)strcmp
);
9410 sipe_election_start(sip
, session
);
9417 sipe_process_pending_invite_queue(struct sipe_account_data
*sip
,
9418 struct sip_session
*session
)
9421 GSList
*entry
= session
->pending_invite_queue
;
9424 invitee
= entry
->data
;
9425 sipe_invite_to_chat(sip
, session
, invitee
);
9426 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
9432 sipe_election_result(struct sipe_account_data
*sip
,
9435 struct sip_session
*session
= (struct sip_session
*)sess
;
9437 gboolean has_won
= TRUE
;
9439 if (session
->roster_manager
) {
9440 purple_debug_info("sipe",
9441 "sipe_election_result: RM has already been elected in the meantime. It is %s\n", session
->roster_manager
);
9445 session
->is_voting_in_progress
= FALSE
;
9447 SIPE_DIALOG_FOREACH
{
9448 if (dialog
->election_vote
< 0) {
9450 rival
= dialog
->with
;
9453 } SIPE_DIALOG_FOREACH_END
;
9456 purple_debug_info("sipe", "sipe_election_result: we have won RM election!\n");
9458 session
->roster_manager
= sip_uri_self(sip
);
9460 SIPE_DIALOG_FOREACH
{
9461 /* send SetRM to each chat participant*/
9462 sipe_send_election_set_rm(sip
, dialog
);
9463 } SIPE_DIALOG_FOREACH_END
;
9465 purple_debug_info("sipe", "sipe_election_result: we loose RM election to %s\n", rival
);
9469 sipe_process_pending_invite_queue(sip
, session
);
9473 * For 2007+ conference only.
9476 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9478 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9479 struct sip_session
*session
;
9481 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s\n", buddy
->name
);
9482 purple_debug_info("sipe", "sipe_buddy_menu_chat_make_leader_cb: chat_title=%s\n", chat_title
);
9484 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9486 sipe_conf_modify_user_role(sip
, session
, buddy
->name
);
9490 * For 2007+ conference only.
9493 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
9495 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9496 struct sip_session
*session
;
9498 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: buddy->name=%s\n", buddy
->name
);
9499 purple_debug_info("sipe", "sipe_buddy_menu_chat_remove_cb: chat_title=%s\n", chat_title
);
9501 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9503 sipe_conf_delete_user(sip
, session
, buddy
->name
);
9507 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
9509 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9510 struct sip_session
*session
;
9512 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: buddy->name=%s\n", buddy
->name
);
9513 purple_debug_info("sipe", "sipe_buddy_menu_chat_invite_cb: chat_title=%s\n", chat_title
);
9515 session
= sipe_session_find_chat_by_title(sip
, chat_title
);
9517 sipe_invite_to_chat(sip
, session
, buddy
->name
);
9521 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
9523 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9525 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: buddy->name=%s\n", buddy
->name
);
9527 char *tel_uri
= sip_to_tel_uri(phone
);
9529 purple_debug_info("sipe", "sipe_buddy_menu_make_call_cb: going to call number: %s\n", tel_uri
? tel_uri
: "");
9530 sip_csta_make_call(sip
, tel_uri
);
9537 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
9540 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
9542 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9545 char *mailto
= g_strdup_printf("mailto:%s", email
);
9546 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
9550 char *const parmList
[] = {"xdg-email", mailto
, NULL
};
9551 if ((pid
= fork()) == -1)
9553 purple_debug_info("sipe", "fork() error\n");
9557 execvp(parmList
[0], parmList
);
9558 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
9566 //@TODO resolve env variable %WINDIR% first
9567 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
9570 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
9579 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
9584 * A menu which appear when right-clicking on buddy in contact list.
9587 sipe_buddy_menu(PurpleBuddy
*buddy
)
9589 PurpleBlistNode
*g_node
;
9590 PurpleGroup
*group
, *gr_parent
;
9591 PurpleMenuAction
*act
;
9593 GList
*menu_groups
= NULL
;
9594 struct sipe_account_data
*sip
= buddy
->account
->gc
->proto_data
;
9597 const char *phone_disp_str
;
9598 gchar
*self
= sip_uri_self(sip
);
9600 SIPE_SESSION_FOREACH
{
9601 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_title
&& session
->conv
)
9603 if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
))
9605 PurpleConvChatBuddyFlags flags
;
9606 PurpleConvChatBuddyFlags flags_us
;
9608 flags
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), buddy
->name
);
9609 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9610 if (session
->focus_uri
9611 && PURPLE_CBFLAGS_OP
!= (flags
& PURPLE_CBFLAGS_OP
) /* Not conf OP */
9612 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9614 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
9615 act
= purple_menu_action_new(label
,
9616 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
9617 session
->chat_title
, NULL
);
9619 menu
= g_list_prepend(menu
, act
);
9622 if (session
->focus_uri
9623 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9625 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
9626 act
= purple_menu_action_new(label
,
9627 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
9628 session
->chat_title
, NULL
);
9630 menu
= g_list_prepend(menu
, act
);
9635 if (!session
->focus_uri
9636 || (session
->focus_uri
&& !session
->locked
))
9638 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
9639 act
= purple_menu_action_new(label
,
9640 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
9641 session
->chat_title
, NULL
);
9643 menu
= g_list_prepend(menu
, act
);
9647 } SIPE_SESSION_FOREACH_END
;
9649 act
= purple_menu_action_new(_("New chat"),
9650 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
9652 menu
= g_list_prepend(menu
, act
);
9654 if (sip
->csta
&& !sip
->csta
->line_status
) {
9657 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
9658 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
9660 gchar
*label
= g_strdup_printf(_("Work %s"),
9661 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9662 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9666 menu
= g_list_prepend(menu
, act
);
9670 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
9671 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
9673 gchar
*label
= g_strdup_printf(_("Mobile %s"),
9674 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9675 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9679 menu
= g_list_prepend(menu
, act
);
9683 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
9684 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
9686 gchar
*label
= g_strdup_printf(_("Home %s"),
9687 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9688 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9692 menu
= g_list_prepend(menu
, act
);
9696 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
9697 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
9699 gchar
*label
= g_strdup_printf(_("Other %s"),
9700 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9701 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9705 menu
= g_list_prepend(menu
, act
);
9709 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
9710 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
9712 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
9713 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
9714 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
9718 menu
= g_list_prepend(menu
, act
);
9722 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
9724 act
= purple_menu_action_new(_("Send email..."),
9725 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
9727 menu
= g_list_prepend(menu
, act
);
9730 gr_parent
= purple_buddy_get_group(buddy
);
9731 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
9732 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
9735 group
= (PurpleGroup
*)g_node
;
9736 if (group
== gr_parent
)
9739 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
9742 act
= purple_menu_action_new(purple_group_get_name(group
),
9743 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
9745 menu_groups
= g_list_prepend(menu_groups
, act
);
9747 menu_groups
= g_list_reverse(menu_groups
);
9749 act
= purple_menu_action_new(_("Copy to"),
9752 menu
= g_list_prepend(menu
, act
);
9753 menu
= g_list_reverse(menu
);
9760 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
9762 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9763 struct sip_session
*session
;
9765 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9766 sipe_conf_modify_conference_lock(sip
, session
, locked
);
9770 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
9772 purple_debug_info("sipe", "sipe_chat_menu_unlock_cb() called\n");
9773 sipe_conf_modify_lock(chat
, FALSE
);
9777 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
9779 purple_debug_info("sipe", "sipe_chat_menu_lock_cb() called\n");
9780 sipe_conf_modify_lock(chat
, TRUE
);
9784 sipe_chat_menu(PurpleChat
*chat
)
9786 PurpleMenuAction
*act
;
9787 PurpleConvChatBuddyFlags flags_us
;
9789 struct sipe_account_data
*sip
= chat
->account
->gc
->proto_data
;
9790 struct sip_session
*session
;
9793 session
= sipe_session_find_chat_by_title(sip
, (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
9794 if (!session
) return NULL
;
9796 self
= sip_uri_self(sip
);
9797 flags_us
= purple_conv_chat_user_get_flags(PURPLE_CONV_CHAT(session
->conv
), self
);
9799 if (session
->focus_uri
9800 && PURPLE_CBFLAGS_OP
== (flags_us
& PURPLE_CBFLAGS_OP
)) /* We are a conf OP */
9802 if (session
->locked
) {
9803 act
= purple_menu_action_new(_("Unlock"),
9804 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
9806 menu
= g_list_prepend(menu
, act
);
9808 act
= purple_menu_action_new(_("Lock"),
9809 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
9811 menu
= g_list_prepend(menu
, act
);
9815 menu
= g_list_reverse(menu
);
9822 sipe_blist_node_menu(PurpleBlistNode
*node
)
9824 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
9825 return sipe_buddy_menu((PurpleBuddy
*) node
);
9826 } else if(PURPLE_BLIST_NODE_IS_CHAT(node
)) {
9827 return sipe_chat_menu((PurpleChat
*)node
);
9834 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
9836 char *uri
= trans
->payload
->data
;
9838 PurpleNotifyUserInfo
*info
;
9839 PurpleBuddy
*pbuddy
= NULL
;
9840 struct sipe_buddy
*sbuddy
;
9841 const char *alias
= NULL
;
9842 char *device_name
= NULL
;
9843 char *server_alias
= NULL
;
9844 char *phone_number
= NULL
;
9847 char *first_name
= NULL
;
9848 char *last_name
= NULL
;
9850 if (!sip
) return FALSE
;
9852 purple_debug_info("sipe", "Fetching %s's user info for %s\n", uri
, sip
->username
);
9854 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
9855 alias
= purple_buddy_get_local_alias(pbuddy
);
9857 //will query buddy UA's capabilities and send answer to log
9858 sipe_options_request(sip
, uri
);
9860 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
9862 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
9865 info
= purple_notify_user_info_new();
9867 if (msg
->response
!= 200) {
9868 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
9870 xmlnode
*searchResults
;
9873 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
9874 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
9875 if (!searchResults
) {
9876 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
9877 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
9879 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
9880 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
9881 phone_number
= g_strdup(xmlnode_get_attrib(mrow
, "phone"));
9883 /* For 2007 system we will take this from ContactCard -
9884 * it has cleaner tel: URIs at least
9886 if (!sip
->ocs2007
) {
9887 char *tel_uri
= sip_to_tel_uri(phone_number
);
9888 /* trims its parameters, so call first */
9889 sipe_update_user_info(sip
, uri
, ALIAS_PROP
, server_alias
);
9890 sipe_update_user_info(sip
, uri
, EMAIL_PROP
, email
);
9891 sipe_update_user_info(sip
, uri
, PHONE_PROP
, tel_uri
);
9892 sipe_update_user_info(sip
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
9896 if (server_alias
&& strlen(server_alias
) > 0) {
9897 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9899 if ((value
= xmlnode_get_attrib(mrow
, "title")) && strlen(value
) > 0) {
9900 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
9902 if ((value
= xmlnode_get_attrib(mrow
, "office")) && strlen(value
) > 0) {
9903 purple_notify_user_info_add_pair(info
, _("Office"), value
);
9905 if (phone_number
&& strlen(phone_number
) > 0) {
9906 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
9908 if ((value
= xmlnode_get_attrib(mrow
, "company")) && strlen(value
) > 0) {
9909 purple_notify_user_info_add_pair(info
, _("Company"), value
);
9911 if ((value
= xmlnode_get_attrib(mrow
, "city")) && strlen(value
) > 0) {
9912 purple_notify_user_info_add_pair(info
, _("City"), value
);
9914 if ((value
= xmlnode_get_attrib(mrow
, "state")) && strlen(value
) > 0) {
9915 purple_notify_user_info_add_pair(info
, _("State"), value
);
9917 if ((value
= xmlnode_get_attrib(mrow
, "country")) && strlen(value
) > 0) {
9918 purple_notify_user_info_add_pair(info
, _("Country"), value
);
9920 if (email
&& strlen(email
) > 0) {
9921 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9925 xmlnode_free(searchResults
);
9928 purple_notify_user_info_add_section_break(info
);
9930 if (is_empty(server_alias
)) {
9931 g_free(server_alias
);
9932 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
9934 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
9938 /* present alias if it differs from server alias */
9939 if (alias
&& !sipe_strequal(alias
, server_alias
))
9941 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
9944 if (is_empty(email
)) {
9946 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
9948 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
9952 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
9954 purple_notify_user_info_add_pair(info
, _("Site"), site
);
9957 sipe_get_first_last_names(sip
, uri
, &first_name
, &last_name
);
9958 if (first_name
&& last_name
) {
9959 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
9961 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
9968 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
9971 /* show a buddy's user info in a nice dialog box */
9972 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
9973 uri
, /* buddy's URI */
9975 NULL
, /* callback called when dialog closed */
9976 NULL
); /* userdata for callback */
9978 g_free(phone_number
);
9979 g_free(server_alias
);
9981 g_free(device_name
);
9987 * AD search first, LDAP based
9989 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
9991 struct sipe_account_data
*sip
= gc
->proto_data
;
9992 gchar
*domain_uri
= sip_uri_from_name(sip
->sipdomain
);
9993 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
9994 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
9995 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
9997 payload
->destroy
= g_free
;
9998 payload
->data
= g_strdup(username
);
10000 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
10001 send_soap_request_with_cb(sip
, domain_uri
, body
,
10002 (TransCallback
) process_get_info_response
, payload
);
10003 g_free(domain_uri
);
10008 PurplePluginProtocolInfo prpl_info
=
10010 OPT_PROTO_CHAT_TOPIC
,
10011 NULL
, /* user_splits */
10012 NULL
, /* protocol_options */
10013 NO_BUDDY_ICONS
, /* icon_spec */
10014 sipe_list_icon
, /* list_icon */
10015 NULL
, /* list_emblems */
10016 sipe_status_text
, /* status_text */
10017 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
10018 sipe_status_types
, /* away_states */
10019 sipe_blist_node_menu
, /* blist_node_menu */
10020 NULL
, /* chat_info */
10021 NULL
, /* chat_info_defaults */
10022 sipe_login
, /* login */
10023 sipe_close
, /* close */
10024 sipe_im_send
, /* send_im */
10025 NULL
, /* set_info */ // TODO maybe
10026 sipe_send_typing
, /* send_typing */
10027 sipe_get_info
, /* get_info */
10028 sipe_set_status
, /* set_status */
10029 sipe_set_idle
, /* set_idle */
10030 NULL
, /* change_passwd */
10031 sipe_add_buddy
, /* add_buddy */
10032 NULL
, /* add_buddies */
10033 sipe_remove_buddy
, /* remove_buddy */
10034 NULL
, /* remove_buddies */
10035 sipe_add_permit
, /* add_permit */
10036 sipe_add_deny
, /* add_deny */
10037 sipe_add_deny
, /* rem_permit */
10038 sipe_add_permit
, /* rem_deny */
10039 dummy_permit_deny
, /* set_permit_deny */
10040 NULL
, /* join_chat */
10041 NULL
, /* reject_chat */
10042 NULL
, /* get_chat_name */
10043 sipe_chat_invite
, /* chat_invite */
10044 sipe_chat_leave
, /* chat_leave */
10045 NULL
, /* chat_whisper */
10046 sipe_chat_send
, /* chat_send */
10047 sipe_keep_alive
, /* keepalive */
10048 NULL
, /* register_user */
10049 NULL
, /* get_cb_info */ // deprecated
10050 NULL
, /* get_cb_away */ // deprecated
10051 sipe_alias_buddy
, /* alias_buddy */
10052 sipe_group_buddy
, /* group_buddy */
10053 sipe_rename_group
, /* rename_group */
10054 NULL
, /* buddy_free */
10055 sipe_convo_closed
, /* convo_closed */
10056 purple_normalize_nocase
, /* normalize */
10057 NULL
, /* set_buddy_icon */
10058 sipe_remove_group
, /* remove_group */
10059 NULL
, /* get_cb_real_name */ // TODO?
10060 NULL
, /* set_chat_topic */
10061 NULL
, /* find_blist_chat */
10062 NULL
, /* roomlist_get_list */
10063 NULL
, /* roomlist_cancel */
10064 NULL
, /* roomlist_expand_category */
10065 NULL
, /* can_receive_file */
10066 sipe_ft_send_file
, /* send_file */
10067 sipe_ft_new_xfer
, /* new_xfer */
10068 NULL
, /* offline_message */
10069 NULL
, /* whiteboard_prpl_ops */
10070 sipe_send_raw
, /* send_raw */
10071 NULL
, /* roomlist_room_serialize */
10072 NULL
, /* unregister_user */
10073 NULL
, /* send_attention */
10074 NULL
, /* get_attention_types */
10075 #if !PURPLE_VERSION_CHECK(2,5,0)
10076 /* Backward compatibility when compiling against 2.4.x API */
10077 (void (*)(void)) /* _purple_reserved4 */
10079 sizeof(PurplePluginProtocolInfo
), /* struct_size */
10080 #if PURPLE_VERSION_CHECK(2,5,0)
10081 sipe_get_account_text_table
, /* get_account_text_table */
10082 #if PURPLE_VERSION_CHECK(2,6,0)
10083 NULL
, /* initiate_media */
10084 NULL
, /* get_media_caps */
10090 PurplePluginInfo info
= {
10091 PURPLE_PLUGIN_MAGIC
,
10092 PURPLE_MAJOR_VERSION
,
10093 PURPLE_MINOR_VERSION
,
10094 PURPLE_PLUGIN_PROTOCOL
, /**< type */
10095 NULL
, /**< ui_requirement */
10097 NULL
, /**< dependencies */
10098 PURPLE_PRIORITY_DEFAULT
, /**< priority */
10099 "prpl-sipe", /**< id */
10100 "Office Communicator", /**< name */
10101 PACKAGE_VERSION
, /**< version */
10102 "Microsoft Office Communicator Protocol Plugin", /**< summary */
10103 "A plugin for the extended SIP/SIMPLE protocol used by " /**< description */
10104 "Microsoft Live/Office Communications Server (LCS2005/OCS2007+)", /**< description */
10105 "Anibal Avelar <avelar@gmail.com>, " /**< author */
10106 "Gabriel Burt <gburt@novell.com>, " /**< author */
10107 "Stefan Becker <stefan.becker@nokia.com>, " /**< author */
10108 "pier11 <pier11@operamail.com>", /**< author */
10109 PACKAGE_URL
, /**< homepage */
10110 sipe_plugin_load
, /**< load */
10111 sipe_plugin_unload
, /**< unload */
10112 sipe_plugin_destroy
, /**< destroy */
10113 NULL
, /**< ui_info */
10114 &prpl_info
, /**< extra_info */
10126 c-file-style: "bsd"
10127 indent-tabs-mode: t