6 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
7 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
8 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
9 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
12 * Thanks to Google's Summer of Code Program and the helpful mentors
15 * Session-based SIP MESSAGE documentation:
16 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #include <sys/socket.h>
35 #include <sys/ioctl.h>
36 #include <sys/types.h>
37 #include <netinet/in.h>
41 # define _(String) ((const char *) gettext (String))
43 # define _(String) ((const char *) (String))
44 #endif /* ENABLE_NLS */
49 #define _LIBC_INTERNAL_
62 #include "accountopt.h"
64 #include "conversation.h"
81 #endif /*USE_KERBEROS*/
84 #include "sipe-sign.h"
88 /* Keep in sync with sipe_transport_type! */
89 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
90 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
92 /* Status identifiers (see also: sipe_status_types()) */
93 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
94 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
95 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
96 /* PURPLE_STATUS_UNAVAILABLE: */
97 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
98 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
99 #define SIPE_STATUS_ID_ONPHONE "on-the-phone" /* On The Phone */
100 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
101 /* PURPLE_STATUS_AWAY: */
102 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
103 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
104 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
105 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
106 /* ??? PURPLE_STATUS_MOBILE */
107 /* ??? PURPLE_STATUS_TUNE */
109 static char *gentag()
111 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
114 static gchar
*get_epid(struct sipe_account_data
*sip
)
117 sip
->epid
= sipe_uuid_get_macaddr(purple_network_get_my_ip(-1));
119 return g_strdup(sip
->epid
);
122 static char *genbranch()
124 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
125 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
126 rand() & 0xFFFF, rand() & 0xFFFF);
129 static char *gencallid()
131 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
132 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
133 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
134 rand() & 0xFFFF, rand() & 0xFFFF);
137 static gchar
*find_tag(const gchar
*hdr
)
139 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
141 // In case it's at the end and there's no trailing ;
142 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
148 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
153 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
155 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
157 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
158 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
161 static void sipe_close(PurpleConnection
*gc
);
163 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, const char * buddy_name
);
164 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
);
165 static void send_presence_status(struct sipe_account_data
*sip
);
167 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
169 static void sipe_keep_alive_timeout(struct sipe_account_data
*sip
, const gchar
*hdr
)
171 gchar
*timeout
= sipmsg_find_part_of_header(hdr
, "timeout=", ";", NULL
);
172 if (timeout
!= NULL
) {
173 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
174 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
175 sip
->keepalive_timeout
);
180 static void sipe_keep_alive(PurpleConnection
*gc
)
182 struct sipe_account_data
*sip
= gc
->proto_data
;
183 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
184 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
185 gchar buf
[2] = {0, 0};
186 purple_debug_info("sipe", "sending keep alive\n");
187 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
189 time_t now
= time(NULL
);
190 if ((sip
->keepalive_timeout
> 0) &&
191 ((now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
192 #if PURPLE_VERSION_CHECK(2,4,0)
193 && ((now
- gc
->last_received
) >= sip
->keepalive_timeout
)
196 purple_debug_info("sipe", "sending keep alive %d\n",sip
->keepalive_timeout
);
197 sendout_pkt(gc
, "\r\n\r\n");
198 sip
->last_keepalive
= now
;
203 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
205 struct sip_connection
*ret
= NULL
;
206 GSList
*entry
= sip
->openconns
;
209 if (ret
->fd
== fd
) return ret
;
215 static void sipe_auth_free(struct sip_auth
*auth
)
219 g_free(auth
->opaque
);
223 g_free(auth
->target
);
225 g_free(auth
->digest_session_key
);
226 auth
->digest_session_key
= NULL
;
227 g_free(auth
->ntlm_key
);
228 auth
->ntlm_key
= NULL
;
229 auth
->type
= AUTH_TYPE_UNSET
;
234 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
236 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
238 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
242 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
244 struct sip_connection
*conn
= connection_find(sip
, fd
);
246 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
247 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
253 static void connection_free_all(struct sipe_account_data
*sip
)
255 struct sip_connection
*ret
= NULL
;
256 GSList
*entry
= sip
->openconns
;
259 connection_remove(sip
, ret
->fd
);
260 entry
= sip
->openconns
;
264 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
266 const gchar
*method
= msg
->method
;
267 const gchar
*target
= msg
->target
;
272 const char *authdomain
= sip
->authdomain
;
273 const char *authuser
= sip
->authuser
;
274 //const char *krb5_realm;
276 //gchar *krb5_token = NULL;
278 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
279 // and do error checking
281 // KRB realm should always be uppercase
282 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
284 if (sip
->realhostname
) {
285 host
= sip
->realhostname
;
286 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
287 host
= purple_account_get_string(sip
->account
, "proxy", "");
289 host
= sip
->sipdomain
;
292 /*gboolean new_auth = krb5_auth.gss_context == NULL;
294 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
297 if (new_auth || force_reauth) {
298 krb5_token = krb5_auth.base64_token;
301 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
302 krb5_token = krb5_auth.base64_token;*/
308 if (!authuser
|| strlen(authuser
) < 1) {
309 authuser
= sip
->username
;
312 if (auth
->type
== AUTH_TYPE_DIGEST
) { /* Digest */
313 sprintf(noncecount
, "%08d", auth
->nc
++);
314 response
= purple_cipher_http_digest_calculate_response(
315 "md5", method
, target
, NULL
, NULL
,
316 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
317 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
319 ret
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser
, auth
->realm
, auth
->nonce
, target
, noncecount
, response
);
322 } else if (auth
->type
== AUTH_TYPE_NTLM
) { /* NTLM */
323 // If we have a signature for the message, include that
324 if (msg
->signature
) {
325 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", crand=\"%s\", cnum=\"%s\", response=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, msg
->rand
, msg
->num
, msg
->signature
);
329 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
330 const gchar
*ntlm_key
;
332 #if GLIB_CHECK_VERSION(2,8,0)
333 const gchar
* hostname
= g_get_host_name();
335 static char hostname
[256];
336 int ret
= gethostname(hostname
, sizeof(hostname
));
337 hostname
[sizeof(hostname
) - 1] = '\0';
338 if (ret
== -1 || hostname
[0] == '\0') {
339 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Error when getting host name. Using \"localhost.\"\n");
341 strcpy(hostname
, "localhost");
344 /*const gchar * hostname = purple_get_host_name();*/
346 gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, hostname
, authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
347 auth
->ntlm_key
= (gchar
*)ntlm_key
;
348 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
353 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
355 } else if (auth
->type
== AUTH_TYPE_KERBEROS
) {
358 /*if (new_auth || force_reauth) {
359 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
361 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, krb5_token);
363 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
366 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
367 gchar * mic = "MICTODO";
368 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
369 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
370 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
371 //auth->opaque ? auth->opaque : "", auth->target);
372 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
377 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
380 sprintf(noncecount
, "%08d", auth
->nc
++);
381 response
= purple_cipher_http_digest_calculate_response(
382 "md5", method
, target
, NULL
, NULL
,
383 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
384 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
386 ret
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser
, auth
->realm
, auth
->nonce
, target
, noncecount
, response
);
391 static char *parse_attribute(const char *attrname
, const char *source
)
393 const char *tmp
, *tmp2
;
395 int len
= strlen(attrname
);
397 if (!strncmp(source
, attrname
, len
)) {
399 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
401 retval
= g_strndup(tmp
, tmp2
- tmp
);
403 retval
= g_strdup(tmp
);
409 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
412 const char *authuser
;
415 //const char *krb5_realm;
418 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
419 // and do error checking
421 // KRB realm should always be uppercase
422 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
424 if (sip->realhostname) {
425 host = sip->realhostname;
426 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
427 host = purple_account_get_string(sip->account, "proxy", "");
429 host = sip->sipdomain;
432 authuser
= sip
->authuser
;
434 if (!authuser
|| strlen(authuser
) < 1) {
435 authuser
= sip
->username
;
439 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
443 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
444 auth
->type
= AUTH_TYPE_NTLM
;
445 parts
= g_strsplit(hdr
+5, "\", ", 0);
448 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
449 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
451 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
454 if ((tmp
= parse_attribute("targetname=\"",
456 g_free(auth
->target
);
459 else if ((tmp
= parse_attribute("realm=\"",
464 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
465 g_free(auth
->opaque
);
472 if (!strstr(hdr
, "gssapi-data")) {
480 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
481 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
482 auth
->type
= AUTH_TYPE_KERBEROS
;
483 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
484 parts
= g_strsplit(hdr
+9, "\", ", 0);
487 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
488 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
489 /*if (krb5_auth.gss_context == NULL) {
490 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
492 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
495 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
496 g_free(auth
->target
);
498 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
501 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
502 g_free(auth
->opaque
);
512 auth
->type
= AUTH_TYPE_DIGEST
;
513 parts
= g_strsplit(hdr
, " ", 0);
515 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
519 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
527 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
529 g_free(auth
->digest_session_key
);
530 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
531 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
537 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
539 PurpleConnection
*gc
= data
;
540 struct sipe_account_data
*sip
= gc
->proto_data
;
544 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
546 if (max_write
== 0) {
547 if (sip
->tx_handler
!= 0){
548 purple_input_remove(sip
->tx_handler
);
554 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
556 if (written
< 0 && errno
== EAGAIN
)
558 else if (written
<= 0) {
559 /*TODO: do we really want to disconnect on a failure to write?*/
560 purple_connection_error(gc
, _("Could not write"));
564 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
567 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
569 PurpleConnection
*gc
= data
;
570 struct sipe_account_data
*sip
= gc
->proto_data
;
574 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
576 if (max_write
== 0) {
577 if (sip
->tx_handler
!= 0) {
578 purple_input_remove(sip
->tx_handler
);
584 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
586 if (written
< 0 && errno
== EAGAIN
)
588 else if (written
<= 0) {
589 /*TODO: do we really want to disconnect on a failure to write?*/
590 purple_connection_error(gc
, _("Could not write"));
594 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
597 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
599 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
601 PurpleConnection
*gc
= data
;
602 struct sipe_account_data
*sip
;
603 struct sip_connection
*conn
;
605 if (!PURPLE_CONNECTION_IS_VALID(gc
))
613 purple_connection_error(gc
, _("Could not connect"));
617 sip
= gc
->proto_data
;
619 sip
->connecting
= FALSE
;
620 sip
->last_keepalive
= time(NULL
);
622 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
624 /* If there is more to write now, we need to register a handler */
625 if (sip
->txbuf
->bufused
> 0)
626 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
628 conn
= connection_create(sip
, source
);
629 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
632 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
634 struct sipe_account_data
*sip
;
635 struct sip_connection
*conn
;
637 if (!PURPLE_CONNECTION_IS_VALID(gc
))
639 if (gsc
) purple_ssl_close(gsc
);
643 sip
= gc
->proto_data
;
646 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
647 sip
->connecting
= FALSE
;
648 sip
->last_keepalive
= time(NULL
);
650 conn
= connection_create(sip
, gsc
->fd
);
652 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
657 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
659 PurpleConnection
*gc
= data
;
660 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
661 if (sip
== NULL
) return;
663 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
665 /* If there is more to write now */
666 if (sip
->txbuf
->bufused
> 0) {
667 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
672 static void sendlater(PurpleConnection
*gc
, const char *buf
)
674 struct sipe_account_data
*sip
= gc
->proto_data
;
676 if (!sip
->connecting
) {
677 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
678 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
679 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
681 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
682 purple_connection_error(gc
, _("Couldn't create socket"));
685 sip
->connecting
= TRUE
;
688 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
689 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
691 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
694 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
696 struct sipe_account_data
*sip
= gc
->proto_data
;
697 time_t currtime
= time(NULL
);
698 int writelen
= strlen(buf
);
700 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
701 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
702 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
703 purple_debug_info("sipe", "could not send packet\n");
712 if (sip
->tx_handler
) {
717 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
719 ret
= write(sip
->fd
, buf
, writelen
);
723 if (ret
< 0 && errno
== EAGAIN
)
725 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
730 if (ret
< writelen
) {
731 if (!sip
->tx_handler
){
733 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
736 sip
->tx_handler
= purple_input_add(sip
->fd
,
737 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
742 /* XXX: is it OK to do this? You might get part of a request sent
743 with part of another. */
744 if (sip
->txbuf
->bufused
> 0)
745 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
747 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
753 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
755 sendout_pkt(gc
, buf
);
759 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
761 GSList
*tmp
= msg
->headers
;
764 GString
*outstr
= g_string_new("");
765 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
767 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
768 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
769 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
770 tmp
= g_slist_next(tmp
);
772 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
773 sendout_pkt(sip
->gc
, outstr
->str
);
774 g_string_free(outstr
, TRUE
);
777 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
780 if (sip
->registrar
.ntlm_key
) {
781 struct sipmsg_breakdown msgbd
;
782 gchar
*signature_input_str
;
784 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
785 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
786 sip
->registrar
.ntlm_num
++;
787 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
788 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
789 if (signature_input_str
!= NULL
) {
790 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
791 msg
->rand
= g_strdup(msgbd
.rand
);
792 msg
->num
= g_strdup(msgbd
.num
);
793 g_free(signature_input_str
);
795 sipmsg_breakdown_free(&msgbd
);
798 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
799 buf
= auth_header(sip
, &sip
->registrar
, msg
);
800 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
801 sipmsg_add_header(msg
, "Authorization", buf
);
803 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
806 } else if (!strcmp(method
,"SUBSCRIBE") || !strcmp(method
,"SERVICE") || !strcmp(method
,"MESSAGE") || !strcmp(method
,"INVITE") || !strcmp(method
, "ACK") || !strcmp(method
, "NOTIFY") || !strcmp(method
, "BYE") || !strcmp(method
, "INFO") || !strcmp(method
, "OPTIONS")) {
807 sip
->registrar
.nc
= 3;
808 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
810 buf
= auth_header(sip
, &sip
->registrar
, msg
);
811 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
814 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
818 static char *get_contact(struct sipe_account_data
*sip
)
820 return g_strdup(sip
->contact
);
825 static char *get_contact_service(struct sipe_account_data *sip)
827 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip->listenport, TRANSPORT_DESCRIPTOR, generateUUIDfromEPID(get_epid()));
828 //return g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->listenport, purple_network_get_my_ip(-1), TRANSPORT_DESCRIPTOR);
832 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
833 const char *text
, const char *body
)
837 GString
*outstr
= g_string_new("");
838 struct sipe_account_data
*sip
= gc
->proto_data
;
842 sipmsg_remove_header(msg
, "ms-user-data");
844 contact
= get_contact(sip
);
845 sipmsg_remove_header(msg
, "Contact");
846 sipmsg_add_header(msg
, "Contact", contact
);
849 /* When sending the acknowlegements and errors, the content length from the original
850 message is still here, but there is no body; we need to make sure we're sending the
851 correct content length */
852 sipmsg_remove_header(msg
, "Content-Length");
855 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
856 sipmsg_add_header(msg
, "Content-Length", len
);
858 sipmsg_remove_header(msg
, "Content-Type");
859 sipmsg_add_header(msg
, "Content-Length", "0");
862 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
863 //gchar * mic = "MICTODO";
864 msg
->response
= code
;
866 sipmsg_remove_header(msg
, "Authentication-Info");
867 sign_outgoing_message(msg
, sip
, msg
->method
);
869 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
872 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
873 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
875 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
876 tmp
= g_slist_next(tmp
);
878 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
879 sendout_pkt(gc
, outstr
->str
);
880 g_string_free(outstr
, TRUE
);
883 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
885 if (trans
->msg
) sipmsg_free(trans
->msg
);
886 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
890 static struct transaction
*
891 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
893 struct transaction
*trans
= g_new0(struct transaction
, 1);
894 trans
->time
= time(NULL
);
895 trans
->msg
= (struct sipmsg
*)msg
;
896 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
897 trans
->callback
= callback
;
898 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
902 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
904 struct transaction
*trans
;
905 GSList
*transactions
= sip
->transactions
;
906 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
908 while (transactions
) {
909 trans
= transactions
->data
;
910 if (!strcmp(trans
->cseq
, cseq
)) {
913 transactions
= transactions
->next
;
919 static struct transaction
*
920 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
921 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
922 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
924 struct sipe_account_data
*sip
= gc
->proto_data
;
925 const char *addh
= "";
928 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
929 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
930 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
931 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
932 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
933 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
934 gchar
*route
= strdup("");
935 gchar
*epid
= get_epid(sip
); // TODO generate one per account/login
936 struct transaction
*trans
;
938 if (dialog
&& dialog
->routes
)
940 GSList
*iter
= dialog
->routes
;
945 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
947 iter
= g_slist_next(iter
);
951 if (!ourtag
&& !dialog
) {
955 if (!strcmp(method
, "REGISTER")) {
956 if (sip
->regcallid
) {
958 callid
= g_strdup(sip
->regcallid
);
960 sip
->regcallid
= g_strdup(callid
);
964 if (addheaders
) addh
= addheaders
;
966 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
967 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
968 "From: <sip:%s>%s%s;epid=%s\r\n"
969 "To: <%s>%s%s%s%s\r\n"
970 "Max-Forwards: 70\r\n"
975 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
977 dialog
&& dialog
->request
? dialog
->request
: url
,
978 TRANSPORT_DESCRIPTOR
,
979 purple_network_get_my_ip(-1),
981 branch
? ";branch=" : "",
982 branch
? branch
: "",
984 ourtag
? ";tag=" : "",
985 ourtag
? ourtag
: "",
988 theirtag
? ";tag=" : "",
989 theirtag
? theirtag
: "",
990 theirepid
? ";epid=" : "",
991 theirepid
? theirepid
: "",
992 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
998 body
? strlen(body
) : 0,
1002 //printf ("parsing msg buf:\n%s\n\n", buf);
1003 msg
= sipmsg_parse_msg(buf
);
1014 sign_outgoing_message (msg
, sip
, method
);
1016 buf
= sipmsg_to_string (msg
);
1018 /* add to ongoing transactions */
1019 trans
= transactions_add_buf(sip
, msg
, tc
);
1020 sendout_pkt(gc
, buf
);
1026 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
1028 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
1029 gchar
*contact
= get_contact(sip
);
1030 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1031 "Content-Type: application/SOAP+xml\r\n",contact
);
1033 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1034 tr
->payload
= payload
;
1041 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1043 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
1046 static char *get_contact_register(struct sipe_account_data
*sip
)
1048 char *epid
= get_epid(sip
);
1049 char *uuid
= generateUUIDfromEPID(epid
);
1050 char *buf
= g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip
->listenport
, TRANSPORT_DESCRIPTOR
, uuid
);
1056 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1058 char *expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1059 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1060 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1061 char *contact
= get_contact_register(sip
);
1062 char *hdr
= g_strdup_printf("Contact: %s\r\n"
1063 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1064 "Event: registration\r\n"
1065 "Allow-Events: presence\r\n"
1066 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1067 "%s", contact
, expires
);
1071 sip
->registerstatus
= 1;
1073 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1074 process_register_response
);
1081 static void do_register_cb(struct sipe_account_data
*sip
)
1083 do_register_exp(sip
, -1);
1084 sip
->reregister_set
= FALSE
;
1087 static void do_register(struct sipe_account_data
*sip
)
1089 do_register_exp(sip
, -1);
1093 * Returns URI from provided To or From header.
1095 * Needs to g_free() after use.
1097 * @return URI with sip: prefix
1099 static gchar
*parse_from(const gchar
*hdr
)
1102 const gchar
*tmp
, *tmp2
= hdr
;
1104 if (!hdr
) return NULL
;
1105 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1106 tmp
= strchr(hdr
, '<');
1108 /* i hate the different SIP UA behaviours... */
1109 if (tmp
) { /* sip address in <...> */
1111 tmp
= strchr(tmp2
, '>');
1113 from
= g_strndup(tmp2
, tmp
- tmp2
);
1115 purple_debug_info("sipe", "found < without > in From\n");
1119 tmp
= strchr(tmp2
, ';');
1121 from
= g_strndup(tmp2
, tmp
- tmp2
);
1123 from
= g_strdup(tmp2
);
1126 purple_debug_info("sipe", "got %s\n", from
);
1130 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1133 xmlnode
* node
= NULL
;
1136 va_start(args
, parent
);
1137 while ((name
= va_arg(args
, const char *)) != NULL
) {
1138 node
= xmlnode_get_child(parent
, name
);
1139 if (node
== NULL
) return NULL
;
1149 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1151 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1152 send_soap_request(sip
, body
);
1157 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1160 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1162 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1165 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1169 void sipe_auth_user_cb(void * data
)
1171 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1174 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1179 void sipe_deny_user_cb(void * data
)
1181 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1184 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1189 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1191 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1192 sipe_contact_allow_deny(sip
, name
, TRUE
);
1196 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1198 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1199 sipe_contact_allow_deny(sip
, name
, FALSE
);
1203 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1205 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1206 sipe_contact_set_acl(sip, name, "");
1210 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1214 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1215 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1217 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1219 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1220 if (!watchers
) return;
1222 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1223 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1224 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1225 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1227 // TODO pull out optional displayName to pass as alias
1229 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1230 job
->who
= remote_user
;
1232 purple_account_request_authorization(
1246 xmlnode_free(watchers
);
1251 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1253 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1254 if (!purple_group
) {
1255 purple_group
= purple_group_new(group
->name
);
1256 purple_blist_add_group(purple_group
, NULL
);
1260 group
->purple_group
= purple_group
;
1261 sip
->groups
= g_slist_append(sip
->groups
, group
);
1262 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1264 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1268 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1270 struct sipe_group
*group
;
1276 entry
= sip
->groups
;
1278 group
= entry
->data
;
1279 if (group
->id
== id
) {
1282 entry
= entry
->next
;
1287 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1289 struct sipe_group
*group
;
1295 entry
= sip
->groups
;
1297 group
= entry
->data
;
1298 if (!strcmp(group
->name
, name
)) {
1301 entry
= entry
->next
;
1307 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1310 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1311 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1312 send_soap_request(sip
, body
);
1314 g_free(group
->name
);
1315 group
->name
= g_strdup(name
);
1319 * Only appends if no such value already stored.
1322 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1323 GSList
* res
= list
;
1324 if (!g_slist_find_custom(list
, data
, func
)) {
1325 res
= g_slist_insert_sorted(list
, data
, func
);
1331 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1332 return group1
->id
- group2
->id
;
1336 * Returns string like "2 4 7 8" - group ids buddy belong to.
1339 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1342 //creating array from GList, converting int to gchar*
1343 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1344 GSList
*entry
= buddy
->groups
;
1346 struct sipe_group
* group
= entry
->data
;
1347 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1348 entry
= entry
->next
;
1352 res
= g_strjoinv(" ", ids_arr
);
1353 g_strfreev(ids_arr
);
1358 * Sends buddy update to server
1361 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1363 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1364 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1366 if (buddy
&& purple_buddy
) {
1367 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1369 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1370 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1372 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1373 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1375 send_soap_request(sip
, body
);
1381 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1383 if (msg
->response
== 200) {
1384 struct sipe_group
*group
;
1385 struct group_user_context
*ctx
= (struct group_user_context
*)tc
->payload
;
1389 struct sipe_buddy
*buddy
;
1391 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1397 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1404 group_id
= xmlnode_get_data(node
);
1411 group
= g_new0(struct sipe_group
, 1);
1412 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1414 group
->name
= ctx
->group_name
;
1416 sipe_group_add(sip
, group
);
1418 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1420 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1423 sipe_group_set_user(sip
, ctx
->user_name
);
1432 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1434 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1436 ctx
->group_name
= g_strdup(name
);
1437 ctx
->user_name
= g_strdup(who
);
1439 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1440 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1446 * Should return FALSE if repetitive action is not needed
1448 gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1451 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1452 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1453 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1454 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1455 ret
= sched_action
->repetitive
;
1456 g_free(sched_action
->payload
);
1457 g_free(sched_action
->name
);
1458 g_free(sched_action
);
1463 * Do schedule action for execution in the future.
1464 * Non repetitive execution.
1466 * @param name of action (will be copied)
1467 * @param timeout in seconds
1468 * @action callback function
1469 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1471 void sipe_schedule_action(gchar
*name
, int timeout
, Action action
, struct sipe_account_data
*sip
, void * payload
)
1473 struct scheduled_action
*sched_action
;
1475 purple_debug_info("sipe","scheduling action %s timeout:%d\n", name
, timeout
);
1476 sched_action
= g_new0(struct scheduled_action
, 1);
1477 sched_action
->repetitive
= FALSE
;
1478 sched_action
->name
= g_strdup(name
);
1479 sched_action
->action
= action
;
1480 sched_action
->sip
= sip
;
1481 sched_action
->payload
= payload
;
1482 sched_action
->timeout_handler
= purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1483 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1484 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1488 * Kills action timer effectively cancelling
1491 * @param name of action
1493 void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, gchar
*name
)
1497 if (!sip
->timeouts
|| !name
) return;
1499 entry
= sip
->timeouts
;
1501 struct scheduled_action
*sched_action
= entry
->data
;
1502 if(!strcmp(sched_action
->name
, name
)) {
1503 GSList
*to_delete
= entry
;
1504 entry
= entry
->next
;
1505 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1506 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1507 purple_timeout_remove(sched_action
->timeout_handler
);
1508 g_free(sched_action
->payload
);
1509 g_free(sched_action
->name
);
1510 g_free(sched_action
);
1512 entry
= entry
->next
;
1517 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1519 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1521 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1523 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1528 static void sipe_subscribe_resource_uri(const char *name
, gpointer value
, gchar
**resources_uri
)
1530 gchar
*tmp
= *resources_uri
;
1531 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1535 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
1537 gchar
*tmp
= *resources_uri
;
1538 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"><context/></resource>\n", tmp
, name
);
1543 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1544 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1545 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1546 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1547 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1550 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
){
1551 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1552 gchar
*contact
= get_contact(sip
);
1555 gchar
*resources_uri
= g_strdup("");
1556 gchar
*require
= "";
1558 gchar
*autoextend
= "";
1559 gchar
*content_type
;
1562 if (sip
->msrtc_event_categories
) {
1563 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
1564 require
= ", categoryList";
1565 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1566 content_type
= "application/msrtc-adrl-categorylist+xml";
1567 content
= g_strdup_printf(
1568 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1569 "<action name=\"subscribe\" id=\"63792024\">\n"
1570 "<adhocList>\n%s</adhocList>\n"
1571 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1572 "<category name=\"note\"/>\n"
1573 "<category name=\"state\"/>\n"
1576 "</batchSub>", sip
->username
, resources_uri
);
1578 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1579 autoextend
= "Supported: com.microsoft.autoextend\r\n";
1580 content_type
= "application/adrl+xml";
1581 content
= g_strdup_printf(
1582 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1583 "<create xmlns=\"\">\n%s</create>\n"
1584 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1586 g_free(resources_uri
);
1588 request
= g_strdup_printf(
1589 "Require: adhoclist%s\r\n"
1590 "Supported: eventlist\r\n"
1591 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1592 "Supported: ms-piggyback-first-notify\r\n"
1593 "%sSupported: ms-benotify\r\n"
1594 "Proxy-Require: ms-benotify\r\n"
1595 "Event: presence\r\n"
1596 "Content-Type: %s\r\n"
1597 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
1600 /* subscribe to buddy presence */
1601 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1602 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1610 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1611 * The user sends a single SUBSCRIBE request to the subscribed contact.
1612 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1616 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, const char * buddy_name
)
1618 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1619 gchar
*tmp
= get_contact(sip
);
1622 request
= g_strdup_printf(
1623 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1624 "Supported: ms-piggyback-first-notify\r\n"
1625 "Supported: com.microsoft.autoextend\r\n"
1626 "Supported: ms-benotify\r\n"
1627 "Proxy-Require: ms-benotify\r\n"
1628 "Event: presence\r\n"
1629 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1630 "Contact: %s\r\n", tmp
);
1632 content
= g_strdup_printf(
1633 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1634 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1635 "<resource uri=\"%s\"/>\n"
1637 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1638 "<category name=\"note\"/>\n"
1639 "<category name=\"state\"/>\n"
1642 "</batchSub>", sip
->username
, to
1647 /* subscribe to buddy presence */
1648 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1655 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1657 if (!purple_status_is_active(status
))
1661 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1664 g_free(sip
->status
);
1665 sip
->status
= g_strdup(purple_status_get_id(status
));
1666 send_presence_status(sip
);
1672 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1674 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1675 sipe_group_set_user(sip
, name
);
1679 sipe_group_buddy(PurpleConnection
*gc
,
1681 const char *old_group_name
,
1682 const char *new_group_name
)
1684 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1685 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1686 struct sipe_group
* old_group
= NULL
;
1687 struct sipe_group
* new_group
;
1689 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1690 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1692 if(!buddy
) { // buddy not in roaming list
1696 if (old_group_name
) {
1697 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1699 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1702 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1703 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1707 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1709 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1710 sipe_group_set_user(sip
, who
);
1714 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1716 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1717 struct sipe_buddy
*b
;
1719 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1721 // Prepend sip: if needed
1722 if (strncmp("sip:", buddy
->name
, 4)) {
1723 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1724 purple_blist_rename_buddy(buddy
, buf
);
1728 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1729 b
= g_new0(struct sipe_buddy
, 1);
1730 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1731 b
->name
= g_strdup(buddy
->name
);
1732 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1733 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1734 sipe_subscribe_presence_single(sip
, b
->name
); //@TODO should go to callback
1736 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1740 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1742 g_free(buddy
->name
);
1743 g_free(buddy
->annotation
);
1744 g_free(buddy
->device_name
);
1745 g_slist_free(buddy
->groups
);
1750 * Unassociates buddy from group first.
1751 * Then see if no groups left, removes buddy completely.
1752 * Otherwise updates buddy groups on server.
1754 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1756 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1757 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1758 struct sipe_group
*g
= NULL
;
1760 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1765 g
= sipe_group_find_by_name(sip
, group
->name
);
1769 b
->groups
= g_slist_remove(b
->groups
, g
);
1770 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1773 if (g_slist_length(b
->groups
) < 1) {
1774 gchar
*action_name
= g_strdup_printf("<%s><%s>", "presence", buddy
->name
);
1775 sipe_cancel_scheduled_action(sip
, action_name
);
1776 g_free(action_name
);
1778 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1781 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1782 send_soap_request(sip
, body
);
1788 //updates groups on server
1789 sipe_group_set_user(sip
, b
->name
);
1795 sipe_rename_group(PurpleConnection
*gc
,
1796 const char *old_name
,
1798 GList
*moved_buddies
)
1800 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1801 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1803 sipe_group_rename(sip
, s_group
, group
->name
);
1805 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1810 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1812 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1813 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1816 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1817 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1818 send_soap_request(sip
, body
);
1821 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1822 g_free(s_group
->name
);
1825 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1829 static GList
*sipe_status_types(PurpleAccount
*acc
)
1831 PurpleStatusType
*type
;
1832 GList
*types
= NULL
;
1835 type
= purple_status_type_new_with_attrs(
1836 PURPLE_STATUS_AVAILABLE
, NULL
, _("Online"), TRUE
, TRUE
, FALSE
,
1837 // Translators: noun
1838 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1840 types
= g_list_append(types
, type
);
1843 type
= purple_status_type_new_with_attrs(
1844 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_BUSY
, _("Busy"), TRUE
, TRUE
, FALSE
,
1845 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1847 types
= g_list_append(types
, type
);
1849 // Do Not Disturb (not user settable)
1850 type
= purple_status_type_new_with_attrs(
1851 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_DND
, _("Do Not Disturb"), TRUE
, FALSE
, FALSE
,
1852 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1854 types
= g_list_append(types
, type
);
1857 type
= purple_status_type_new_with_attrs(
1858 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_BRB
, _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1859 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1861 types
= g_list_append(types
, type
);
1864 type
= purple_status_type_new_with_attrs(
1865 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1866 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1868 types
= g_list_append(types
, type
);
1871 type
= purple_status_type_new_with_attrs(
1872 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_ONPHONE
, _("On The Phone"), TRUE
, TRUE
, FALSE
,
1873 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1875 types
= g_list_append(types
, type
);
1878 type
= purple_status_type_new_with_attrs(
1879 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_LUNCH
, _("Out To Lunch"), TRUE
, TRUE
, FALSE
,
1880 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1882 types
= g_list_append(types
, type
);
1885 type
= purple_status_type_new_full(
1886 PURPLE_STATUS_INVISIBLE
, NULL
, _("Appear Offline"), TRUE
, TRUE
, FALSE
);
1887 types
= g_list_append(types
, type
);
1890 type
= purple_status_type_new_full(
1891 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1892 types
= g_list_append(types
, type
);
1898 * A callback for g_hash_table_foreach
1900 static void sipe_buddy_subscribe_cb(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1902 sipe_subscribe_presence_single(sip
, buddy
->name
);
1906 * Removes entries from purple buddy list
1907 * that does not correspond ones in the roaming contact list.
1909 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1910 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1911 GSList
*entry
= buddies
;
1912 struct sipe_buddy
*buddy
;
1916 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1917 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1920 g
= purple_buddy_get_group(b
);
1921 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1923 gboolean in_sipe_groups
= FALSE
;
1924 GSList
*entry2
= buddy
->groups
;
1926 struct sipe_group
*group
= entry2
->data
;
1927 if (!strcmp(group
->name
, g
->name
)) {
1928 in_sipe_groups
= TRUE
;
1931 entry2
= entry2
->next
;
1933 if(!in_sipe_groups
) {
1934 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1935 purple_blist_remove_buddy(b
);
1938 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1939 purple_blist_remove_buddy(b
);
1941 entry
= entry
->next
;
1945 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1947 int len
= msg
->bodylen
;
1949 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1952 const gchar
*contacts_delta
;
1953 xmlnode
*group_node
;
1954 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1958 /* Convert the contact from XML to Purple Buddies */
1959 isc
= xmlnode_from_str(msg
->body
, len
);
1964 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
1965 if (contacts_delta
) {
1966 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1970 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1971 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1972 const char *name
= xmlnode_get_attrib(group_node
, "name");
1974 if (!strncmp(name
, "~", 1)) {
1976 name
= "Other Contacts";
1978 group
->name
= g_strdup(name
);
1979 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1981 sipe_group_add(sip
, group
);
1984 // Make sure we have at least one group
1985 if (g_slist_length(sip
->groups
) == 0) {
1986 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1987 PurpleGroup
*purple_group
;
1989 group
->name
= g_strdup("Other Contacts");
1991 purple_group
= purple_group_new(group
->name
);
1992 purple_blist_add_group(purple_group
, NULL
);
1993 sip
->groups
= g_slist_append(sip
->groups
, group
);
1996 /* Parse contacts */
1997 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1998 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1999 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
2000 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
2001 gchar
* buddy_name
= g_strdup_printf("sip:%s", uri
);
2002 gchar
**item_groups
;
2003 struct sipe_group
*group
= NULL
;
2004 struct sipe_buddy
*buddy
= NULL
;
2007 // assign to group Other Contacts if nothing else received
2008 if(!groups
|| !strcmp("", groups
) ) {
2009 group
= sipe_group_find_by_name(sip
, "Other Contacts");
2010 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
2013 item_groups
= g_strsplit(groups
, " ", 0);
2015 while (item_groups
[i
]) {
2016 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2018 // If couldn't find the right group for this contact, just put them in the first group we have
2019 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2020 group
= sip
->groups
->data
;
2023 if (group
!= NULL
) {
2024 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2026 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2027 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2030 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2031 if (name
!= NULL
&& strlen(name
) != 0) {
2032 purple_blist_alias_buddy(b
, name
);
2037 buddy
= g_new0(struct sipe_buddy
, 1);
2038 buddy
->name
= g_strdup(b
->name
);
2039 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2042 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2044 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2046 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2051 } // while, contact groups
2052 g_strfreev(item_groups
);
2062 sipe_cleanup_local_blist(sip
);
2064 //subscribe to buddies
2065 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2066 //if(sip->msrtc_event_categories){
2067 sipe_subscribe_presence_batched(sip
);
2069 //g_hash_table_foreach(sip->buddies, (GHFunc)sipe_buddy_subscribe_cb, (gpointer)sip);
2071 sip
->subscribed_buddies
= TRUE
;
2078 * Subscribe roaming contacts
2080 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2082 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2083 gchar
*tmp
= get_contact(sip
);
2084 gchar
*hdr
= g_strdup_printf(
2085 "Event: vnd-microsoft-roaming-contacts\r\n"
2086 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2087 "Supported: com.microsoft.autoextend\r\n"
2088 "Supported: ms-benotify\r\n"
2089 "Proxy-Require: ms-benotify\r\n"
2090 "Supported: ms-piggyback-first-notify\r\n"
2091 "Contact: %s\r\n", tmp
);
2094 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2099 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2101 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2102 gchar
*tmp
= get_contact(sip
);
2103 gchar
*hdr
= g_strdup_printf(
2104 "Event: presence.wpending\r\n"
2105 "Accept: text/xml+msrtc.wpending\r\n"
2106 "Supported: com.microsoft.autoextend\r\n"
2107 "Supported: ms-benotify\r\n"
2108 "Proxy-Require: ms-benotify\r\n"
2109 "Supported: ms-piggyback-first-notify\r\n"
2110 "Contact: %s\r\n", tmp
);
2113 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2119 * Fires on deregistration event initiated by server.
2120 * [MS-SIPREGE] SIP extension.
2125 // Content-Type: text/registration-event
2126 // subscription-state: terminated;expires=0
2127 // ms-diagnostics-public: 4141;reason="User disabled"
2129 // deregistered;event=rejected
2131 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2133 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2134 gchar
*event
= NULL
;
2135 gchar
*reason
= NULL
;
2136 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2138 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2139 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2141 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2142 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2143 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2144 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2146 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2150 if (warning
!= NULL
) {
2151 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2152 } else { // for LCS2005
2154 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2155 error_id
= 4140; // [MS-SIPREGE]
2156 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2157 reason
= g_strdup(_("You have been signed off because you've signed in at another location"));
2158 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2160 reason
= g_strdup(_("User disabled")); // [MS-OCER]
2161 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2163 reason
= g_strdup(_("User moved")); // [MS-OCER]
2167 warning
= g_strdup_printf(_("Unregistered by Server: %s."), reason
? reason
: _("no reason given"));
2170 sip
->gc
->wants_to_die
= TRUE
;
2171 purple_connection_error(sip
->gc
, warning
);
2176 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2178 const gchar
*contacts_delta
;
2181 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2187 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2190 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2199 * When we receive some self (BE) NOTIFY with a new subscriber
2200 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2204 static void sipe_process_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2210 char *display_name
= NULL
;
2211 PurpleBuddy
*pbuddy
;
2216 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2218 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2221 contact
= get_contact(sip
);
2222 to
= g_strdup_printf("sip:%s", sip
->username
);
2224 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2226 const char *acknowledged
;
2230 user
= xmlnode_get_attrib(node
, "user");
2231 if (!user
) continue;
2232 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2233 uri_user
= g_strdup_printf("sip:%s", user
);
2234 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri_user
);
2236 alias
= purple_buddy_get_local_alias(pbuddy
);
2237 uri_alias
= g_strdup_printf("sip:%s", alias
);
2238 display_name
= g_strdup(xmlnode_get_attrib(node
, "displayName"));
2239 if (display_name
&& !g_ascii_strcasecmp(uri_user
, uri_alias
)) { // 'bad' alias
2240 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri_user
, display_name
);
2241 purple_blist_alias_buddy(pbuddy
, display_name
);
2247 acknowledged
= xmlnode_get_attrib(node
, "acknowledged");
2248 if(!g_ascii_strcasecmp(acknowledged
,"false")){
2249 purple_debug_info("sipe", "sipe_process_roaming_self: user added you %s\n", user
);
2250 hdr
= g_strdup_printf(
2252 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2254 body
= g_strdup_printf(
2255 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2256 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2257 "</setSubscribers>", user
);
2259 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2270 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2272 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2273 gchar
*tmp
= get_contact(sip
);
2274 gchar
*hdr
= g_strdup_printf(
2275 "Event: vnd-microsoft-roaming-ACL\r\n"
2276 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2277 "Supported: com.microsoft.autoextend\r\n"
2278 "Supported: ms-benotify\r\n"
2279 "Proxy-Require: ms-benotify\r\n"
2280 "Supported: ms-piggyback-first-notify\r\n"
2281 "Contact: %s\r\n", tmp
);
2284 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2290 * To request for presence information about the user, access level settings that have already been configured by the user
2291 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2292 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2295 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2297 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2298 gchar
*tmp
= get_contact(sip
);
2299 gchar
*hdr
= g_strdup_printf(
2300 "Event: vnd-microsoft-roaming-self\r\n"
2301 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2302 "Supported: ms-benotify\r\n"
2303 "Proxy-Require: ms-benotify\r\n"
2304 "Supported: ms-piggyback-first-notify\r\n"
2306 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2308 gchar
*body
=g_strdup(
2309 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2310 "<roaming type=\"categories\"/>"
2311 "<roaming type=\"containers\"/>"
2312 "<roaming type=\"subscribers\"/></roamingList>");
2315 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2324 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2326 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2327 gchar
*tmp
= get_contact(sip
);
2328 gchar
*hdr
= g_strdup_printf(
2329 "Event: vnd-microsoft-provisioning\r\n"
2330 "Accept: application/vnd-microsoft-roaming-provisioning+xml\r\n"
2331 "Supported: com.microsoft.autoextend\r\n"
2332 "Supported: ms-benotify\r\n"
2333 "Proxy-Require: ms-benotify\r\n"
2334 "Supported: ms-piggyback-first-notify\r\n"
2336 "Contact: %s\r\n", tmp
);
2339 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, NULL
, NULL
, process_subscribe_response
);
2344 /** Subscription for provisioning information to help with initial
2345 * configuration. This subscription is a one-time query (denoted by the Expires header,
2346 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2347 * configuration, meeting policies, and policy settings that Communicator must enforce.
2348 * TODO: for what we need this information.
2351 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2353 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2354 gchar
*tmp
= get_contact(sip
);
2355 gchar
*hdr
= g_strdup_printf(
2356 "Event: vnd-microsoft-provisioning-v2\r\n"
2357 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2358 "Supported: com.microsoft.autoextend\r\n"
2359 "Supported: ms-benotify\r\n"
2360 "Proxy-Require: ms-benotify\r\n"
2361 "Supported: ms-piggyback-first-notify\r\n"
2364 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2365 gchar
*body
= g_strdup(
2366 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2367 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2368 "<provisioningGroup name=\"ucPolicy\"/>"
2369 "</provisioningGroupList>");
2372 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2378 /* IM Session (INVITE and MESSAGE methods) */
2380 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
2382 struct sip_im_session
*session
;
2384 if (sip
== NULL
|| who
== NULL
) {
2388 entry
= sip
->im_sessions
;
2390 session
= entry
->data
;
2391 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
2394 entry
= entry
->next
;
2399 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
2401 struct sip_im_session
*session
= find_im_session(sip
, who
);
2403 session
= g_new0(struct sip_im_session
, 1);
2404 session
->with
= g_strdup(who
);
2405 session
->unconfirmed_messages
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2406 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2411 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2413 struct sip_dialog
*dialog
= session
->dialog
;
2416 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
2419 entry
= dialog
->routes
;
2421 g_free(entry
->data
);
2422 entry
= g_slist_remove(entry
, entry
->data
);
2424 entry
= dialog
->supported
;
2426 g_free(entry
->data
);
2427 entry
= g_slist_remove(entry
, entry
->data
);
2429 g_free(dialog
->callid
);
2430 g_free(dialog
->ourtag
);
2431 g_free(dialog
->theirtag
);
2432 g_free(dialog
->theirepid
);
2433 g_free(dialog
->request
);
2435 g_free(session
->dialog
);
2437 entry
= session
->outgoing_message_queue
;
2439 g_free(entry
->data
);
2440 entry
= g_slist_remove(entry
, entry
->data
);
2443 g_hash_table_destroy(session
->unconfirmed_messages
);
2445 g_free(session
->with
);
2450 process_options_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2452 gboolean ret
= TRUE
;
2454 if (msg
->response
!= 200) {
2455 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2459 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2465 * Asks UA/proxy about its capabilities.
2467 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2469 gchar
*to
= strstr(who
, "sip:") ? g_strdup(who
) : g_strdup_printf("sip:%s", who
);
2470 gchar
*contact
= get_contact(sip
);
2472 request
= g_strdup_printf(
2473 "Accept: application/sdp\r\n"
2474 "Contact: %s\r\n", contact
);
2478 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2484 static void sipe_present_message_undelivered_err(gchar
*with
, struct sipe_account_data
*sip
, gchar
*message
)
2486 char *msg
, *msg_tmp
;
2487 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2488 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2490 msg_tmp
= g_strdup_printf( _("The following message could not be delivered to all recipients, "\
2491 "possibly because one or more persons are offline:\n%s") ,
2493 purple_conv_present_error(with
, sip
->account
, msg_tmp
);
2498 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
);
2501 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2503 gboolean ret
= TRUE
;
2504 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2505 struct sip_im_session
* session
= find_im_session(sip
, with
);
2506 struct sip_dialog
*dialog
;
2512 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2517 dialog
= session
->dialog
;
2519 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2524 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2525 key
= g_strdup_printf("<%s><%d><MESSAGE>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
));
2527 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2529 if (msg
->response
!= 200) {
2530 purple_debug_info("sipe", "process_message_response: MESSAGE response not 200\n");
2532 sipe_present_message_undelivered_err(with
, sip
, message
);
2533 im_session_destroy(sip
, session
);
2536 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2537 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
2538 key
, g_hash_table_size(session
->unconfirmed_messages
));
2544 sipe_im_process_queue(sip
, session
);
2548 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
2558 if (strncmp("sip:", session
->with
, 4)) {
2559 fullto
= g_strdup_printf("sip:%s", session
->with
);
2561 fullto
= g_strdup(session
->with
);
2564 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2565 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2567 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2570 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2573 msgr
= g_strdup("");
2576 tmp
= get_contact(sip
);
2577 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2578 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2579 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
2580 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n",
2585 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msgtext
, session
->dialog
, process_message_response
);
2593 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
2595 GSList
*entry
= session
->outgoing_message_queue
;
2597 if (session
->outgoing_invite
) return; //do not send messages until INVITE responded.
2600 char *key
= g_strdup_printf("<%s><%d><MESSAGE>", session
->dialog
->callid
, (session
->dialog
->cseq
) + 1);
2601 char *queued_msg
= entry
->data
;
2602 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
2603 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
2604 key
, g_hash_table_size(session
->unconfirmed_messages
));
2606 sipe_send_message(sip
, session
, queued_msg
);
2607 entry
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2613 sipe_get_route_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2615 GSList
*hdr
= msg
->headers
;
2616 struct siphdrelement
*elem
;
2622 if(!g_ascii_strcasecmp(elem
->name
, "Record-Route"))
2624 gchar
*route
= sipmsg_find_part_of_header(elem
->value
, "<", ">", NULL
);
2625 dialog
->routes
= g_slist_append(dialog
->routes
, route
);
2627 hdr
= g_slist_next(hdr
);
2632 dialog
->routes
= g_slist_reverse(dialog
->routes
);
2637 dialog
->request
= dialog
->routes
->data
;
2638 dialog
->routes
= g_slist_remove(dialog
->routes
, dialog
->routes
->data
);
2641 contact
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "<", ">", NULL
);
2642 dialog
->routes
= g_slist_append(dialog
->routes
, contact
);
2646 sipe_get_supported_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2648 GSList
*hdr
= msg
->headers
;
2649 struct siphdrelement
*elem
;
2653 if(!g_ascii_strcasecmp(elem
->name
, "Supported")
2654 && !g_slist_find_custom(dialog
->supported
, elem
->value
, (GCompareFunc
)g_ascii_strcasecmp
))
2656 dialog
->supported
= g_slist_append(dialog
->supported
, g_strdup(elem
->value
));
2659 hdr
= g_slist_next(hdr
);
2664 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2666 gchar
*us
= outgoing
? "From" : "To";
2667 gchar
*them
= outgoing
? "To" : "From";
2669 g_free(dialog
->callid
);
2670 g_free(dialog
->ourtag
);
2671 g_free(dialog
->theirtag
);
2673 dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
2674 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
2675 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
2676 if (!dialog
->theirepid
) {
2677 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
2678 if (!dialog
->theirepid
) {
2679 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
2683 sipe_get_route_header(msg
, dialog
, outgoing
);
2684 sipe_get_supported_header(msg
, dialog
, outgoing
);
2689 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2691 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2692 struct sip_im_session
* session
= find_im_session(sip
, with
);
2693 struct sip_dialog
*dialog
;
2699 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2704 dialog
= session
->dialog
;
2706 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2711 sipe_parse_dialog(msg
, dialog
, TRUE
);
2713 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2714 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
2716 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2718 if (msg
->response
!= 200) {
2719 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2721 sipe_present_message_undelivered_err(with
, sip
, message
);
2722 im_session_destroy(sip
, session
);
2728 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
2729 session
->outgoing_invite
= NULL
;
2730 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
2731 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
2732 if (session
->outgoing_message_queue
) {
2733 char *queued_msg
= session
->outgoing_message_queue
->data
;
2734 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2739 sipe_im_process_queue(sip
, session
);
2741 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2742 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
2743 key
, g_hash_table_size(session
->unconfirmed_messages
));
2751 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
*session
, const gchar
*msg_body
)
2760 char *ms_text_format
;
2765 if (session
->dialog
) {
2766 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
2770 session
->dialog
= g_new0(struct sip_dialog
, 1);
2771 session
->dialog
->callid
= gencallid();
2773 if (strstr(session
->with
, "sip:")) {
2774 to
= g_strdup(session
->with
);
2776 to
= g_strdup_printf("sip:%s", session
->with
);
2779 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
2780 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
2782 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2786 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2790 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
2791 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
2796 key
= g_strdup_printf("<%s><%d><INVITE>", session
->dialog
->callid
, (session
->dialog
->cseq
) + 1);
2797 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
2798 purple_debug_info("sipe", "sipe_im_send: added message %s to unconfirmed_messages(count=%d)\n",
2799 key
, g_hash_table_size(session
->unconfirmed_messages
));
2802 contact
= get_contact(sip
);
2803 hdr
= g_strdup_printf(
2805 "Content-Type: application/sdp\r\n",
2806 contact
, ms_text_format
);
2807 g_free(ms_text_format
);
2809 body
= g_strdup_printf(
2811 "o=- 0 0 IN IP4 %s\r\n"
2815 "m=message %d sip null\r\n"
2816 "a=accept-types:text/plain text/html image/gif "
2817 "multipart/alternative application/im-iscomposing+xml\r\n",
2818 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
2820 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
2821 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
2830 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2833 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
2834 im_session_destroy(sip
, session
);
2839 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
2841 struct sipe_account_data
*sip
= gc
->proto_data
;
2843 purple_debug_info("sipe", "conversation with %s closed\n", who
);
2844 im_session_close(sip
, find_im_session(sip
, who
));
2848 im_session_close_all (struct sipe_account_data
*sip
)
2850 GSList
*entry
= sip
->im_sessions
;
2852 im_session_close (sip
, entry
->data
);
2853 entry
= sip
->im_sessions
;
2857 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
2859 struct sipe_account_data
*sip
;
2861 struct sip_im_session
*session
;
2863 sip
= gc
->proto_data
;
2866 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
2868 session
= find_or_create_im_session(sip
, who
);
2870 // Queue the message
2871 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
2873 if (session
->dialog
&& session
->dialog
->callid
) {
2874 sipe_im_process_queue(sip
, session
);
2875 } else if (!session
->outgoing_invite
) {
2876 // Need to send the INVITE to get the outgoing dialog setup
2877 sipe_invite(sip
, session
, what
);
2884 /* End IM Session (INVITE and MESSAGE methods) */
2887 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
2889 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2890 struct sip_im_session
*session
;
2892 if (state
== PURPLE_NOT_TYPING
)
2895 session
= find_im_session(sip
, who
);
2897 if (session
&& session
->dialog
) {
2898 send_sip_request(gc
, "INFO", who
, who
,
2899 "Content-Type: application/xml\r\n",
2900 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
2902 return SIPE_TYPING_SEND_TIMEOUT
;
2905 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
2907 GSList
*tmp
= sip
->transactions
;
2908 time_t currtime
= time(NULL
);
2910 struct transaction
*trans
= tmp
->data
;
2912 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime
-trans
->time
);
2913 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
2916 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
2918 sendout_sipmsg(sip
, trans
->msg
);
2925 static void do_reauthenticate_cb(struct sipe_account_data
*sip
)
2927 /* register again when security token expires */
2928 /* we have to start a new authentication as the security token
2929 * is almost expired by sending a not signed REGISTER message */
2930 purple_debug_info("sipe", "do a full reauthentication\n");
2931 sipe_auth_free(&sip
->registrar
);
2932 sip
->registerstatus
= 0;
2934 sip
->reauthenticate_set
= FALSE
;
2937 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2941 gboolean found
= FALSE
;
2943 from
= parse_from(sipmsg_find_header(msg
, "From"));
2947 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
2949 contenttype
= sipmsg_find_header(msg
, "Content-Type");
2950 if (!strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
2952 gchar
*html
= get_html_message(contenttype
, msg
->body
);
2953 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
2955 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2958 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
2959 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2964 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
2968 state
= xmlnode_get_child(isc
, "state");
2971 purple_debug_info("sipe", "process_incoming_message: no state found\n");
2976 statedata
= xmlnode_get_data(state
);
2978 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
2979 else serv_got_typing_stopped(sip
->gc
, from
);
2984 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2988 purple_debug_info("sipe", "got unknown mime-type");
2989 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
2994 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2996 gchar
*ms_text_format
;
2999 struct sip_im_session
*session
;
3001 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
3003 // Only accept text invitations
3004 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
3005 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3009 from
= parse_from(sipmsg_find_header(msg
, "From"));
3010 session
= find_or_create_im_session (sip
, from
);
3012 if (session
->dialog
) {
3013 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
3015 session
->dialog
= g_new0(struct sip_dialog
, 1);
3017 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
3019 session
->dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
3020 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
3021 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
3022 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
3025 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
3028 //ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk=
3029 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
3030 if (ms_text_format
) {
3031 if (!strncmp(ms_text_format
, "text/plain", 10) || !strncmp(ms_text_format
, "text/html", 9)) {
3033 gchar
*html
= get_html_message(ms_text_format
, NULL
);
3035 serv_got_im(sip
->gc
, from
, html
, 0, time(NULL
));
3037 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message received
3043 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
3044 sipmsg_remove_header(msg
, "Ms-Text-Format");
3045 sipmsg_remove_header(msg
, "EndPoints");
3046 sipmsg_remove_header(msg
, "User-Agent");
3047 sipmsg_remove_header(msg
, "Roster-Manager");
3049 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3050 //sipmsg_add_header(msg, "Supported", "ms-renders-gif");
3052 body
= g_strdup_printf(
3054 "o=- 0 0 IN IP4 %s\r\n"
3058 "m=message %d sip sip:%s\r\n"
3059 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3060 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
3061 sip
->realport
, sip
->username
);
3062 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3066 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3070 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
3071 sipmsg_remove_header(msg
, "EndPoints");
3072 sipmsg_remove_header(msg
, "User-Agent");
3074 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, BENOTIFY");
3075 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3077 body
= g_strdup_printf(
3079 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3081 "c=IN IP4 0.0.0.0\r\n"
3083 "m=message %d sip sip:%s\r\n"
3084 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3085 sip
->realport
, sip
->username
);
3086 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3090 static void sipe_connection_cleanup(struct sipe_account_data
*);
3091 static void create_connection(struct sipe_account_data
*, gchar
*, int);
3093 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3097 const gchar
*expires_header
;
3099 GSList
*hdr
= msg
->headers
;
3101 struct siphdrelement
*elem
;
3103 expires_header
= sipmsg_find_header(msg
, "Expires");
3104 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
3105 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
3107 switch (msg
->response
) {
3110 sip
->registerstatus
= 0;
3112 gchar
*contact_hdr
= NULL
;
3117 if (!sip
->reregister_set
) {
3118 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
3119 sipe_schedule_action(action_name
, expires
, (Action
) do_register_cb
, sip
, NULL
);
3120 g_free(action_name
);
3121 sip
->reregister_set
= TRUE
;
3124 sip
->registerstatus
= 3;
3126 if (!sip
->reauthenticate_set
) {
3127 /* we have to reauthenticate as our security token expires
3128 after eight hours (be five minutes early) */
3129 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3130 guint reauth_timeout
= (8 * 3600) - 360;
3131 sipe_schedule_action(action_name
, reauth_timeout
, (Action
) do_reauthenticate_cb
, sip
, NULL
);
3132 g_free(action_name
);
3133 sip
->reauthenticate_set
= TRUE
;
3136 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3138 epid
= get_epid(sip
);
3139 uuid
= generateUUIDfromEPID(epid
);
3142 // There can be multiple Contact headers (one per location where the user is logged in) so
3143 // make sure to only get the one for this uuid
3144 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3145 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3146 if (valid_contact
) {
3147 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3148 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3149 g_free(valid_contact
);
3152 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3157 g_free(sip
->contact
);
3159 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3162 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3163 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
);
3165 sip
->msrtc_event_categories
= FALSE
;
3170 if (!g_ascii_strcasecmp(elem
->name
, "Supported")) {
3171 if (strstr(elem
->value
, "msrtc-event-categories")) {
3172 sip
->msrtc_event_categories
= TRUE
;
3174 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s, %d\n", elem
->value
, sip
->msrtc_event_categories
);
3176 if (!g_ascii_strcasecmp(elem
->name
, "Allow-Events")){
3177 gchar
**caps
= g_strsplit(elem
->value
,",",0);
3180 sip
->allow_events
= g_slist_append(sip
->allow_events
, g_strdup(caps
[i
]));
3181 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Allow-Events: %s\n", caps
[i
]);
3186 hdr
= g_slist_next(hdr
);
3189 if (!sip
->subscribed
) { //do it just once, not every re-register
3190 if(!sip
->msrtc_event_categories
){ //Only for LCS2005, on OCS2007 always backs the error 504 Server time-out
3191 sipe_options_request(sip
, sip
->sipdomain
);
3193 entry
= sip
->allow_events
;
3196 if (tmp
&& !g_ascii_strcasecmp(tmp
, "vnd-microsoft-roaming-contacts")) {
3197 sipe_subscribe_roaming_contacts(sip
, msg
);
3199 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-roaming-ACL")) {
3200 sipe_subscribe_roaming_acl(sip
, msg
);
3202 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-roaming-self")) {
3203 sipe_subscribe_roaming_self(sip
, msg
);
3205 if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-provisioning-v2")) {
3206 sipe_subscribe_roaming_provisioning_v2(sip
, msg
);
3207 } else if (tmp
&& !g_ascii_strcasecmp(tmp
,"vnd-microsoft-provisioning")) { // LSC2005
3208 sipe_subscribe_roaming_provisioning(sip
, msg
);
3210 if (tmp
&& !g_ascii_strcasecmp(tmp
,"presence.wpending")) {
3211 sipe_subscribe_presence_wpending(sip
, msg
);
3213 entry
= entry
->next
;
3215 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3216 sip
->subscribed
= TRUE
;
3219 if (purple_account_get_bool(sip
->account
, "clientkeepalive", FALSE
)) {
3220 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Setting user defined keepalive\n");
3221 sip
->keepalive_timeout
= purple_account_get_int(sip
->account
, "keepalive", 0);
3223 tmp
= sipmsg_find_header(msg
, "ms-keep-alive");
3225 sipe_keep_alive_timeout(sip
, tmp
);
3229 // Should we remove the transaction here?
3230 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
3231 transactions_remove(sip
, tc
);
3236 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
3238 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
3239 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
3243 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
3246 tmp
= g_strsplit(parts
[0], ":", 0);
3247 hostname
= g_strdup(tmp
[0]);
3248 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
3252 tmp
= g_strsplit(parts
[i
], "=", 0);
3254 if (g_strcasecmp("transport", tmp
[0]) == 0) {
3255 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
3256 transport
= SIPE_TRANSPORT_TCP
;
3257 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
3258 transport
= SIPE_TRANSPORT_UDP
;
3267 /* Close old connection */
3268 sipe_connection_cleanup(sip
);
3270 /* Create new connection */
3271 sip
->transport
= transport
;
3272 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3273 hostname
, port
, TRANSPORT_DESCRIPTOR
);
3274 create_connection(sip
, hostname
, port
);
3280 if (sip
->registerstatus
!= 2) {
3281 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
3282 if (sip
->registrar
.retries
> 3) {
3283 sip
->gc
->wants_to_die
= TRUE
;
3284 purple_connection_error(sip
->gc
, _("Wrong Password"));
3287 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3288 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3290 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3292 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3293 fill_auth(sip
, tmp
, &sip
->registrar
);
3294 sip
->registerstatus
= 2;
3295 if (sip
->account
->disconnecting
) {
3296 do_register_exp(sip
, 0);
3304 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
3305 if (warning
!= NULL
) {
3307 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
3309 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
3310 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
3313 warning
= g_strdup(_("You have been rejected by the server"));
3316 sip
->gc
->wants_to_die
= TRUE
;
3317 purple_connection_error(sip
->gc
, warning
);
3324 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3325 if (warning
!= NULL
) {
3326 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3327 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
3330 warning
= g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
3333 sip
->gc
->wants_to_die
= TRUE
;
3334 purple_connection_error(sip
->gc
, warning
);
3341 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3342 if (warning
!= NULL
) {
3343 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3344 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
3347 warning
= g_strdup(_("Service unavailable: no reason given"));
3350 sip
->gc
->wants_to_die
= TRUE
;
3351 purple_connection_error(sip
->gc
, warning
);
3360 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3363 xmlnode
*xn_categories
;
3364 xmlnode
*xn_category
;
3366 const char *activity
= NULL
;
3368 xn_categories
= xmlnode_from_str(data
, len
);
3369 uri
= xmlnode_get_attrib(xn_categories
, "uri");
3371 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
3373 xn_category
= xmlnode_get_next_twin(xn_category
) )
3375 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
3377 if (!strcmp(attrVar
, "note"))
3380 struct sipe_buddy
*sbuddy
;
3381 xn_node
= xmlnode_get_child(xn_category
, "note");
3382 if (!xn_node
) continue;
3383 xn_node
= xmlnode_get_child(xn_node
, "body");
3384 if (!xn_node
) continue;
3386 note
= xmlnode_get_data(xn_node
);
3389 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3393 purple_debug_info("sipe", "process_incoming_notify_rlmi: uri(%s),note(%s)\n",uri
,note
);
3394 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3395 sbuddy
->annotation
= g_strdup(note
);
3400 else if(!strcmp(attrVar
, "state"))
3404 xn_node
= xmlnode_get_child(xn_category
, "state");
3405 if (!xn_node
) continue;
3406 xn_node
= xmlnode_get_child(xn_node
, "availability");
3407 if (!xn_node
) continue;
3409 data
= xmlnode_get_data(xn_node
);
3414 activity
= SIPE_STATUS_ID_UNKNOWN
;
3415 else if (avail
< 4500)
3416 activity
= SIPE_STATUS_ID_AVAILABLE
;
3417 else if (avail
< 6000)
3418 activity
= SIPE_STATUS_ID_BRB
;
3419 else if (avail
< 7500)
3420 activity
= SIPE_STATUS_ID_ONPHONE
;
3421 else if (avail
< 9000)
3422 activity
= SIPE_STATUS_ID_BUSY
;
3423 else if (avail
< 12000)
3424 activity
= SIPE_STATUS_ID_DND
;
3425 else if (avail
< 18000)
3426 activity
= SIPE_STATUS_ID_AWAY
;
3428 activity
= SIPE_STATUS_ID_OFFLINE
;
3432 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity
);
3433 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
3436 xmlnode_free(xn_categories
);
3439 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3441 const char *uri
,*state
;
3443 xmlnode
*xn_resource
;
3444 xmlnode
*xn_instance
;
3446 xn_list
= xmlnode_from_str(data
, len
);
3448 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
3450 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
3452 uri
= xmlnode_get_attrib(xn_resource
, "uri");
3453 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
3454 if (!xn_instance
) return;
3456 state
= xmlnode_get_attrib(xn_instance
, "state");
3457 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n",uri
,state
);
3458 if(strstr(state
,"resubscribe")){
3459 sipe_subscribe_presence_single(sip
, uri
);
3464 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3468 gchar
*activity
= NULL
;
3470 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
3471 gboolean isonline
= FALSE
;
3472 xmlnode
*display_name_node
;
3474 pidf
= xmlnode_from_str(data
, len
);
3476 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
3480 uri
= xmlnode_get_attrib(pidf
, "entity");
3482 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
3484 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3485 basicstatus
= xmlnode_get_child(status
, "basic");
3490 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
3495 getbasic
= xmlnode_get_data(basicstatus
);
3497 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
3502 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
3503 if (strstr(getbasic
, "open")) {
3508 display_name_node
= xmlnode_get_child(pidf
, "display-name");
3509 // updating display name if alias was just URI
3510 if (display_name_node
) {
3511 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3512 GSList
*entry
= buddies
;
3513 PurpleBuddy
*p_buddy
;
3514 char * display_name
= xmlnode_get_data(display_name_node
);
3517 const char *server_alias
;
3520 p_buddy
= entry
->data
;
3522 alias
= (char *)purple_buddy_get_alias(p_buddy
);
3523 alias
= alias
? g_strdup_printf("sip:%s", alias
) : NULL
;
3524 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
3525 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3526 purple_blist_alias_buddy(p_buddy
, display_name
);
3530 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3532 ( (server_alias
&& strcmp(display_name
, server_alias
))
3533 || !server_alias
|| strlen(server_alias
) == 0 )
3535 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3538 entry
= entry
->next
;
3540 g_free(display_name
);
3543 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
3544 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3545 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
3546 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
3547 activity
= xmlnode_get_data(basicstatus
);
3548 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
3555 const gchar
* status_id
= NULL
;
3557 if (strstr(activity
, "busy")) {
3558 status_id
= SIPE_STATUS_ID_BUSY
;
3559 } else if (strstr(activity
, "away")) {
3560 status_id
= SIPE_STATUS_ID_AWAY
;
3565 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3568 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
3569 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
3571 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
3578 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3580 const char *availability
;
3581 const char *activity
;
3582 const char *display_name
= NULL
;
3583 const char *activity_name
;
3588 struct sipe_buddy
*sbuddy
;
3590 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
3592 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
3593 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
3594 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
3595 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
3596 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
3597 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
3598 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
3599 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
3600 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
3601 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
3602 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
3603 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
3605 name
= xmlnode_get_attrib(xn_presentity
, "uri");
3606 uri
= g_strdup_printf("sip:%s", name
);
3607 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
3608 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
3610 // updating display name if alias was just URI
3611 if (xn_display_name
) {
3612 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3613 GSList
*entry
= buddies
;
3614 PurpleBuddy
*p_buddy
;
3615 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
3618 const char *email_str
, *server_alias
;
3620 p_buddy
= entry
->data
;
3622 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
3623 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3624 purple_blist_alias_buddy(p_buddy
, display_name
);
3627 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3629 ( (server_alias
&& strcmp(display_name
, server_alias
))
3630 || !server_alias
|| strlen(server_alias
) == 0 )
3632 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3636 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
3637 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
3638 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
3642 entry
= entry
->next
;
3646 avl
= atoi(availability
);
3647 act
= atoi(activity
);
3650 activity_name
= SIPE_STATUS_ID_AWAY
;
3651 else if (act
<= 150)
3652 activity_name
= SIPE_STATUS_ID_LUNCH
;
3653 else if (act
<= 300)
3654 activity_name
= SIPE_STATUS_ID_BRB
;
3655 else if (act
<= 400)
3656 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3657 else if (act
<= 500)
3658 activity_name
= SIPE_STATUS_ID_ONPHONE
;
3659 else if (act
<= 600)
3660 activity_name
= SIPE_STATUS_ID_BUSY
;
3662 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3665 activity_name
= SIPE_STATUS_ID_OFFLINE
;
3667 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3670 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3671 sbuddy
->annotation
= NULL
;
3672 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
3674 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
3675 sbuddy
->device_name
= NULL
;
3676 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
3679 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
3680 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
3682 xmlnode_free(xn_presentity
);
3686 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3688 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3690 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
3692 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
3693 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
3695 const char *content
= msg
->body
;
3696 unsigned length
= msg
->bodylen
;
3697 PurpleMimeDocument
*mime
= NULL
;
3699 if (strstr(ctype
, "multipart"))
3701 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
3702 const char *content_type
;
3704 mime
= purple_mime_document_parse(doc
);
3705 parts
= purple_mime_document_get_parts(mime
);
3707 content
= purple_mime_part_get_data(parts
->data
);
3708 length
= purple_mime_part_get_length(parts
->data
);
3709 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
3710 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
3712 process_incoming_notify_rlmi_resub(sip
, content
, length
);
3714 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
3716 process_incoming_notify_msrtc(sip
, content
, length
);
3720 process_incoming_notify_rlmi(sip
, content
, length
);
3722 parts
= parts
->next
;
3728 purple_mime_document_free(mime
);
3731 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
3733 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
3735 else if(strstr(ctype
, "application/rlmi+xml"))
3737 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
3740 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
3742 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
3746 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
3751 * Dispatcher for all incoming subscription information
3752 * whether it comes from NOTIFY, BENOTIFY requests or
3753 * piggy-backed to subscription's OK responce.
3755 * @param request whether initiated from BE/NOTIFY request or OK-response message.
3756 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
3758 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
3760 gchar
*event
= sipmsg_find_header(msg
, "Event");
3761 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
3764 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
3765 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n", subscription_state
);
3769 const gchar
*expires_header
;
3770 expires_header
= sipmsg_find_header(msg
, "Expires");
3771 timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
3772 purple_debug_info("sipe", "process_incoming_notify: expires:%d\n\n", timeout
);
3773 timeout
= (timeout
- 60) > 60 ? (timeout
- 60) : timeout
; // 1 min ahead of expiration
3776 if (!subscription_state
|| strstr(subscription_state
, "active"))
3778 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
3780 sipe_process_presence(sip
, msg
);
3782 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
3784 sipe_process_roaming_contacts(sip
, msg
, NULL
);
3786 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self") )
3788 sipe_process_roaming_self(sip
, msg
);
3790 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
3792 sipe_process_roaming_acl(sip
, msg
);
3794 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
3796 sipe_process_presence_wpending(sip
, msg
);
3800 purple_debug_info("sipe", "Unable to process (BE)NOTIFY. Event is not supported:%s\n", event
? event
: "");
3804 //The server sends a (BE)NOTIFY with the status 'terminated'
3805 if(request
&& subscription_state
&& strstr(subscription_state
, "terminated") ) {
3806 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3807 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
3813 if (event
&& !sip
->msrtc_event_categories
)
3815 /*if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-contacts") &&
3816 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-contacts", (GCompareFunc)g_ascii_strcasecmp))
3818 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-contacts");
3819 sipe_schedule_action(action_name, timeout, (Action) sipe_subscribe_roaming_contacts, sip, msg);
3820 g_free(action_name);
3822 else if (!g_ascii_strcasecmp(event, "vnd-microsoft-roaming-ACL") &&
3823 g_slist_find_custom(sip->allow_events, "vnd-microsoft-roaming-ACL", (GCompareFunc)g_ascii_strcasecmp))
3825 gchar *action_name = g_strdup_printf("<%s>", "vnd-microsoft-roaming-ACL");
3826 sipe_schedule_action(action_name, timeout, (Action) sipe_subscribe_roaming_acl, sip, msg);
3827 g_free(action_name);
3830 else */if (!g_ascii_strcasecmp(event
, "presence.wpending") &&
3831 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
3833 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
3834 sipe_schedule_action(action_name
, timeout
, (Action
) sipe_subscribe_presence_wpending
, sip
, NULL
);
3835 g_free(action_name
);
3837 else if (!g_ascii_strcasecmp(event
, "presence") &&
3838 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
3840 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
3841 gchar
*action_name
= g_strdup_printf("<%s><%s>", "presence", who
);
3842 sipe_schedule_action(action_name
, timeout
, (Action
) sipe_subscribe_presence_batched
, sip
, who
);
3843 g_free(action_name
);
3849 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
3851 sipe_process_registration_notify(sip
, msg
);
3854 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
3855 if (request
&& !benotify
)
3857 sipmsg_remove_header(msg
, "Expires");
3858 sipmsg_remove_header(msg
, "subscription-state");
3859 sipmsg_remove_header(msg
, "Event");
3860 sipmsg_remove_header(msg
, "Require");
3861 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3868 static gchar* gen_xpidf(struct sipe_account_data *sip)
3870 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3872 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
3873 "<display name=\"sip:%s\"/>\r\n"
3874 "<atom id=\"1234\">\r\n"
3875 "<address uri=\"sip:%s\">\r\n"
3876 "<status status=\"%s\"/>\r\n"
3889 static gchar* gen_pidf(struct sipe_account_data *sip)
3891 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3892 "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" xmlns:ep=\"urn:ietf:params:xml:ns:pidf:status:rpid-status\" xmlns:ci=\"urn:ietf:params:xml:ns:pidf:cipid\" entity=\"sip:%s\">\r\n"
3893 "<tuple id=\"0\">\r\n"
3895 "<basic>open</basic>\r\n"
3896 "<ep:activities>\r\n"
3897 " <ep:activity>%s</ep:activity>\r\n"
3901 "<ci:display-name>%s</ci:display-name>\r\n"
3910 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
3912 int availability
= 300; // online
3913 int activity
= 400; // Available
3916 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
3918 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
3920 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
3922 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
3924 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
3926 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
3928 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
3929 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
3930 availability
= 0; // offline
3933 activity
= 400; // available
3936 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
3937 //@TODO: send user data - state; add hostname in upper case
3938 body
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
3939 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
3945 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3947 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3948 if (msg
->response
== 200) {
3949 sip
->status_version
= 0;
3950 send_presence_status(sip
);
3956 process_send_presence_category_publish_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3958 if (msg
->response
== 409) {
3959 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3960 // TODO need to parse the version #'s?
3961 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
3962 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
3966 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg
->body
);
3968 tmp
= get_contact(sip
);
3969 hdr
= g_strdup_printf("Contact: %s\r\n"
3970 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3972 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
3982 static void send_presence_category_publish(struct sipe_account_data
*sip
, const char * note
)
3989 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
3990 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
3992 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
3994 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
3996 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
3998 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
4000 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
4002 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
4005 // Offline or invisible
4009 uri
= g_strdup_printf("sip:%s", sip
->username
);
4010 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
4011 sip
->status_version
, code
,
4012 sip
->status_version
, code
,
4013 sip
->status_version
, note
? note
: "",
4014 sip
->status_version
, note
? note
: "",
4015 sip
->status_version
, note
? note
: ""
4017 sip
->status_version
++;
4019 tmp
= get_contact(sip
);
4020 hdr
= g_strdup_printf("Contact: %s\r\n"
4021 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4023 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
4031 static void send_presence_status(struct sipe_account_data
*sip
)
4033 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
4035 if (!status
) return;
4037 note
= purple_status_get_attr_string(status
, "message");
4039 if(sip
->msrtc_event_categories
){
4040 send_presence_category_publish(sip
, note
);
4042 send_presence_soap(sip
, note
);
4046 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
4048 gboolean found
= FALSE
;
4049 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
4050 if (msg
->response
== 0) { /* request */
4051 if (!strcmp(msg
->method
, "MESSAGE")) {
4052 process_incoming_message(sip
, msg
);
4054 } else if (!strcmp(msg
->method
, "NOTIFY")) {
4055 purple_debug_info("sipe","send->process_incoming_notify\n");
4056 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
4058 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
4059 purple_debug_info("sipe","send->process_incoming_benotify\n");
4060 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
4062 } else if (!strcmp(msg
->method
, "INVITE")) {
4063 process_incoming_invite(sip
, msg
);
4065 } else if (!strcmp(msg
->method
, "OPTIONS")) {
4066 process_incoming_options(sip
, msg
);
4068 } else if (!strcmp(msg
->method
, "INFO")) {
4070 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
4072 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
4075 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4077 } else if (!strcmp(msg
->method
, "ACK")) {
4078 // ACK's don't need any response
4080 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
4081 // LCS 2005 sends us these - just respond 200 OK
4083 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4084 } else if (!strcmp(msg
->method
, "BYE")) {
4085 struct sip_im_session
*session
;
4087 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
4089 from
= parse_from(sipmsg_find_header(msg
, "From"));
4090 session
= find_im_session (sip
, from
);
4094 // TODO Let the user know the other user left the conversation?
4095 im_session_destroy(sip
, session
);
4100 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
4102 } else { /* response */
4103 struct transaction
*trans
= transactions_find(sip
, msg
);
4105 if (msg
->response
== 407) {
4106 gchar
*resend
, *auth
, *ptmp
;
4108 if (sip
->proxy
.retries
> 30) return;
4109 sip
->proxy
.retries
++;
4110 /* do proxy authentication */
4112 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
4114 fill_auth(sip
, ptmp
, &sip
->proxy
);
4115 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
4116 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
4117 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4119 resend
= sipmsg_to_string(trans
->msg
);
4120 /* resend request */
4121 sendout_pkt(sip
->gc
, resend
);
4124 if (msg
->response
== 100 || msg
->response
== 180) {
4125 /* ignore provisional response */
4126 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
4128 sip
->proxy
.retries
= 0;
4129 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
4130 if (msg
->response
== 401)
4132 sip
->registrar
.retries
++;
4133 sip
->registrar
.expires
= 0;
4137 sip
->registrar
.retries
= 0;
4139 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
4141 if (msg
->response
== 401) {
4142 gchar
*resend
, *auth
, *ptmp
;
4144 if (sip
->registrar
.retries
> 4) return;
4145 sip
->registrar
.retries
++;
4147 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4148 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
4150 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4153 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
4155 fill_auth(sip
, ptmp
, &sip
->registrar
);
4156 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
4157 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
4158 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
4160 //sipmsg_remove_header(trans->msg, "Authorization");
4161 //sipmsg_add_header(trans->msg, "Authorization", auth);
4163 resend
= sipmsg_to_string(trans
->msg
);
4164 /* resend request */
4165 sendout_pkt(sip
->gc
, resend
);
4170 if (trans
->callback
) {
4171 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
4172 /* call the callback to process response*/
4173 (trans
->callback
)(sip
, msg
, trans
);
4175 /* Not sure if this is needed or what needs to be done
4176 but transactions seem to be removed prematurely so
4177 this only removes them if the response is 200 OK */
4178 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
4179 /*Has a bug and it's unneccesary*/
4180 /*transactions_remove(sip, trans);*/
4186 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
4190 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
4194 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
4202 /* according to the RFC remove CRLF at the beginning */
4203 while (*cur
== '\r' || *cur
== '\n') {
4206 if (cur
!= conn
->inbuf
) {
4207 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
4208 conn
->inbufused
= strlen(conn
->inbuf
);
4211 /* Received a full Header? */
4212 sip
->processing_input
= TRUE
;
4213 while (sip
->processing_input
&&
4214 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
4215 time_t currtime
= time(NULL
);
4218 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
4219 msg
= sipmsg_parse_header(conn
->inbuf
);
4222 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
4223 if (restlen
>= msg
->bodylen
) {
4224 dummy
= g_malloc(msg
->bodylen
+ 1);
4225 memcpy(dummy
, cur
, msg
->bodylen
);
4226 dummy
[msg
->bodylen
] = '\0';
4228 cur
+= msg
->bodylen
;
4229 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
4230 conn
->inbufused
= strlen(conn
->inbuf
);
4232 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
4233 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
4239 purple_debug_info("sipe", "body:\n%s", msg->body);
4242 // Verify the signature before processing it
4243 if (sip
->registrar
.ntlm_key
) {
4244 struct sipmsg_breakdown msgbd
;
4245 gchar
*signature_input_str
;
4246 gchar
*signature
= NULL
;
4249 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
4250 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
4251 if (signature_input_str
!= NULL
) {
4252 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
4254 g_free(signature_input_str
);
4256 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
4258 if (signature
!= NULL
) {
4259 if (rspauth
!= NULL
) {
4260 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
4261 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
4262 process_input_message(sip
, msg
);
4264 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
4265 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
4266 sip
->gc
->wants_to_die
= TRUE
;
4268 } else if (msg
->response
== 401) {
4269 purple_connection_error(sip
->gc
, _("Wrong Password"));
4270 sip
->gc
->wants_to_die
= TRUE
;
4276 sipmsg_breakdown_free(&msgbd
);
4278 process_input_message(sip
, msg
);
4285 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
4287 PurpleConnection
*gc
= data
;
4288 struct sipe_account_data
*sip
= gc
->proto_data
;
4293 static char buffer
[65536];
4294 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
4296 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
4297 msg
= sipmsg_parse_msg(buffer
);
4298 if (msg
) process_input_message(sip
, msg
);
4302 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
4304 struct sipe_account_data
*sip
= gc
->proto_data
;
4305 PurpleSslConnection
*gsc
= sip
->gsc
;
4307 purple_debug_error("sipe", "%s",debug
);
4308 purple_connection_error(gc
, msg
);
4310 /* Invalidate this connection. Next send will open a new one */
4312 connection_remove(sip
, gsc
->fd
);
4313 purple_ssl_close(gsc
);
4319 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4321 PurpleConnection
*gc
= data
;
4322 struct sipe_account_data
*sip
;
4323 struct sip_connection
*conn
;
4325 gboolean firstread
= TRUE
;
4327 /* NOTE: This check *IS* necessary */
4328 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
4329 purple_ssl_close(gsc
);
4333 sip
= gc
->proto_data
;
4334 conn
= connection_find(sip
, gsc
->fd
);
4336 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
4337 gc
->wants_to_die
= TRUE
;
4338 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
4342 /* Read all available data from the SSL connection */
4344 /* Increase input buffer size as needed */
4345 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4346 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4347 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4348 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
4351 /* Try to read as much as there is space left in the buffer */
4352 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
4353 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
4355 if (len
< 0 && errno
== EAGAIN
) {
4356 /* Try again later */
4358 } else if (len
< 0) {
4359 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
4361 } else if (firstread
&& (len
== 0)) {
4362 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
4366 conn
->inbufused
+= len
;
4369 /* Equivalence indicates that there is possibly more data to read */
4370 } while (len
== readlen
);
4372 conn
->inbuf
[conn
->inbufused
] = '\0';
4373 process_input(sip
, conn
);
4377 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4379 PurpleConnection
*gc
= data
;
4380 struct sipe_account_data
*sip
= gc
->proto_data
;
4382 struct sip_connection
*conn
= connection_find(sip
, source
);
4384 purple_debug_error("sipe", "Connection not found!\n");
4388 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4389 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4390 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4393 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
4395 if (len
< 0 && errno
== EAGAIN
)
4397 else if (len
<= 0) {
4398 purple_debug_info("sipe", "sipe_input_cb: read error\n");
4399 connection_remove(sip
, source
);
4400 if (sip
->fd
== source
) sip
->fd
= -1;
4404 conn
->inbufused
+= len
;
4405 conn
->inbuf
[conn
->inbufused
] = '\0';
4407 process_input(sip
, conn
);
4410 /* Callback for new connections on incoming TCP port */
4411 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4413 PurpleConnection
*gc
= data
;
4414 struct sipe_account_data
*sip
= gc
->proto_data
;
4415 struct sip_connection
*conn
;
4417 int newfd
= accept(source
, NULL
, NULL
);
4419 conn
= connection_create(sip
, newfd
);
4421 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4424 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
4426 PurpleConnection
*gc
= data
;
4427 struct sipe_account_data
*sip
;
4428 struct sip_connection
*conn
;
4430 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4438 purple_connection_error(gc
, _("Could not connect"));
4442 sip
= gc
->proto_data
;
4444 sip
->last_keepalive
= time(NULL
);
4446 conn
= connection_create(sip
, source
);
4450 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4453 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4455 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
4456 if (sip
== NULL
) return;
4461 static guint
sipe_ht_hash_nick(const char *nick
)
4463 char *lc
= g_utf8_strdown(nick
, -1);
4464 guint bucket
= g_str_hash(lc
);
4470 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
4472 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
4475 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
4477 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4479 sip
->listen_data
= NULL
;
4481 if (listenfd
== -1) {
4482 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4488 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
4489 sip
->listenfd
= sip
->fd
;
4491 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
4493 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
4497 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
4499 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4502 sip
->query_data
= NULL
;
4504 if (!hosts
|| !hosts
->data
) {
4505 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
4509 addr_size
= GPOINTER_TO_INT(hosts
->data
);
4510 hosts
= g_slist_remove(hosts
, hosts
->data
);
4511 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
4512 g_free(hosts
->data
);
4513 hosts
= g_slist_remove(hosts
, hosts
->data
);
4515 hosts
= g_slist_remove(hosts
, hosts
->data
);
4516 g_free(hosts
->data
);
4517 hosts
= g_slist_remove(hosts
, hosts
->data
);
4520 /* create socket for incoming connections */
4521 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
4522 sipe_udp_host_resolved_listen_cb
, sip
);
4523 if (sip
->listen_data
== NULL
) {
4524 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4529 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
4532 PurpleConnection
*gc
= data
;
4533 struct sipe_account_data
*sip
;
4535 /* If the connection is already disconnected, we don't need to do anything else */
4536 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4539 sip
= gc
->proto_data
;
4544 case PURPLE_SSL_CONNECT_FAILED
:
4545 purple_connection_error(gc
, _("Connection Failed"));
4547 case PURPLE_SSL_HANDSHAKE_FAILED
:
4548 purple_connection_error(gc
, _("SSL Handshake Failed"));
4550 case PURPLE_SSL_CERTIFICATE_INVALID
:
4551 purple_connection_error(gc
, _("SSL Certificate Invalid"));
4557 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
4559 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4560 PurpleProxyConnectData
*connect_data
;
4562 sip
->listen_data
= NULL
;
4564 sip
->listenfd
= listenfd
;
4565 if (sip
->listenfd
== -1) {
4566 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4570 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
4571 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
4572 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
4573 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
4574 sipe_newconn_cb
, sip
->gc
);
4575 purple_debug_info("sipe", "connecting to %s port %d\n",
4576 sip
->realhostname
, sip
->realport
);
4577 /* open tcp connection to the server */
4578 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
4579 sip
->realport
, login_cb
, sip
->gc
);
4581 if (connect_data
== NULL
) {
4582 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
4587 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
4589 PurpleAccount
*account
= sip
->account
;
4590 PurpleConnection
*gc
= sip
->gc
;
4592 if (purple_account_get_bool(account
, "useport", FALSE
)) {
4593 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
4594 port
= purple_account_get_int(account
, "port", 0);
4596 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
4599 sip
->realhostname
= hostname
;
4600 sip
->realport
= port
;
4602 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
4605 /* TODO: is there a good default grow size? */
4606 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
4607 sip
->txbuf
= purple_circ_buffer_new(0);
4609 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
4611 if (!purple_ssl_is_supported()) {
4612 gc
->wants_to_die
= TRUE
;
4613 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
4617 purple_debug_info("sipe", "using SSL\n");
4619 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
4620 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
4621 if (sip
->gsc
== NULL
) {
4622 purple_connection_error(gc
, _("Could not create SSL context"));
4625 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
4627 purple_debug_info("sipe", "using UDP\n");
4629 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
4630 if (sip
->query_data
== NULL
) {
4631 purple_connection_error(gc
, _("Could not resolve hostname"));
4635 purple_debug_info("sipe", "using TCP\n");
4636 /* create socket for incoming connections */
4637 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
4638 sipe_tcp_connect_listen_cb
, sip
);
4639 if (sip
->listen_data
== NULL
) {
4640 purple_connection_error(gc
, _("Could not create listen socket"));
4646 /* Service list for autodection */
4647 static const struct sipe_service_data service_autodetect
[] = {
4648 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4649 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4650 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4651 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4655 /* Service list for SSL/TLS */
4656 static const struct sipe_service_data service_tls
[] = {
4657 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4658 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4662 /* Service list for TCP */
4663 static const struct sipe_service_data service_tcp
[] = {
4664 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4665 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4669 /* Service list for UDP */
4670 static const struct sipe_service_data service_udp
[] = {
4671 { "sip", "udp", SIPE_TRANSPORT_UDP
},
4675 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
4676 static void resolve_next_service(struct sipe_account_data
*sip
,
4677 const struct sipe_service_data
*start
)
4680 sip
->service_data
= start
;
4682 sip
->service_data
++;
4683 if (sip
->service_data
->service
== NULL
) {
4685 /* Try connecting to the SIP hostname directly */
4686 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
4687 if (sip
->auto_transport
) {
4688 // If SSL is supported, default to using it; OCS servers aren't configured
4689 // by default to accept TCP
4690 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
4691 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
4692 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
4695 hostname
= g_strdup(sip
->sipdomain
);
4696 create_connection(sip
, hostname
, 0);
4701 /* Try to resolve next service */
4702 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
4703 sip
->service_data
->transport
,
4708 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
4710 struct sipe_account_data
*sip
= data
;
4712 sip
->srv_query_data
= NULL
;
4714 /* find the host to connect to */
4716 gchar
*hostname
= g_strdup(resp
->hostname
);
4717 int port
= resp
->port
;
4718 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
4722 sip
->transport
= sip
->service_data
->type
;
4724 create_connection(sip
, hostname
, port
);
4726 resolve_next_service(sip
, NULL
);
4730 static void sipe_login(PurpleAccount
*account
)
4732 PurpleConnection
*gc
;
4733 struct sipe_account_data
*sip
;
4734 gchar
**signinname_login
, **userserver
, **domain_user
;
4735 const char *transport
;
4737 const char *username
= purple_account_get_username(account
);
4738 gc
= purple_account_get_connection(account
);
4740 if (strpbrk(username
, "\t\v\r\n") != NULL
) {
4741 gc
->wants_to_die
= TRUE
;
4742 purple_connection_error(gc
, _("SIP Exchange username contains invalid characters"));
4746 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
4747 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
4748 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
4750 sip
->account
= account
;
4751 sip
->reregister_set
= FALSE
;
4752 sip
->reauthenticate_set
= FALSE
;
4753 sip
->subscribed
= FALSE
;
4754 sip
->subscribed_buddies
= FALSE
;
4756 signinname_login
= g_strsplit(username
, ",", 2);
4758 userserver
= g_strsplit(signinname_login
[0], "@", 2);
4759 purple_connection_set_display_name(gc
, userserver
[0]);
4760 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
4761 sip
->sipdomain
= g_strdup(userserver
[1]);
4763 if (strpbrk(sip
->username
, " \t\v\r\n") != NULL
) {
4764 gc
->wants_to_die
= TRUE
;
4765 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
4769 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
4770 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
4771 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
4773 sip
->password
= g_strdup(purple_connection_get_password(gc
));
4775 g_strfreev(userserver
);
4776 g_strfreev(domain_user
);
4777 g_strfreev(signinname_login
);
4779 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
4781 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
4783 /* TODO: Set the status correctly. */
4784 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
4786 transport
= purple_account_get_string(account
, "transport", "auto");
4787 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
4788 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
4791 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
4792 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
4793 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
4794 } else if (strcmp(transport
, "auto") == 0) {
4795 sip
->auto_transport
= TRUE
;
4796 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
4797 } else if (strcmp(transport
, "tls") == 0) {
4798 resolve_next_service(sip
, service_tls
);
4799 } else if (strcmp(transport
, "tcp") == 0) {
4800 resolve_next_service(sip
, service_tcp
);
4802 resolve_next_service(sip
, service_udp
);
4806 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
4808 connection_free_all(sip
);
4813 if (sip
->query_data
!= NULL
)
4814 purple_dnsquery_destroy(sip
->query_data
);
4815 sip
->query_data
= NULL
;
4817 if (sip
->srv_query_data
!= NULL
)
4818 purple_srv_cancel(sip
->srv_query_data
);
4819 sip
->srv_query_data
= NULL
;
4821 if (sip
->listen_data
!= NULL
)
4822 purple_network_listen_cancel(sip
->listen_data
);
4823 sip
->listen_data
= NULL
;
4825 if (sip
->gsc
!= NULL
)
4826 purple_ssl_close(sip
->gsc
);
4829 sipe_auth_free(&sip
->registrar
);
4830 sipe_auth_free(&sip
->proxy
);
4833 purple_circ_buffer_destroy(sip
->txbuf
);
4836 g_free(sip
->realhostname
);
4837 sip
->realhostname
= NULL
;
4840 purple_input_remove(sip
->listenpa
);
4842 if (sip
->tx_handler
)
4843 purple_input_remove(sip
->tx_handler
);
4844 sip
->tx_handler
= 0;
4845 if (sip
->resendtimeout
)
4846 purple_timeout_remove(sip
->resendtimeout
);
4847 sip
->resendtimeout
= 0;
4848 if (sip
->timeouts
) {
4849 GSList
*entry
= sip
->timeouts
;
4851 struct scheduled_action
*sched_action
= entry
->data
;
4852 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
4853 purple_timeout_remove(sched_action
->timeout_handler
);
4854 g_free(sched_action
->payload
);
4855 g_free(sched_action
->name
);
4856 g_free(sched_action
);
4857 entry
= entry
->next
;
4860 g_slist_free(sip
->timeouts
);
4862 if (sip
->allow_events
) {
4863 GSList
*entry
= sip
->allow_events
;
4865 g_free(entry
->data
);
4866 entry
= entry
->next
;
4869 g_slist_free(sip
->allow_events
);
4872 g_free(sip
->contact
);
4873 sip
->contact
= NULL
;
4875 g_free(sip
->regcallid
);
4876 sip
->regcallid
= NULL
;
4879 sip
->processing_input
= FALSE
;
4883 * A callback for g_hash_table_foreach_remove
4885 static gboolean
sipe_buddy_remove(gpointer key
, struct sipe_buddy
*buddy
, gpointer user_data
)
4887 sipe_free_buddy(buddy
);
4890 static void sipe_close(PurpleConnection
*gc
)
4892 struct sipe_account_data
*sip
= gc
->proto_data
;
4895 /* leave all conversations */
4896 im_session_close_all(sip
);
4899 do_register_exp(sip
, 0);
4901 sipe_connection_cleanup(sip
);
4902 g_free(sip
->sipdomain
);
4903 g_free(sip
->username
);
4904 g_free(sip
->password
);
4905 g_free(sip
->authdomain
);
4906 g_free(sip
->authuser
);
4907 g_free(sip
->status
);
4909 g_hash_table_foreach_remove(sip
->buddies
, (GHRFunc
) sipe_buddy_remove
, NULL
);
4910 g_hash_table_destroy(sip
->buddies
);
4912 g_free(gc
->proto_data
);
4913 gc
->proto_data
= NULL
;
4916 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4918 PurpleAccount
*acct
= purple_connection_get_account(gc
);
4919 char *id
= g_strdup_printf("sip:%s", (char *)g_list_nth_data(row
, 0));
4920 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
4922 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
4923 purple_conversation_present(conv
);
4927 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4930 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
4931 g_list_nth_data(row
, 0), NULL
, g_list_nth_data(row
, 1));
4934 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
4936 PurpleNotifySearchResults
*results
;
4937 PurpleNotifySearchColumn
*column
;
4938 xmlnode
*searchResults
;
4940 int match_count
= 0;
4941 gboolean more
= FALSE
;
4944 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
4946 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4947 if (!searchResults
) {
4948 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
4952 results
= purple_notify_searchresults_new();
4954 if (results
== NULL
) {
4955 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
4956 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
4958 xmlnode_free(searchResults
);
4962 column
= purple_notify_searchresults_column_new(_("User Name"));
4963 purple_notify_searchresults_column_add(results
, column
);
4965 column
= purple_notify_searchresults_column_new(_("Name"));
4966 purple_notify_searchresults_column_add(results
, column
);
4968 column
= purple_notify_searchresults_column_new(_("Company"));
4969 purple_notify_searchresults_column_add(results
, column
);
4971 column
= purple_notify_searchresults_column_new(_("Country"));
4972 purple_notify_searchresults_column_add(results
, column
);
4974 column
= purple_notify_searchresults_column_new(_("Email"));
4975 purple_notify_searchresults_column_add(results
, column
);
4977 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
4980 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
4981 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
4982 g_strfreev(uri_parts
);
4984 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
4985 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
4986 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
4987 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
4989 purple_notify_searchresults_row_add(results
, row
);
4993 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
4994 char *data
= xmlnode_get_data_unescaped(mrow
);
4995 more
= (g_strcasecmp(data
, "true") == 0);
4999 secondary
= g_strdup_printf(
5000 dngettext(GETTEXT_PACKAGE
,
5001 "Found %d contact%s:",
5002 "Found %d contacts%s:", match_count
),
5003 match_count
, more
? _(" (more matched your query)") : "");
5005 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5006 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5007 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5010 xmlnode_free(searchResults
);
5014 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5016 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5017 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
5021 PurpleRequestField
*field
= entries
->data
;
5022 const char *id
= purple_request_field_get_id(field
);
5023 const char *value
= purple_request_field_string_get_value(field
);
5025 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
5027 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
5028 } while ((entries
= g_list_next(entries
)) != NULL
);
5032 gchar
*query
= g_strjoinv(NULL
, attrs
);
5033 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
5034 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
5035 send_soap_request_with_cb(gc
->proto_data
, body
,
5036 (TransCallback
) process_search_contact_response
, NULL
);
5044 static void sipe_show_find_contact(PurplePluginAction
*action
)
5046 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
5047 PurpleRequestFields
*fields
;
5048 PurpleRequestFieldGroup
*group
;
5049 PurpleRequestField
*field
;
5051 fields
= purple_request_fields_new();
5052 group
= purple_request_field_group_new(NULL
);
5053 purple_request_fields_add_group(fields
, group
);
5055 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
5056 purple_request_field_group_add_field(group
, field
);
5057 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
5058 purple_request_field_group_add_field(group
, field
);
5059 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
5060 purple_request_field_group_add_field(group
, field
);
5061 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
5062 purple_request_field_group_add_field(group
, field
);
5064 purple_request_fields(gc
,
5066 _("Search for a Contact"),
5067 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
5069 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
5071 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
5074 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
5077 PurplePluginAction
*act
;
5079 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
5080 menu
= g_list_prepend(menu
, act
);
5082 menu
= g_list_reverse(menu
);
5087 static void dummy_permit_deny(PurpleConnection
*gc
)
5091 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
5097 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
5103 static char *sipe_status_text(PurpleBuddy
*buddy
)
5105 struct sipe_account_data
*sip
;
5106 struct sipe_buddy
*sbuddy
;
5109 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5110 if (sip
) //happens on pidgin exit
5112 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5113 if (sbuddy
&& sbuddy
->annotation
)
5115 text
= g_strdup(sbuddy
->annotation
);
5122 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
5124 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
5125 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
5126 struct sipe_account_data
*sip
;
5127 struct sipe_buddy
*sbuddy
;
5128 char *annotation
= NULL
;
5130 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5131 if (sip
) //happens on pidgin exit
5133 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5136 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
5141 if (purple_presence_is_online(presence
))
5143 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
5148 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
5155 sipe_get_account_text_table(PurpleAccount
*account
)
5158 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
5159 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
5163 static PurpleBuddy
*
5164 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
5167 const gchar
*server_alias
, *email
;
5168 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
5170 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
5172 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
5174 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
5176 purple_blist_server_alias_buddy(clone
, server_alias
);
5179 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
5181 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
5184 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
5186 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
5191 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
5193 PurpleBuddy
*buddy
, *b
;
5194 PurpleConnection
*gc
;
5195 PurpleGroup
* group
= purple_find_group(group_name
);
5197 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
5199 buddy
= (PurpleBuddy
*)node
;
5201 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
5202 gc
= purple_account_get_connection(buddy
->account
);
5204 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
5206 b
= purple_blist_add_buddy_clone(group
, buddy
);
5209 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
5213 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
5216 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
5218 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
5221 char *mailto
= g_strdup_printf("mailto:%s", email
);
5222 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
5226 char *const parmList
[] = {mailto
, NULL
};
5227 if ((pid
= fork()) == -1)
5229 purple_debug_info("sipe", "fork() error\n");
5233 execvp("xdg-email", parmList
);
5234 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
5242 //@TODO resolve env variable %WINDIR% first
5243 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
5246 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
5255 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
5260 * A menu which appear when right-clicking on buddy in contact list.
5263 sipe_buddy_menu(PurpleBuddy
*buddy
)
5265 PurpleBlistNode
*g_node
;
5266 PurpleGroup
*group
, *gr_parent
;
5267 PurpleMenuAction
*act
;
5269 GList
*menu_groups
= NULL
;
5271 act
= purple_menu_action_new(_("Send Email..."),
5272 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
5274 menu
= g_list_prepend(menu
, act
);
5276 gr_parent
= purple_buddy_get_group(buddy
);
5277 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
5278 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
5281 group
= (PurpleGroup
*)g_node
;
5282 if (group
== gr_parent
)
5285 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
5288 act
= purple_menu_action_new(purple_group_get_name(group
),
5289 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
5291 menu_groups
= g_list_prepend(menu_groups
, act
);
5293 menu_groups
= g_list_reverse(menu_groups
);
5295 act
= purple_menu_action_new(_("Copy to"),
5298 menu
= g_list_prepend(menu
, act
);
5299 menu
= g_list_reverse(menu
);
5304 GList
*sipe_blist_node_menu(PurpleBlistNode
*node
) {
5305 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
5306 return sipe_buddy_menu((PurpleBuddy
*) node
);
5313 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
5315 gboolean ret
= TRUE
;
5316 char *username
= (char *)trans
->payload
;
5318 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
5319 PurpleBuddy
*pbuddy
;
5320 struct sipe_buddy
*sbuddy
;
5322 char *server_alias
= NULL
;
5324 const char *device_name
= NULL
;
5326 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
5328 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
5329 alias
= purple_buddy_get_local_alias(pbuddy
);
5333 //will query buddy UA's capabilities and send answer to log
5334 sipe_options_request(sip
, username
);
5336 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
5339 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
5343 if (msg
->response
!= 200) {
5344 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
5346 xmlnode
*searchResults
;
5349 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
5350 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5351 if (!searchResults
) {
5352 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
5353 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
5354 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
5355 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
5356 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
5357 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
5358 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
5359 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
5360 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
5361 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
5362 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
5363 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
5364 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
5365 if (!email
|| strcmp("", email
)) {
5366 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
5367 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
5371 xmlnode_free(searchResults
);
5374 purple_notify_user_info_add_section_break(info
);
5376 if (!server_alias
|| !strcmp("", server_alias
)) {
5377 g_free(server_alias
);
5378 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
5380 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
5384 // same as server alias, do not present
5385 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
5388 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
5391 if (!email
|| !strcmp("", email
)) {
5393 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
5395 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
5401 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
5404 /* show a buddy's user info in a nice dialog box */
5405 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
5406 username
, /* buddy's username */
5408 NULL
, /* callback called when dialog closed */
5409 NULL
); /* userdata for callback */
5415 * AD search first, LDAP based
5417 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
5419 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
5420 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
5422 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
5423 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
5424 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
5429 static PurplePlugin
*my_protocol
= NULL
;
5431 static PurplePluginProtocolInfo prpl_info
=
5434 NULL
, /* user_splits */
5435 NULL
, /* protocol_options */
5436 NO_BUDDY_ICONS
, /* icon_spec */
5437 sipe_list_icon
, /* list_icon */
5438 NULL
, /* list_emblems */
5439 sipe_status_text
, /* status_text */
5440 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
5441 sipe_status_types
, /* away_states */
5442 sipe_blist_node_menu
, /* blist_node_menu */
5443 NULL
, /* chat_info */
5444 NULL
, /* chat_info_defaults */
5445 sipe_login
, /* login */
5446 sipe_close
, /* close */
5447 sipe_im_send
, /* send_im */
5448 NULL
, /* set_info */ // TODO maybe
5449 sipe_send_typing
, /* send_typing */
5450 sipe_get_info
, /* get_info */
5451 sipe_set_status
, /* set_status */
5452 NULL
, /* set_idle */
5453 NULL
, /* change_passwd */
5454 sipe_add_buddy
, /* add_buddy */
5455 NULL
, /* add_buddies */
5456 sipe_remove_buddy
, /* remove_buddy */
5457 NULL
, /* remove_buddies */
5458 sipe_add_permit
, /* add_permit */
5459 sipe_add_deny
, /* add_deny */
5460 sipe_add_deny
, /* rem_permit */
5461 sipe_add_permit
, /* rem_deny */
5462 dummy_permit_deny
, /* set_permit_deny */
5463 NULL
, /* join_chat */
5464 NULL
, /* reject_chat */
5465 NULL
, /* get_chat_name */
5466 NULL
, /* chat_invite */
5467 NULL
, /* chat_leave */
5468 NULL
, /* chat_whisper */
5469 NULL
, /* chat_send */
5470 sipe_keep_alive
, /* keepalive */
5471 NULL
, /* register_user */
5472 NULL
, /* get_cb_info */ // deprecated
5473 NULL
, /* get_cb_away */ // deprecated
5474 sipe_alias_buddy
, /* alias_buddy */
5475 sipe_group_buddy
, /* group_buddy */
5476 sipe_rename_group
, /* rename_group */
5477 NULL
, /* buddy_free */
5478 sipe_convo_closed
, /* convo_closed */
5479 purple_normalize_nocase
, /* normalize */
5480 NULL
, /* set_buddy_icon */
5481 sipe_remove_group
, /* remove_group */
5482 NULL
, /* get_cb_real_name */ // TODO?
5483 NULL
, /* set_chat_topic */
5484 NULL
, /* find_blist_chat */
5485 NULL
, /* roomlist_get_list */
5486 NULL
, /* roomlist_cancel */
5487 NULL
, /* roomlist_expand_category */
5488 NULL
, /* can_receive_file */
5489 NULL
, /* send_file */
5490 NULL
, /* new_xfer */
5491 NULL
, /* offline_message */
5492 NULL
, /* whiteboard_prpl_ops */
5493 sipe_send_raw
, /* send_raw */
5494 NULL
, /* roomlist_room_serialize */
5495 NULL
, /* unregister_user */
5496 NULL
, /* send_attention */
5497 NULL
, /* get_attention_types */
5499 sizeof(PurplePluginProtocolInfo
), /* struct_size */
5500 sipe_get_account_text_table
, /* get_account_text_table */
5504 static PurplePluginInfo info
= {
5505 PURPLE_PLUGIN_MAGIC
,
5506 PURPLE_MAJOR_VERSION
,
5507 PURPLE_MINOR_VERSION
,
5508 PURPLE_PLUGIN_PROTOCOL
, /**< type */
5509 NULL
, /**< ui_requirement */
5511 NULL
, /**< dependencies */
5512 PURPLE_PRIORITY_DEFAULT
, /**< priority */
5513 "prpl-sipe", /**< id */
5514 "Microsoft LCS/OCS", /**< name */
5515 VERSION
, /**< version */
5516 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
5517 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
5518 "Anibal Avelar <avelar@gmail.com>, " /**< author */
5519 "Gabriel Burt <gburt@novell.com>", /**< author */
5520 PURPLE_WEBSITE
, /**< homepage */
5521 sipe_plugin_load
, /**< load */
5522 sipe_plugin_unload
, /**< unload */
5523 sipe_plugin_destroy
, /**< destroy */
5524 NULL
, /**< ui_info */
5525 &prpl_info
, /**< extra_info */
5534 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
5538 entry
= prpl_info
.protocol_options
;
5540 purple_account_option_destroy(entry
->data
);
5541 entry
= g_list_delete_link(entry
, entry
);
5543 prpl_info
.protocol_options
= NULL
;
5545 entry
= prpl_info
.user_splits
;
5547 purple_account_user_split_destroy(entry
->data
);
5548 entry
= g_list_delete_link(entry
, entry
);
5550 prpl_info
.user_splits
= NULL
;
5553 static void init_plugin(PurplePlugin
*plugin
)
5555 PurpleAccountUserSplit
*split
;
5556 PurpleAccountOption
*option
;
5559 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
5560 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
5561 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
5564 purple_plugin_register(plugin
);
5566 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
5567 purple_account_user_split_set_reverse(split
, FALSE
);
5568 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
5570 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
5571 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5572 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
5573 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5575 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
5576 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5577 // Translators: noun (networking port)
5578 option
= purple_account_option_int_new(_("Port"), "port", 5061);
5579 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5581 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
5582 purple_account_option_add_list_item(option
, _("Auto"), "auto");
5583 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
5584 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
5585 purple_account_option_add_list_item(option
, _("UDP"), "udp");
5586 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5588 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
5589 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
5591 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
5592 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5594 // TODO commented out so won't show in the preferences until we fix krb message signing
5595 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
5596 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5598 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
5599 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
5600 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5603 option
= purple_account_option_bool_new(_("Use Client-specified Keepalive"), "clientkeepalive", FALSE
);
5604 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5605 option
= purple_account_option_int_new(_("Keepalive Timeout"), "keepalive", 300);
5606 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5607 my_protocol
= plugin
;
5610 /* I had to redefined the function for it load, but works */
5611 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
5612 plugin
->info
= &(info
);
5613 init_plugin((plugin
));
5614 sipe_plugin_load((plugin
));
5615 return purple_plugin_register(plugin
);