6 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
7 * Copyright (C) 2007 Anibal Avelar <avelar@gmail.com>
8 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
11 * Thanks to Google's Summer of Code Program and the helpful mentors
14 * Session-based SIP MESSAGE documentation:
15 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #include <sys/socket.h>
34 #include <sys/ioctl.h>
35 #include <sys/types.h>
36 #include <netinet/in.h>
40 # define _(String) ((const char *) gettext (String))
42 # define _(String) ((const char *) (String))
43 #endif /* ENABLE_NLS */
48 #define _LIBC_INTERNAL_
61 #include "accountopt.h"
63 #include "conversation.h"
80 #endif /*USE_KERBEROS*/
83 #include "sipe-sign.h"
87 /* Keep in sync with sipe_transport_type! */
88 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
89 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
91 /* Status identifiers (see also: sipe_status_types()) */
92 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
93 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
94 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
95 /* PURPLE_STATUS_UNAVAILABLE: */
96 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
97 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
98 #define SIPE_STATUS_ID_ONPHONE "on-the-phone" /* On The Phone */
99 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
100 /* PURPLE_STATUS_AWAY: */
101 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
102 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
103 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
104 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
105 /* ??? PURPLE_STATUS_MOBILE */
106 /* ??? PURPLE_STATUS_TUNE */
108 static char *gentag()
110 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
113 static gchar
*get_epid(struct sipe_account_data
*sip
)
116 sip
->epid
= sipe_uuid_get_macaddr(purple_network_get_my_ip(-1));
118 return g_strdup(sip
->epid
);
121 static char *genbranch()
123 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
124 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
125 rand() & 0xFFFF, rand() & 0xFFFF);
128 static char *gencallid()
130 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
131 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
132 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
133 rand() & 0xFFFF, rand() & 0xFFFF);
136 static gchar
*find_tag(const gchar
*hdr
)
138 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
140 // In case it's at the end and there's no trailing ;
141 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
147 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
152 static void sipe_plugin_destroy(PurplePlugin
*plugin
);
154 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
156 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
157 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
160 static void sipe_close(PurpleConnection
*gc
);
162 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, const char * buddy_name
);
163 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
);
164 static void send_presence_status(struct sipe_account_data
*sip
);
166 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
168 static void sipe_keep_alive_timeout(struct sipe_account_data
*sip
, const gchar
*hdr
)
170 gchar
*timeout
= sipmsg_find_part_of_header(hdr
, "timeout=", ";", NULL
);
171 if (timeout
!= NULL
) {
172 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
173 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
174 sip
->keepalive_timeout
);
179 static void sipe_keep_alive(PurpleConnection
*gc
)
181 struct sipe_account_data
*sip
= gc
->proto_data
;
182 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
183 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
184 gchar buf
[2] = {0, 0};
185 purple_debug_info("sipe", "sending keep alive\n");
186 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
188 time_t now
= time(NULL
);
189 if ((sip
->keepalive_timeout
> 0) &&
190 ((now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
191 #if PURPLE_VERSION_CHECK(2,4,0)
192 && ((now
- gc
->last_received
) >= sip
->keepalive_timeout
)
195 purple_debug_info("sipe", "sending keep alive\n");
196 sendout_pkt(gc
, "\r\n\r\n");
197 sip
->last_keepalive
= now
;
202 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
204 struct sip_connection
*ret
= NULL
;
205 GSList
*entry
= sip
->openconns
;
208 if (ret
->fd
== fd
) return ret
;
214 static void sipe_auth_free(struct sip_auth
*auth
)
218 g_free(auth
->opaque
);
222 g_free(auth
->target
);
224 g_free(auth
->digest_session_key
);
225 auth
->digest_session_key
= NULL
;
226 g_free(auth
->ntlm_key
);
227 auth
->ntlm_key
= NULL
;
228 auth
->type
= AUTH_TYPE_UNSET
;
233 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
235 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
237 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
241 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
243 struct sip_connection
*conn
= connection_find(sip
, fd
);
245 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
246 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
252 static void connection_free_all(struct sipe_account_data
*sip
)
254 struct sip_connection
*ret
= NULL
;
255 GSList
*entry
= sip
->openconns
;
258 connection_remove(sip
, ret
->fd
);
259 entry
= sip
->openconns
;
263 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
265 const gchar
*method
= msg
->method
;
266 const gchar
*target
= msg
->target
;
271 const char *authdomain
= sip
->authdomain
;
272 const char *authuser
= sip
->authuser
;
273 //const char *krb5_realm;
275 //gchar *krb5_token = NULL;
277 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
278 // and do error checking
280 // KRB realm should always be uppercase
281 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
283 if (sip
->realhostname
) {
284 host
= sip
->realhostname
;
285 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
286 host
= purple_account_get_string(sip
->account
, "proxy", "");
288 host
= sip
->sipdomain
;
291 /*gboolean new_auth = krb5_auth.gss_context == NULL;
293 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
296 if (new_auth || force_reauth) {
297 krb5_token = krb5_auth.base64_token;
300 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
301 krb5_token = krb5_auth.base64_token;*/
307 if (!authuser
|| strlen(authuser
) < 1) {
308 authuser
= sip
->username
;
311 if (auth
->type
== AUTH_TYPE_DIGEST
) { /* Digest */
312 sprintf(noncecount
, "%08d", auth
->nc
++);
313 response
= purple_cipher_http_digest_calculate_response(
314 "md5", method
, target
, NULL
, NULL
,
315 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
316 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
318 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
);
321 } else if (auth
->type
== AUTH_TYPE_NTLM
) { /* NTLM */
322 // If we have a signature for the message, include that
323 if (msg
->signature
) {
324 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
);
328 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
329 const gchar
*ntlm_key
;
331 #if GLIB_CHECK_VERSION(2,8,0)
332 const gchar
* hostname
= g_get_host_name();
334 static char hostname
[256];
335 int ret
= gethostname(hostname
, sizeof(hostname
));
336 hostname
[sizeof(hostname
) - 1] = '\0';
337 if (ret
== -1 || hostname
[0] == '\0') {
338 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Error when getting host name. Using \"localhost.\"\n");
340 strcpy(hostname
, "localhost");
343 /*const gchar * hostname = purple_get_host_name();*/
345 gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, hostname
, authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
346 auth
->ntlm_key
= (gchar
*)ntlm_key
;
347 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
352 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
354 } else if (auth
->type
== AUTH_TYPE_KERBEROS
) {
357 /*if (new_auth || force_reauth) {
358 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
360 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);
362 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
365 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
366 gchar * mic = "MICTODO";
367 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
368 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
369 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
370 //auth->opaque ? auth->opaque : "", auth->target);
371 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
376 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
379 sprintf(noncecount
, "%08d", auth
->nc
++);
380 response
= purple_cipher_http_digest_calculate_response(
381 "md5", method
, target
, NULL
, NULL
,
382 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
383 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
385 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
);
390 static char *parse_attribute(const char *attrname
, const char *source
)
392 const char *tmp
, *tmp2
;
394 int len
= strlen(attrname
);
396 if (!strncmp(source
, attrname
, len
)) {
398 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
400 retval
= g_strndup(tmp
, tmp2
- tmp
);
402 retval
= g_strdup(tmp
);
408 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
411 const char *authuser
;
414 //const char *krb5_realm;
417 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
418 // and do error checking
420 // KRB realm should always be uppercase
421 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
423 if (sip->realhostname) {
424 host = sip->realhostname;
425 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
426 host = purple_account_get_string(sip->account, "proxy", "");
428 host = sip->sipdomain;
431 authuser
= sip
->authuser
;
433 if (!authuser
|| strlen(authuser
) < 1) {
434 authuser
= sip
->username
;
438 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
442 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
443 auth
->type
= AUTH_TYPE_NTLM
;
444 parts
= g_strsplit(hdr
+5, "\", ", 0);
447 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
448 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
450 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
453 if ((tmp
= parse_attribute("targetname=\"",
455 g_free(auth
->target
);
458 else if ((tmp
= parse_attribute("realm=\"",
463 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
464 g_free(auth
->opaque
);
471 if (!strstr(hdr
, "gssapi-data")) {
479 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
480 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
481 auth
->type
= AUTH_TYPE_KERBEROS
;
482 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
483 parts
= g_strsplit(hdr
+9, "\", ", 0);
486 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
487 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
488 /*if (krb5_auth.gss_context == NULL) {
489 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
491 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
494 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
495 g_free(auth
->target
);
497 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
500 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
501 g_free(auth
->opaque
);
511 auth
->type
= AUTH_TYPE_DIGEST
;
512 parts
= g_strsplit(hdr
, " ", 0);
514 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
518 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
526 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
528 g_free(auth
->digest_session_key
);
529 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
530 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
536 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
538 PurpleConnection
*gc
= data
;
539 struct sipe_account_data
*sip
= gc
->proto_data
;
543 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
545 if (max_write
== 0) {
546 if (sip
->tx_handler
!= 0){
547 purple_input_remove(sip
->tx_handler
);
553 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
555 if (written
< 0 && errno
== EAGAIN
)
557 else if (written
<= 0) {
558 /*TODO: do we really want to disconnect on a failure to write?*/
559 purple_connection_error(gc
, _("Could not write"));
563 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
566 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
568 PurpleConnection
*gc
= data
;
569 struct sipe_account_data
*sip
= gc
->proto_data
;
573 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
575 if (max_write
== 0) {
576 if (sip
->tx_handler
!= 0) {
577 purple_input_remove(sip
->tx_handler
);
583 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
585 if (written
< 0 && errno
== EAGAIN
)
587 else if (written
<= 0) {
588 /*TODO: do we really want to disconnect on a failure to write?*/
589 purple_connection_error(gc
, _("Could not write"));
593 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
596 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
598 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
600 PurpleConnection
*gc
= data
;
601 struct sipe_account_data
*sip
;
602 struct sip_connection
*conn
;
604 if (!PURPLE_CONNECTION_IS_VALID(gc
))
612 purple_connection_error(gc
, _("Could not connect"));
616 sip
= gc
->proto_data
;
618 sip
->connecting
= FALSE
;
619 sip
->last_keepalive
= time(NULL
);
621 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
623 /* If there is more to write now, we need to register a handler */
624 if (sip
->txbuf
->bufused
> 0)
625 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
627 conn
= connection_create(sip
, source
);
628 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
631 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
633 struct sipe_account_data
*sip
;
634 struct sip_connection
*conn
;
636 if (!PURPLE_CONNECTION_IS_VALID(gc
))
638 if (gsc
) purple_ssl_close(gsc
);
642 sip
= gc
->proto_data
;
645 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
646 sip
->connecting
= FALSE
;
647 sip
->last_keepalive
= time(NULL
);
649 conn
= connection_create(sip
, gsc
->fd
);
651 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
656 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
658 PurpleConnection
*gc
= data
;
659 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
660 if (sip
== NULL
) return;
662 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
664 /* If there is more to write now */
665 if (sip
->txbuf
->bufused
> 0) {
666 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
671 static void sendlater(PurpleConnection
*gc
, const char *buf
)
673 struct sipe_account_data
*sip
= gc
->proto_data
;
675 if (!sip
->connecting
) {
676 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
677 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
678 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
680 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
681 purple_connection_error(gc
, _("Couldn't create socket"));
684 sip
->connecting
= TRUE
;
687 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
688 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
690 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
693 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
695 struct sipe_account_data
*sip
= gc
->proto_data
;
696 time_t currtime
= time(NULL
);
697 int writelen
= strlen(buf
);
699 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
700 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
701 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
702 purple_debug_info("sipe", "could not send packet\n");
711 if (sip
->tx_handler
) {
716 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
718 ret
= write(sip
->fd
, buf
, writelen
);
722 if (ret
< 0 && errno
== EAGAIN
)
724 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
729 if (ret
< writelen
) {
730 if (!sip
->tx_handler
){
732 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
735 sip
->tx_handler
= purple_input_add(sip
->fd
,
736 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
741 /* XXX: is it OK to do this? You might get part of a request sent
742 with part of another. */
743 if (sip
->txbuf
->bufused
> 0)
744 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
746 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
752 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
754 sendout_pkt(gc
, buf
);
758 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
760 GSList
*tmp
= msg
->headers
;
763 GString
*outstr
= g_string_new("");
764 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
766 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
767 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
768 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
769 tmp
= g_slist_next(tmp
);
771 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
772 sendout_pkt(sip
->gc
, outstr
->str
);
773 g_string_free(outstr
, TRUE
);
776 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
779 if (sip
->registrar
.ntlm_key
) {
780 struct sipmsg_breakdown msgbd
;
781 gchar
*signature_input_str
;
783 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
784 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
785 sip
->registrar
.ntlm_num
++;
786 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
787 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
788 if (signature_input_str
!= NULL
) {
789 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
790 msg
->rand
= g_strdup(msgbd
.rand
);
791 msg
->num
= g_strdup(msgbd
.num
);
792 g_free(signature_input_str
);
794 sipmsg_breakdown_free(&msgbd
);
797 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
798 buf
= auth_header(sip
, &sip
->registrar
, msg
);
799 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
800 sipmsg_add_header(msg
, "Authorization", buf
);
802 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
805 } 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")) {
806 sip
->registrar
.nc
= 3;
807 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
809 buf
= auth_header(sip
, &sip
->registrar
, msg
);
810 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
813 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
817 static char *get_contact(struct sipe_account_data
*sip
)
819 return g_strdup(sip
->contact
);
824 static char *get_contact_service(struct sipe_account_data *sip)
826 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()));
827 //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);
831 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
832 const char *text
, const char *body
)
836 GString
*outstr
= g_string_new("");
837 struct sipe_account_data
*sip
= gc
->proto_data
;
841 sipmsg_remove_header(msg
, "ms-user-data");
843 contact
= get_contact(sip
);
844 sipmsg_remove_header(msg
, "Contact");
845 sipmsg_add_header(msg
, "Contact", contact
);
848 /* When sending the acknowlegements and errors, the content length from the original
849 message is still here, but there is no body; we need to make sure we're sending the
850 correct content length */
851 sipmsg_remove_header(msg
, "Content-Length");
854 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
855 sipmsg_add_header(msg
, "Content-Length", len
);
857 sipmsg_remove_header(msg
, "Content-Type");
858 sipmsg_add_header(msg
, "Content-Length", "0");
861 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
862 //gchar * mic = "MICTODO";
863 msg
->response
= code
;
865 sipmsg_remove_header(msg
, "Authentication-Info");
866 sign_outgoing_message(msg
, sip
, msg
->method
);
868 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
871 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
872 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
874 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
875 tmp
= g_slist_next(tmp
);
877 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
878 sendout_pkt(gc
, outstr
->str
);
879 g_string_free(outstr
, TRUE
);
882 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
884 if (trans
->msg
) sipmsg_free(trans
->msg
);
885 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
889 static struct transaction
*
890 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
892 struct transaction
*trans
= g_new0(struct transaction
, 1);
893 trans
->time
= time(NULL
);
894 trans
->msg
= (struct sipmsg
*)msg
;
895 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
896 trans
->callback
= callback
;
897 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
901 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
903 struct transaction
*trans
;
904 GSList
*transactions
= sip
->transactions
;
905 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
907 while (transactions
) {
908 trans
= transactions
->data
;
909 if (!strcmp(trans
->cseq
, cseq
)) {
912 transactions
= transactions
->next
;
918 static struct transaction
*
919 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
920 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
921 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
923 struct sipe_account_data
*sip
= gc
->proto_data
;
924 const char *addh
= "";
927 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
928 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
929 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
930 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
931 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
932 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
933 gchar
*route
= strdup("");
934 gchar
*epid
= get_epid(sip
); // TODO generate one per account/login
935 struct transaction
*trans
;
937 if (dialog
&& dialog
->routes
)
939 GSList
*iter
= dialog
->routes
;
944 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
946 iter
= g_slist_next(iter
);
950 if (!ourtag
&& !dialog
) {
954 if (!strcmp(method
, "REGISTER")) {
955 if (sip
->regcallid
) {
957 callid
= g_strdup(sip
->regcallid
);
959 sip
->regcallid
= g_strdup(callid
);
963 if (addheaders
) addh
= addheaders
;
965 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
966 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
967 "From: <sip:%s>%s%s;epid=%s\r\n"
968 "To: <%s>%s%s%s%s\r\n"
969 "Max-Forwards: 70\r\n"
974 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
976 dialog
&& dialog
->request
? dialog
->request
: url
,
977 TRANSPORT_DESCRIPTOR
,
978 purple_network_get_my_ip(-1),
980 branch
? ";branch=" : "",
981 branch
? branch
: "",
983 ourtag
? ";tag=" : "",
984 ourtag
? ourtag
: "",
987 theirtag
? ";tag=" : "",
988 theirtag
? theirtag
: "",
989 theirepid
? ";epid=" : "",
990 theirepid
? theirepid
: "",
991 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
997 body
? strlen(body
) : 0,
1001 //printf ("parsing msg buf:\n%s\n\n", buf);
1002 msg
= sipmsg_parse_msg(buf
);
1013 sign_outgoing_message (msg
, sip
, method
);
1015 buf
= sipmsg_to_string (msg
);
1017 /* add to ongoing transactions */
1018 trans
= transactions_add_buf(sip
, msg
, tc
);
1019 sendout_pkt(gc
, buf
);
1025 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
1027 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
1028 gchar
*contact
= get_contact(sip
);
1029 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
1030 "Content-Type: application/SOAP+xml\r\n",contact
);
1032 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
1033 tr
->payload
= payload
;
1040 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
1042 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
1045 static char *get_contact_register(struct sipe_account_data
*sip
)
1047 char *epid
= get_epid(sip
);
1048 char *uuid
= generateUUIDfromEPID(epid
);
1049 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
);
1055 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1057 char *expires
= expire
>= 0 ? g_strdup_printf("Expires: %d\r\n", expire
) : g_strdup("");
1058 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1059 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1060 char *contact
= get_contact_register(sip
);
1061 char *hdr
= g_strdup_printf("Contact: %s\r\n"
1062 "Supported: gruu-10, adhoclist, msrtc-event-categories, com.microsoft.msrtc.presence\r\n"
1063 "Event: registration\r\n"
1064 "Allow-Events: presence\r\n"
1065 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1066 "%s", contact
, expires
);
1070 sip
->registerstatus
= 1;
1072 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1073 process_register_response
);
1080 static void do_register_cb(struct sipe_account_data
*sip
)
1082 do_register_exp(sip
, -1);
1083 sip
->reregister_set
= FALSE
;
1086 static void do_register(struct sipe_account_data
*sip
)
1088 do_register_exp(sip
, -1);
1092 * Returns URI from provided To or From header.
1094 * Needs to g_free() after use.
1096 * @return URI with sip: prefix
1098 static gchar
*parse_from(const gchar
*hdr
)
1101 const gchar
*tmp
, *tmp2
= hdr
;
1103 if (!hdr
) return NULL
;
1104 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1105 tmp
= strchr(hdr
, '<');
1107 /* i hate the different SIP UA behaviours... */
1108 if (tmp
) { /* sip address in <...> */
1110 tmp
= strchr(tmp2
, '>');
1112 from
= g_strndup(tmp2
, tmp
- tmp2
);
1114 purple_debug_info("sipe", "found < without > in From\n");
1118 tmp
= strchr(tmp2
, ';');
1120 from
= g_strndup(tmp2
, tmp
- tmp2
);
1122 from
= g_strdup(tmp2
);
1125 purple_debug_info("sipe", "got %s\n", from
);
1129 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1132 xmlnode
* node
= NULL
;
1135 va_start(args
, parent
);
1136 while ((name
= va_arg(args
, const char *)) != NULL
) {
1137 node
= xmlnode_get_child(parent
, name
);
1138 if (node
== NULL
) return NULL
;
1148 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1150 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1151 send_soap_request(sip
, body
);
1156 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1159 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1161 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1164 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1168 void sipe_auth_user_cb(void * data
)
1170 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1173 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1178 void sipe_deny_user_cb(void * data
)
1180 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1183 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1188 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1190 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1191 sipe_contact_allow_deny(sip
, name
, TRUE
);
1195 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1197 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1198 sipe_contact_allow_deny(sip
, name
, FALSE
);
1202 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1204 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1205 sipe_contact_set_acl(sip, name, "");
1209 sipe_process_presence_wpending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1213 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1214 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1216 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1218 watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1219 if (!watchers
) return;
1221 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1222 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1223 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1224 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1226 // TODO pull out optional displayName to pass as alias
1228 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1229 job
->who
= remote_user
;
1231 purple_account_request_authorization(
1245 xmlnode_free(watchers
);
1250 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1252 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1253 if (!purple_group
) {
1254 purple_group
= purple_group_new(group
->name
);
1255 purple_blist_add_group(purple_group
, NULL
);
1259 group
->purple_group
= purple_group
;
1260 sip
->groups
= g_slist_append(sip
->groups
, group
);
1261 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1263 purple_debug_info("sipe", "did not add group %s\n", group
->name
? group
->name
: "");
1267 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1269 struct sipe_group
*group
;
1275 entry
= sip
->groups
;
1277 group
= entry
->data
;
1278 if (group
->id
== id
) {
1281 entry
= entry
->next
;
1286 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1288 struct sipe_group
*group
;
1294 entry
= sip
->groups
;
1296 group
= entry
->data
;
1297 if (!strcmp(group
->name
, name
)) {
1300 entry
= entry
->next
;
1306 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1309 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1310 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1311 send_soap_request(sip
, body
);
1313 g_free(group
->name
);
1314 group
->name
= g_strdup(name
);
1318 * Only appends if no such value already stored.
1321 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
1322 GSList
* res
= list
;
1323 if (!g_slist_find_custom(list
, data
, func
)) {
1324 res
= g_slist_insert_sorted(list
, data
, func
);
1330 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
1331 return group1
->id
- group2
->id
;
1335 * Returns string like "2 4 7 8" - group ids buddy belong to.
1338 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
1341 //creating array from GList, converting int to gchar*
1342 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
1343 GSList
*entry
= buddy
->groups
;
1345 struct sipe_group
* group
= entry
->data
;
1346 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
1347 entry
= entry
->next
;
1351 res
= g_strjoinv(" ", ids_arr
);
1352 g_strfreev(ids_arr
);
1357 * Sends buddy update to server
1360 sipe_group_set_user (struct sipe_account_data
*sip
, const gchar
* who
)
1362 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1363 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
1365 if (buddy
&& purple_buddy
) {
1366 gchar
*alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1368 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
1369 purple_debug_info("sipe", "Saving buddy %s with alias %s and groups %s\n", who
, alias
, groups
);
1371 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
1372 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
1374 send_soap_request(sip
, body
);
1380 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1382 if (msg
->response
== 200) {
1383 struct sipe_group
*group
;
1384 struct group_user_context
*ctx
= (struct group_user_context
*)tc
->payload
;
1388 struct sipe_buddy
*buddy
;
1390 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1396 node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1403 group_id
= xmlnode_get_data(node
);
1410 group
= g_new0(struct sipe_group
, 1);
1411 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1413 group
->name
= ctx
->group_name
;
1415 sipe_group_add(sip
, group
);
1417 buddy
= g_hash_table_lookup(sip
->buddies
, ctx
->user_name
);
1419 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1422 sipe_group_set_user(sip
, ctx
->user_name
);
1431 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1433 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1435 ctx
->group_name
= g_strdup(name
);
1436 ctx
->user_name
= g_strdup(who
);
1438 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1439 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1445 * Should return FALSE if repetitive action is not needed
1447 gboolean
sipe_scheduled_exec(struct scheduled_action
*sched_action
)
1450 purple_debug_info("sipe", "sipe_scheduled_exec: executing\n");
1451 sched_action
->sip
->timeouts
= g_slist_remove(sched_action
->sip
->timeouts
, sched_action
);
1452 purple_debug_info("sipe", "sip->timeouts count:%d after removal\n",g_slist_length(sched_action
->sip
->timeouts
));
1453 (sched_action
->action
)(sched_action
->sip
, sched_action
->payload
);
1454 ret
= sched_action
->repetitive
;
1455 g_free(sched_action
->payload
);
1456 g_free(sched_action
->name
);
1457 g_free(sched_action
);
1462 * Do schedule action for execution in the future.
1463 * Non repetitive execution.
1465 * @param name of action (will be copied)
1466 * @param timeout in seconds
1467 * @action callback function
1468 * @payload callback data (can be NULL, otherwise caller must allocate memory)
1470 void sipe_schedule_action(gchar
*name
, int timeout
, Action action
, struct sipe_account_data
*sip
, void * payload
)
1472 struct scheduled_action
*sched_action
;
1474 purple_debug_info("sipe","scheduling action %s timeout:%d\n", name
, timeout
);
1475 sched_action
= g_new0(struct scheduled_action
, 1);
1476 sched_action
->repetitive
= FALSE
;
1477 sched_action
->name
= g_strdup(name
);
1478 sched_action
->action
= action
;
1479 sched_action
->sip
= sip
;
1480 sched_action
->payload
= payload
;
1481 sched_action
->timeout_handler
= purple_timeout_add_seconds(timeout
, (GSourceFunc
) sipe_scheduled_exec
, sched_action
);
1482 sip
->timeouts
= g_slist_append(sip
->timeouts
, sched_action
);
1483 purple_debug_info("sipe", "sip->timeouts count:%d after addition\n",g_slist_length(sip
->timeouts
));
1487 * Kills action timer effectively cancelling
1490 * @param name of action
1492 void sipe_cancel_scheduled_action(struct sipe_account_data
*sip
, gchar
*name
)
1496 if (!sip
->timeouts
|| !name
) return;
1498 entry
= sip
->timeouts
;
1500 struct scheduled_action
*sched_action
= entry
->data
;
1501 if(!strcmp(sched_action
->name
, name
)) {
1502 GSList
*to_delete
= entry
;
1503 entry
= entry
->next
;
1504 sip
->timeouts
= g_slist_delete_link(sip
->timeouts
, to_delete
);
1505 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
1506 purple_timeout_remove(sched_action
->timeout_handler
);
1507 g_free(sched_action
->payload
);
1508 g_free(sched_action
->name
);
1509 g_free(sched_action
);
1511 entry
= entry
->next
;
1516 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
);
1518 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1520 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
1522 process_incoming_notify(sip
, msg
, FALSE
, FALSE
);
1527 static void sipe_subscribe_resource_uri(const char *name
, gpointer value
, gchar
**resources_uri
)
1529 gchar
*tmp
= *resources_uri
;
1530 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
1535 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
1536 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
1537 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
1538 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
1539 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
1542 static void sipe_subscribe_presence_batched(struct sipe_account_data
*sip
){
1543 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1544 gchar
*contact
= get_contact(sip
);
1547 gchar
*resources_uri
= g_strdup("");
1548 gchar
*require
= "";
1550 gchar
*content_type
;
1552 g_hash_table_foreach(sip
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
1554 if (sip
->msrtc_event_categories
) {
1555 require
= ", categoryList";
1556 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
1557 content_type
= "application/msrtc-adrl-categorylist+xml";
1558 content
= g_strdup_printf(
1559 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1560 "<action name=\"subscribe\" id=\"63792024\">\n"
1561 "<adhocList>\n%s</adhocList>\n"
1562 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1563 "<category name=\"note\"/>\n"
1564 "<category name=\"state\"/>\n"
1567 "</batchSub>", sip
->username
, resources_uri
);
1569 content_type
= "application/adrl+xml";
1570 content
= g_strdup_printf(
1571 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
1572 "<create xmlns=\"\">\n%s</create>\n"
1573 "</adhoclist>\n", sip
->username
, sip
->username
, resources_uri
);
1575 g_free(resources_uri
);
1577 request
= g_strdup_printf(
1578 "Require: adhoclist%s\r\n"
1579 "Supported: eventlist\r\n"
1580 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
1581 "Supported: ms-piggyback-first-notify\r\n"
1582 "Supported: com.microsoft.autoextend\r\n"
1583 "Supported: ms-benotify\r\n"
1584 "Proxy-Require: ms-benotify\r\n"
1585 "Event: presence\r\n"
1586 "Content-Type: %s\r\n"
1587 "Contact: %s\r\n", require
, accept
, content_type
, contact
);
1590 /* subscribe to buddy presence */
1591 //send_sip_request(sip->gc, "SUBSCRIBE", resource_uri, resource_uri, request, content, NULL, process_subscribe_response);
1592 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1600 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
1601 * The user sends a single SUBSCRIBE request to the subscribed contact.
1602 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
1606 static void sipe_subscribe_presence_single(struct sipe_account_data
*sip
, const char * buddy_name
)
1608 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1609 gchar
*tmp
= get_contact(sip
);
1612 request
= g_strdup_printf(
1613 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1614 "Supported: ms-piggyback-first-notify\r\n"
1615 "Supported: com.microsoft.autoextend\r\n"
1616 "Supported: ms-benotify\r\n"
1617 "Proxy-Require: ms-benotify\r\n"
1618 "Event: presence\r\n"
1619 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1620 "Contact: %s\r\n", tmp
);
1622 content
= g_strdup_printf(
1623 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1624 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1625 "<resource uri=\"%s\"/>\n"
1627 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1628 "<category name=\"note\"/>\n"
1629 "<category name=\"state\"/>\n"
1632 "</batchSub>", sip
->username
, to
1637 /* subscribe to buddy presence */
1638 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1645 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1647 if (!purple_status_is_active(status
))
1651 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
1654 g_free(sip
->status
);
1655 sip
->status
= g_strdup(purple_status_get_id(status
));
1656 send_presence_status(sip
);
1662 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1664 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1665 sipe_group_set_user(sip
, name
);
1669 sipe_group_buddy(PurpleConnection
*gc
,
1671 const char *old_group_name
,
1672 const char *new_group_name
)
1674 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1675 struct sipe_buddy
* buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1676 struct sipe_group
* old_group
= NULL
;
1677 struct sipe_group
* new_group
;
1679 purple_debug_info("sipe", "sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s\n",
1680 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1682 if(!buddy
) { // buddy not in roaming list
1686 if (old_group_name
) {
1687 old_group
= sipe_group_find_by_name(sip
, g_strdup(old_group_name
));
1689 new_group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1692 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1693 purple_debug_info("sipe", "buddy %s removed from old group %s\n", who
, old_group_name
);
1697 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1699 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1700 sipe_group_set_user(sip
, who
);
1704 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1706 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1707 struct sipe_buddy
*b
;
1709 purple_debug_info("sipe", "sipe_add_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1711 // Prepend sip: if needed
1712 if (strncmp("sip:", buddy
->name
, 4)) {
1713 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1714 purple_blist_rename_buddy(buddy
, buf
);
1718 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1719 b
= g_new0(struct sipe_buddy
, 1);
1720 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1721 b
->name
= g_strdup(buddy
->name
);
1722 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1723 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1724 sipe_subscribe_presence_single(sip
, b
->name
); //@TODO should go to callback
1726 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1730 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1732 g_free(buddy
->name
);
1733 g_free(buddy
->annotation
);
1734 g_free(buddy
->device_name
);
1735 g_slist_free(buddy
->groups
);
1740 * Unassociates buddy from group first.
1741 * Then see if no groups left, removes buddy completely.
1742 * Otherwise updates buddy groups on server.
1744 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1746 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1747 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1748 struct sipe_group
*g
= NULL
;
1750 purple_debug_info("sipe", "sipe_remove_buddy[CB]: buddy:%s group:%s\n", buddy
? buddy
->name
: "", group
? group
->name
: "");
1755 g
= sipe_group_find_by_name(sip
, group
->name
);
1759 b
->groups
= g_slist_remove(b
->groups
, g
);
1760 purple_debug_info("sipe", "buddy %s removed from group %s\n", buddy
->name
, g
->name
);
1763 if (g_slist_length(b
->groups
) < 1) {
1764 gchar
*action_name
= g_strdup_printf("<%s><%s>", "presence", buddy
->name
);
1765 sipe_cancel_scheduled_action(sip
, action_name
);
1766 g_free(action_name
);
1768 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1771 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1772 send_soap_request(sip
, body
);
1778 //updates groups on server
1779 sipe_group_set_user(sip
, b
->name
);
1785 sipe_rename_group(PurpleConnection
*gc
,
1786 const char *old_name
,
1788 GList
*moved_buddies
)
1790 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1791 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1793 sipe_group_rename(sip
, s_group
, group
->name
);
1795 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1800 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1802 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1803 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1806 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1807 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1808 send_soap_request(sip
, body
);
1811 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1812 g_free(s_group
->name
);
1815 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1819 static GList
*sipe_status_types(PurpleAccount
*acc
)
1821 PurpleStatusType
*type
;
1822 GList
*types
= NULL
;
1825 type
= purple_status_type_new_with_attrs(
1826 PURPLE_STATUS_AVAILABLE
, NULL
, _("Online"), TRUE
, TRUE
, FALSE
,
1827 // Translators: noun
1828 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1830 types
= g_list_append(types
, type
);
1833 type
= purple_status_type_new_with_attrs(
1834 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_BUSY
, _("Busy"), TRUE
, TRUE
, FALSE
,
1835 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1837 types
= g_list_append(types
, type
);
1839 // Do Not Disturb (not user settable)
1840 type
= purple_status_type_new_with_attrs(
1841 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_DND
, _("Do Not Disturb"), TRUE
, FALSE
, FALSE
,
1842 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1844 types
= g_list_append(types
, type
);
1847 type
= purple_status_type_new_with_attrs(
1848 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_BRB
, _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1849 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1851 types
= g_list_append(types
, type
);
1854 type
= purple_status_type_new_with_attrs(
1855 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1856 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1858 types
= g_list_append(types
, type
);
1861 type
= purple_status_type_new_with_attrs(
1862 PURPLE_STATUS_UNAVAILABLE
, SIPE_STATUS_ID_ONPHONE
, _("On The Phone"), TRUE
, TRUE
, FALSE
,
1863 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1865 types
= g_list_append(types
, type
);
1868 type
= purple_status_type_new_with_attrs(
1869 PURPLE_STATUS_AWAY
, SIPE_STATUS_ID_LUNCH
, _("Out To Lunch"), TRUE
, TRUE
, FALSE
,
1870 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1872 types
= g_list_append(types
, type
);
1875 type
= purple_status_type_new_full(
1876 PURPLE_STATUS_INVISIBLE
, NULL
, _("Appear Offline"), TRUE
, TRUE
, FALSE
);
1877 types
= g_list_append(types
, type
);
1880 type
= purple_status_type_new_full(
1881 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1882 types
= g_list_append(types
, type
);
1888 * A callback for g_hash_table_foreach
1890 static void sipe_buddy_subscribe_cb(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1892 sipe_subscribe_presence_single(sip
, buddy
->name
);
1896 * Removes entries from purple buddy list
1897 * that does not correspond ones in the roaming contact list.
1899 static void sipe_cleanup_local_blist(struct sipe_account_data
*sip
) {
1900 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1901 GSList
*entry
= buddies
;
1902 struct sipe_buddy
*buddy
;
1906 purple_debug_info("sipe", "sipe_cleanup_local_blist: overall %d Purple buddies (including clones)\n", g_slist_length(buddies
));
1907 purple_debug_info("sipe", "sipe_cleanup_local_blist: %d sipe buddies (unique)\n", g_hash_table_size(sip
->buddies
));
1910 g
= purple_buddy_get_group(b
);
1911 buddy
= g_hash_table_lookup(sip
->buddies
, b
->name
);
1913 gboolean in_sipe_groups
= FALSE
;
1914 GSList
*entry2
= buddy
->groups
;
1916 struct sipe_group
*group
= entry2
->data
;
1917 if (!strcmp(group
->name
, g
->name
)) {
1918 in_sipe_groups
= TRUE
;
1921 entry2
= entry2
->next
;
1923 if(!in_sipe_groups
) {
1924 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as not having this group in roaming list\n", b
->name
, g
->name
);
1925 purple_blist_remove_buddy(b
);
1928 purple_debug_info("sipe", "*** REMOVING %s from Purple group: %s as this buddy not in roaming list\n", b
->name
, g
->name
);
1929 purple_blist_remove_buddy(b
);
1931 entry
= entry
->next
;
1935 static gboolean
sipe_process_roaming_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1937 int len
= msg
->bodylen
;
1939 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1942 const gchar
*contacts_delta
;
1943 xmlnode
*group_node
;
1944 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1948 /* Convert the contact from XML to Purple Buddies */
1949 isc
= xmlnode_from_str(msg
->body
, len
);
1954 contacts_delta
= xmlnode_get_attrib(isc
, "deltaNum");
1955 if (contacts_delta
) {
1956 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1960 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1961 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1962 const char *name
= xmlnode_get_attrib(group_node
, "name");
1964 if (!strncmp(name
, "~", 1)) {
1966 name
= "Other Contacts";
1968 group
->name
= g_strdup(name
);
1969 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1971 sipe_group_add(sip
, group
);
1974 // Make sure we have at least one group
1975 if (g_slist_length(sip
->groups
) == 0) {
1976 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1977 PurpleGroup
*purple_group
;
1979 group
->name
= g_strdup("Other Contacts");
1981 purple_group
= purple_group_new(group
->name
);
1982 purple_blist_add_group(purple_group
, NULL
);
1983 sip
->groups
= g_slist_append(sip
->groups
, group
);
1986 /* Parse contacts */
1987 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1988 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1989 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1990 gchar
* groups
= g_strdup(xmlnode_get_attrib(item
, "groups"));
1991 gchar
* buddy_name
= g_strdup_printf("sip:%s", uri
);
1992 gchar
**item_groups
;
1993 struct sipe_group
*group
= NULL
;
1994 struct sipe_buddy
*buddy
= NULL
;
1997 // assign to group Other Contacts if nothing else received
1998 if(!groups
|| !strcmp("", groups
) ) {
1999 group
= sipe_group_find_by_name(sip
, "Other Contacts");
2000 groups
= group
? g_strdup_printf("%d", group
->id
) : "1";
2003 item_groups
= g_strsplit(groups
, " ", 0);
2005 while (item_groups
[i
]) {
2006 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[i
], NULL
));
2008 // If couldn't find the right group for this contact, just put them in the first group we have
2009 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
2010 group
= sip
->groups
->data
;
2013 if (group
!= NULL
) {
2014 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
2016 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
2017 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
2020 if (!g_ascii_strcasecmp(uri
, purple_buddy_get_alias(b
))) {
2021 if (name
!= NULL
&& strlen(name
) != 0) {
2022 purple_blist_alias_buddy(b
, name
);
2027 buddy
= g_new0(struct sipe_buddy
, 1);
2028 buddy
->name
= g_strdup(b
->name
);
2029 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
2032 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
2034 purple_debug_info("sipe", "Added buddy %s to group %s\n", b
->name
, group
->name
);
2036 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
2041 } // while, contact groups
2042 g_strfreev(item_groups
);
2052 sipe_cleanup_local_blist(sip
);
2054 //subscribe to buddies
2055 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
2056 //if(sip->msrtc_event_categories){
2057 sipe_subscribe_presence_batched(sip
);
2059 //g_hash_table_foreach(sip->buddies, (GHFunc)sipe_buddy_subscribe_cb, (gpointer)sip);
2061 sip
->subscribed_buddies
= TRUE
;
2068 * Subscribe roaming contacts
2070 static void sipe_subscribe_roaming_contacts(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2072 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2073 gchar
*tmp
= get_contact(sip
);
2074 gchar
*hdr
= g_strdup_printf(
2075 "Event: vnd-microsoft-roaming-contacts\r\n"
2076 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
2077 "Supported: com.microsoft.autoextend\r\n"
2078 "Supported: ms-benotify\r\n"
2079 "Proxy-Require: ms-benotify\r\n"
2080 "Supported: ms-piggyback-first-notify\r\n"
2081 "Contact: %s\r\n", tmp
);
2084 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2089 static void sipe_subscribe_presence_wpending(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2091 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2092 gchar
*tmp
= get_contact(sip
);
2093 gchar
*hdr
= g_strdup_printf(
2094 "Event: presence.wpending\r\n"
2095 "Accept: text/xml+msrtc.wpending\r\n"
2096 "Supported: com.microsoft.autoextend\r\n"
2097 "Supported: ms-benotify\r\n"
2098 "Proxy-Require: ms-benotify\r\n"
2099 "Supported: ms-piggyback-first-notify\r\n"
2100 "Contact: %s\r\n", tmp
);
2103 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2109 * Fires on deregistration event initiated by server.
2110 * [MS-SIPREGE] SIP extension.
2115 // Content-Type: text/registration-event
2116 // subscription-state: terminated;expires=0
2117 // ms-diagnostics-public: 4141;reason="User disabled"
2119 // deregistered;event=rejected
2121 static void sipe_process_registration_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2123 gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
2124 gchar
*event
= NULL
;
2125 gchar
*reason
= NULL
;
2126 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2128 warning
= warning
? warning
: sipmsg_find_header(msg
, "ms-diagnostics-public");
2129 purple_debug_info("sipe", "sipe_process_registration_notify: deregistration received.\n");
2131 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
2132 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
2133 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
2134 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
2136 purple_debug_info("sipe", "sipe_process_registration_notify: unknown content type, exiting.\n");
2140 if (warning
!= NULL
) {
2141 reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2142 } else { // for LCS2005
2144 if (event
&& !g_ascii_strcasecmp(event
, "unregistered")) {
2145 error_id
= 4140; // [MS-SIPREGE]
2146 //reason = g_strdup(_("User logged out")); // [MS-OCER]
2147 reason
= g_strdup(_("You have been signed off because you've signed in at another location"));
2148 } else if (event
&& !g_ascii_strcasecmp(event
, "rejected")) {
2150 reason
= g_strdup(_("User disabled")); // [MS-OCER]
2151 } else if (event
&& !g_ascii_strcasecmp(event
, "deactivated")) {
2153 reason
= g_strdup(_("User moved")); // [MS-OCER]
2157 warning
= g_strdup_printf(_("Unregistered by Server: %s."), reason
? reason
: _("no reason given"));
2160 sip
->gc
->wants_to_die
= TRUE
;
2161 purple_connection_error(sip
->gc
, warning
);
2166 static void sipe_process_roaming_acl(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2168 const gchar
*contacts_delta
;
2171 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2177 contacts_delta
= xmlnode_get_attrib(xml
, "deltaNum");
2180 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
2187 * When we receive some self (BE) NOTIFY with a new subscriber
2188 * we sends a setSubscribers request to him [SIP-PRES]
2192 static void sipe_process_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2199 purple_debug_info("sipe", "sipe_process_roaming_self\n");
2201 xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2204 contact
= get_contact(sip
);
2205 to
= g_strdup_printf("sip:%s", sip
->username
);
2207 for (node
= xmlnode_get_descendant(xml
, "subscribers", "subscriber", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
2212 user
= xmlnode_get_attrib(node
, "user");
2213 if (!user
) continue;
2215 purple_debug_info("sipe", "sipe_process_roaming_self: user %s\n", user
);
2217 hdr
= g_strdup_printf(
2219 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2221 body
= g_strdup_printf(
2222 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2223 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2224 "</setSubscribers>", user
);
2226 send_sip_request(sip
->gc
, "SERVICE", to
, to
, hdr
, body
, NULL
, NULL
);
2236 static void sipe_subscribe_roaming_acl(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2238 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2239 gchar
*tmp
= get_contact(sip
);
2240 gchar
*hdr
= g_strdup_printf(
2241 "Event: vnd-microsoft-roaming-ACL\r\n"
2242 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
2243 "Supported: com.microsoft.autoextend\r\n"
2244 "Supported: ms-benotify\r\n"
2245 "Proxy-Require: ms-benotify\r\n"
2246 "Supported: ms-piggyback-first-notify\r\n"
2247 "Contact: %s\r\n", tmp
);
2250 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, process_subscribe_response
);
2256 * To request for presence information about the user, access level settings that have already been configured by the user
2257 * to control who has access to what information, and the list of contacts who currently have outstanding subscriptions.
2258 * We wait (BE)NOTIFY messages with some info change (categories,containers, subscribers)
2261 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2263 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
2264 gchar
*tmp
= get_contact(sip
);
2265 gchar
*hdr
= g_strdup_printf(
2266 "Event: vnd-microsoft-roaming-self\r\n"
2267 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
2268 "Supported: com.microsoft.autoextend\r\n"
2269 "Supported: ms-benotify\r\n"
2270 "Proxy-Require: ms-benotify\r\n"
2271 "Supported: ms-piggyback-first-notify\r\n"
2273 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
2275 gchar
*body
=g_strdup(
2276 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
2277 "<roaming type=\"categories\"/>"
2278 "<roaming type=\"containers\"/>"
2279 "<roaming type=\"subscribers\"/></roamingList>");
2282 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2288 /** Subscription for provisioning information to help with initial
2289 * configuration. This subscription is a one-time query (denoted by the Expires header,
2290 * which asks for 0 seconds for the subscription lifetime). This subscription asks for server
2291 * configuration, meeting policies, and policy settings that Communicator must enforce.
2292 * TODO: for what we need this information.
2295 static void sipe_subscribe_roaming_provisioning(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-provisioning-v2\r\n"
2301 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
2302 "Supported: com.microsoft.autoextend\r\n"
2303 "Supported: ms-benotify\r\n"
2304 "Proxy-Require: ms-benotify\r\n"
2305 "Supported: ms-piggyback-first-notify\r\n"
2308 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
2309 gchar
*body
= g_strdup(
2310 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
2311 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
2312 "<provisioningGroup name=\"ucPolicy\"/>"
2313 "</provisioningGroupList>");
2316 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, process_subscribe_response
);
2322 /* IM Session (INVITE and MESSAGE methods) */
2324 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
2326 struct sip_im_session
*session
;
2328 if (sip
== NULL
|| who
== NULL
) {
2332 entry
= sip
->im_sessions
;
2334 session
= entry
->data
;
2335 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
2338 entry
= entry
->next
;
2343 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
2345 struct sip_im_session
*session
= find_im_session(sip
, who
);
2347 session
= g_new0(struct sip_im_session
, 1);
2348 session
->with
= g_strdup(who
);
2349 session
->unconfirmed_messages
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2350 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
2355 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2357 struct sip_dialog
*dialog
= session
->dialog
;
2360 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
2363 entry
= dialog
->routes
;
2365 g_free(entry
->data
);
2366 entry
= g_slist_remove(entry
, entry
->data
);
2368 entry
= dialog
->supported
;
2370 g_free(entry
->data
);
2371 entry
= g_slist_remove(entry
, entry
->data
);
2373 g_free(dialog
->callid
);
2374 g_free(dialog
->ourtag
);
2375 g_free(dialog
->theirtag
);
2376 g_free(dialog
->theirepid
);
2377 g_free(dialog
->request
);
2379 g_free(session
->dialog
);
2381 entry
= session
->outgoing_message_queue
;
2383 g_free(entry
->data
);
2384 entry
= g_slist_remove(entry
, entry
->data
);
2387 g_hash_table_destroy(session
->unconfirmed_messages
);
2389 g_free(session
->with
);
2394 process_options_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2396 gboolean ret
= TRUE
;
2398 if (msg
->response
!= 200) {
2399 purple_debug_info("sipe", "process_options_response: OPTIONS response is %d\n", msg
->response
);
2403 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
2409 * Asks UA/proxy about its capabilities.
2411 static void sipe_options_request(struct sipe_account_data
*sip
, const char *who
)
2413 gchar
*to
= strstr(who
, "sip:") ? g_strdup(who
) : g_strdup_printf("sip:%s", who
);
2414 gchar
*contact
= get_contact(sip
);
2416 request
= g_strdup_printf(
2417 "Accept: application/sdp\r\n"
2418 "Contact: %s\r\n", contact
);
2422 send_sip_request(sip
->gc
, "OPTIONS", to
, to
, request
, NULL
, NULL
, process_options_response
);
2428 static void sipe_present_message_undelivered_err(gchar
*with
, struct sipe_account_data
*sip
, gchar
*message
)
2430 char *msg
, *msg_tmp
;
2431 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
2432 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2434 msg_tmp
= g_strdup_printf( _("The following message could not be delivered to all recipients, "\
2435 "possibly because one or more persons are offline:\n%s") ,
2437 purple_conv_present_error(with
, sip
->account
, msg_tmp
);
2442 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
);
2445 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2447 gboolean ret
= TRUE
;
2448 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2449 struct sip_im_session
* session
= find_im_session(sip
, with
);
2450 struct sip_dialog
*dialog
;
2456 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
2461 dialog
= session
->dialog
;
2463 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
2468 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2469 key
= g_strdup_printf("<%s><%d><MESSAGE>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
));
2471 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2473 if (msg
->response
!= 200) {
2474 purple_debug_info("sipe", "process_message_response: MESSAGE response not 200\n");
2476 sipe_present_message_undelivered_err(with
, sip
, message
);
2477 im_session_destroy(sip
, session
);
2480 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2481 purple_debug_info("sipe", "process_message_response: removed message %s from unconfirmed_messages(count=%d)\n",
2482 key
, g_hash_table_size(session
->unconfirmed_messages
));
2488 sipe_im_process_queue(sip
, session
);
2492 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
2502 if (strncmp("sip:", session
->with
, 4)) {
2503 fullto
= g_strdup_printf("sip:%s", session
->with
);
2505 fullto
= g_strdup(session
->with
);
2508 sipe_parse_html(msg
, &msgformat
, &msgtext
);
2509 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
2511 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2514 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2517 msgr
= g_strdup("");
2520 tmp
= get_contact(sip
);
2521 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
2522 //hdr = g_strdup("Content-Type: text/rtf\r\n");
2523 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
2524 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: text/plain; charset=UTF-8%s\r\n",
2529 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msgtext
, session
->dialog
, process_message_response
);
2537 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
2539 GSList
*entry
= session
->outgoing_message_queue
;
2541 if (session
->outgoing_invite
) return; //do not send messages until INVITE responded.
2544 char *key
= g_strdup_printf("<%s><%d><MESSAGE>", session
->dialog
->callid
, (session
->dialog
->cseq
) + 1);
2545 char *queued_msg
= entry
->data
;
2546 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(queued_msg
));
2547 purple_debug_info("sipe", "sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)\n",
2548 key
, g_hash_table_size(session
->unconfirmed_messages
));
2550 sipe_send_message(sip
, session
, queued_msg
);
2551 entry
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2557 sipe_get_route_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2559 GSList
*hdr
= msg
->headers
;
2560 struct siphdrelement
*elem
;
2566 if(!g_ascii_strcasecmp(elem
->name
, "Record-Route"))
2568 gchar
*route
= sipmsg_find_part_of_header(elem
->value
, "<", ">", NULL
);
2569 dialog
->routes
= g_slist_append(dialog
->routes
, route
);
2571 hdr
= g_slist_next(hdr
);
2576 dialog
->routes
= g_slist_reverse(dialog
->routes
);
2581 dialog
->request
= dialog
->routes
->data
;
2582 dialog
->routes
= g_slist_remove(dialog
->routes
, dialog
->routes
->data
);
2585 contact
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "<", ">", NULL
);
2586 dialog
->routes
= g_slist_append(dialog
->routes
, contact
);
2590 sipe_get_supported_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2592 GSList
*hdr
= msg
->headers
;
2593 struct siphdrelement
*elem
;
2597 if(!g_ascii_strcasecmp(elem
->name
, "Supported")
2598 && !g_slist_find_custom(dialog
->supported
, elem
->value
, (GCompareFunc
)strcmp
))
2600 dialog
->supported
= g_slist_append(dialog
->supported
, g_strdup(elem
->value
));
2603 hdr
= g_slist_next(hdr
);
2608 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2610 gchar
*us
= outgoing
? "From" : "To";
2611 gchar
*them
= outgoing
? "To" : "From";
2613 dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
2614 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
2615 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
2616 if (!dialog
->theirepid
) {
2617 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
2619 if (!dialog
->theirepid
) {
2620 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
2623 sipe_get_route_header(msg
, dialog
, outgoing
);
2624 sipe_get_supported_header(msg
, dialog
, outgoing
);
2629 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2631 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
2632 struct sip_im_session
* session
= find_im_session(sip
, with
);
2633 struct sip_dialog
*dialog
;
2639 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2644 dialog
= session
->dialog
;
2646 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2651 sipe_parse_dialog(msg
, dialog
, TRUE
);
2653 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
2654 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
2656 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
2658 if (msg
->response
!= 200) {
2659 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2661 sipe_present_message_undelivered_err(with
, sip
, message
);
2662 im_session_destroy(sip
, session
);
2668 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
2669 session
->outgoing_invite
= NULL
;
2670 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)strcmp
)) {
2671 purple_debug_info("sipe", "process_invite_response: remote system accepted message in INVITE\n");
2672 if (session
->outgoing_message_queue
) {
2673 char *queued_msg
= session
->outgoing_message_queue
->data
;
2674 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
2679 sipe_im_process_queue(sip
, session
);
2681 g_hash_table_remove(session
->unconfirmed_messages
, key
);
2682 purple_debug_info("sipe", "process_invite_response: removed message %s from unconfirmed_messages(count=%d)\n",
2683 key
, g_hash_table_size(session
->unconfirmed_messages
));
2691 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
*session
, const gchar
*msg_body
)
2700 char *ms_text_format
;
2705 if (session
->dialog
) {
2706 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
2710 session
->dialog
= g_new0(struct sip_dialog
, 1);
2711 session
->dialog
->callid
= gencallid();
2713 if (strstr(session
->with
, "sip:")) {
2714 to
= g_strdup(session
->with
);
2716 to
= g_strdup_printf("sip:%s", session
->with
);
2719 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
2720 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
2722 msgr_value
= sipmsg_get_msgr_string(msgformat
);
2726 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2730 base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
2731 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
2736 key
= g_strdup_printf("<%s><%d><INVITE>", session
->dialog
->callid
, (session
->dialog
->cseq
) + 1);
2737 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), g_strdup(msg_body
));
2738 purple_debug_info("sipe", "sipe_im_send: added message %s to unconfirmed_messages(count=%d)\n",
2739 key
, g_hash_table_size(session
->unconfirmed_messages
));
2742 contact
= get_contact(sip
);
2743 hdr
= g_strdup_printf(
2745 "Content-Type: application/sdp\r\n",
2746 contact
, ms_text_format
);
2747 g_free(ms_text_format
);
2749 body
= g_strdup_printf(
2751 "o=- 0 0 IN IP4 %s\r\n"
2755 "m=message %d sip null\r\n"
2756 "a=accept-types:text/plain text/html image/gif "
2757 "multipart/alternative application/im-iscomposing+xml\r\n",
2758 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
2760 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
2761 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
2770 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2773 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
2774 im_session_destroy(sip
, session
);
2779 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
2781 struct sipe_account_data
*sip
= gc
->proto_data
;
2783 purple_debug_info("sipe", "conversation with %s closed\n", who
);
2784 im_session_close(sip
, find_im_session(sip
, who
));
2788 im_session_close_all (struct sipe_account_data
*sip
)
2790 GSList
*entry
= sip
->im_sessions
;
2792 im_session_close (sip
, entry
->data
);
2793 entry
= sip
->im_sessions
;
2797 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
2799 struct sipe_account_data
*sip
;
2801 struct sip_im_session
*session
;
2803 sip
= gc
->proto_data
;
2806 purple_debug_info("sipe", "sipe_im_send what='%s'\n", what
);
2808 session
= find_or_create_im_session(sip
, who
);
2810 // Queue the message
2811 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, g_strdup(what
));
2813 if (session
->dialog
&& session
->dialog
->callid
) {
2814 sipe_im_process_queue(sip
, session
);
2815 } else if (!session
->outgoing_invite
) {
2816 // Need to send the INVITE to get the outgoing dialog setup
2817 sipe_invite(sip
, session
, what
);
2824 /* End IM Session (INVITE and MESSAGE methods) */
2827 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
2829 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2830 struct sip_im_session
*session
;
2832 if (state
== PURPLE_NOT_TYPING
)
2835 session
= find_im_session(sip
, who
);
2837 if (session
&& session
->dialog
) {
2838 send_sip_request(gc
, "INFO", who
, who
,
2839 "Content-Type: application/xml\r\n",
2840 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
2842 return SIPE_TYPING_SEND_TIMEOUT
;
2845 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
2847 GSList
*tmp
= sip
->transactions
;
2848 time_t currtime
= time(NULL
);
2850 struct transaction
*trans
= tmp
->data
;
2852 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime
-trans
->time
);
2853 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
2856 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
2858 sendout_sipmsg(sip
, trans
->msg
);
2865 static void do_reauthenticate_cb(struct sipe_account_data
*sip
)
2867 /* register again when security token expires */
2868 /* we have to start a new authentication as the security token
2869 * is almost expired by sending a not signed REGISTER message */
2870 purple_debug_info("sipe", "do a full reauthentication\n");
2871 sipe_auth_free(&sip
->registrar
);
2872 sip
->registerstatus
= 0;
2874 sip
->reauthenticate_set
= FALSE
;
2877 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2881 gboolean found
= FALSE
;
2883 from
= parse_from(sipmsg_find_header(msg
, "From"));
2887 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
2889 contenttype
= sipmsg_find_header(msg
, "Content-Type");
2890 if (!strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
2892 gchar
*html
= get_html_message(contenttype
, msg
->body
);
2893 serv_got_im(sip
->gc
, g_strdup(from
), g_strdup(html
), 0, time(NULL
));
2894 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2897 } else if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
2898 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2903 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
2907 state
= xmlnode_get_child(isc
, "state");
2910 purple_debug_info("sipe", "process_incoming_message: no state found\n");
2915 statedata
= xmlnode_get_data(state
);
2917 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
2918 else serv_got_typing_stopped(sip
->gc
, from
);
2923 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2927 purple_debug_info("sipe", "got unknown mime-type");
2928 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
2933 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2935 gchar
*ms_text_format
;
2938 struct sip_im_session
*session
;
2940 purple_debug_info("sipe", "process_incoming_invite: body:\n%s!\n", msg
->body
? msg
->body
: "");
2942 // Only accept text invitations
2943 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
2944 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2948 from
= parse_from(sipmsg_find_header(msg
, "From"));
2949 session
= find_or_create_im_session (sip
, from
);
2951 if (session
->dialog
) {
2952 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
2954 session
->dialog
= g_new0(struct sip_dialog
, 1);
2956 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
2958 session
->dialog
->callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
2959 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
2960 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
2961 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
2964 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
2967 //ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk=
2968 ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
2969 if (ms_text_format
) {
2970 if (!strncmp(ms_text_format
, "text/plain", 10) || !strncmp(ms_text_format
, "text/html", 9)) {
2972 gchar
*html
= get_html_message(ms_text_format
, NULL
);
2974 serv_got_im(sip
->gc
, g_strdup(from
), g_strdup(html
), 0, time(NULL
));
2975 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message reciept
2981 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
2982 sipmsg_remove_header(msg
, "Ms-Text-Format");
2983 sipmsg_remove_header(msg
, "EndPoints");
2984 sipmsg_remove_header(msg
, "User-Agent");
2985 sipmsg_remove_header(msg
, "Roster-Manager");
2987 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
2988 //sipmsg_add_header(msg, "Supported", "ms-renders-gif");
2990 body
= g_strdup_printf(
2992 "o=- 0 0 IN IP4 %s\r\n"
2996 "m=message %d sip sip:%s\r\n"
2997 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
2998 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
2999 sip
->realport
, sip
->username
);
3000 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3004 static void process_incoming_options(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3008 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
3009 sipmsg_remove_header(msg
, "EndPoints");
3010 sipmsg_remove_header(msg
, "User-Agent");
3012 sipmsg_add_header(msg
, "Allow", "INVITE, MESSAGE, INFO, SUBSCRIBE, OPTIONS, BYE, CANCEL, NOTIFY, ACK, BENOTIFY");
3013 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
3015 body
= g_strdup_printf(
3017 "o=- 0 0 IN IP4 0.0.0.0\r\n"
3019 "c=IN IP4 0.0.0.0\r\n"
3021 "m=message %d sip sip:%s\r\n"
3022 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
3023 sip
->realport
, sip
->username
);
3024 send_sip_response(sip
->gc
, msg
, 200, "OK", body
);
3028 static void sipe_connection_cleanup(struct sipe_account_data
*);
3029 static void create_connection(struct sipe_account_data
*, gchar
*, int);
3031 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3035 const gchar
*expires_header
;
3037 GSList
*hdr
= msg
->headers
;
3038 struct siphdrelement
*elem
;
3040 expires_header
= sipmsg_find_header(msg
, "Expires");
3041 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
3042 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
3044 switch (msg
->response
) {
3047 sip
->registerstatus
= 0;
3050 gchar
*contact_hdr
= NULL
;
3055 if (!sip
->reregister_set
) {
3056 gchar
*action_name
= g_strdup_printf("<%s>", "registration");
3057 sipe_schedule_action(action_name
, expires
, (Action
) do_register_cb
, sip
, NULL
);
3058 g_free(action_name
);
3059 sip
->reregister_set
= TRUE
;
3062 sip
->registerstatus
= 3;
3064 if (!sip
->reauthenticate_set
) {
3065 /* we have to reauthenticate as our security token expires
3066 after eight hours (be five minutes early) */
3067 gchar
*action_name
= g_strdup_printf("<%s>", "+reauthentication");
3068 guint reauth_timeout
= (8 * 3600) - 360;
3069 sipe_schedule_action(action_name
, reauth_timeout
, (Action
) do_reauthenticate_cb
, sip
, NULL
);
3070 g_free(action_name
);
3071 sip
->reauthenticate_set
= TRUE
;
3074 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
3076 epid
= get_epid(sip
);
3077 uuid
= generateUUIDfromEPID(epid
);
3080 // There can be multiple Contact headers (one per location where the user is logged in) so
3081 // make sure to only get the one for this uuid
3082 for (i
= 0; (contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
)); i
++) {
3083 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
3084 if (valid_contact
) {
3085 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
3086 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
3087 g_free(valid_contact
);
3090 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
3095 g_free(sip
->contact
);
3097 sip
->contact
= g_strdup_printf("<%s>", gruu
);
3100 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
3101 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
);
3103 sip
->msrtc_event_categories
= FALSE
;
3108 if(!g_ascii_strcasecmp(elem
->name
, "Supported"))
3110 if (strstr(elem
->value
, "msrtc-event-categories")){
3111 sip
->msrtc_event_categories
= TRUE
;
3113 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Supported: %s, %d\n", elem
->value
, sip
->msrtc_event_categories
);
3115 hdr
= g_slist_next(hdr
);
3118 if (!sip
->subscribed
) { //do it just once, not every re-register
3119 tmp
= sipmsg_find_header(msg
, "Allow-Events");
3120 sipe_options_request(sip
, sip
->sipdomain
);
3121 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
3122 sipe_subscribe_roaming_contacts(sip
, msg
);
3124 sipe_subscribe_roaming_acl(sip
, msg
);
3125 sipe_subscribe_roaming_self(sip
, msg
);
3126 sipe_subscribe_roaming_provisioning(sip
, msg
);
3127 sipe_subscribe_presence_wpending(sip
, msg
);
3128 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
3129 sip
->subscribed
= TRUE
;
3132 if (purple_account_get_bool(sip
->account
, "clientkeepalive", FALSE
)) {
3133 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Setting user defined keepalive\n");
3134 sip
->keepalive_timeout
= purple_account_get_int(sip
->account
, "keepalive", 0);
3136 tmp
= sipmsg_find_header(msg
, "ms-keep-alive");
3138 sipe_keep_alive_timeout(sip
, tmp
);
3142 // Should we remove the transaction here?
3143 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
3144 transactions_remove(sip
, tc
);
3149 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
3151 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
3152 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
3156 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
3159 tmp
= g_strsplit(parts
[0], ":", 0);
3160 hostname
= g_strdup(tmp
[0]);
3161 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
3165 tmp
= g_strsplit(parts
[i
], "=", 0);
3167 if (g_strcasecmp("transport", tmp
[0]) == 0) {
3168 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
3169 transport
= SIPE_TRANSPORT_TCP
;
3170 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
3171 transport
= SIPE_TRANSPORT_UDP
;
3180 /* Close old connection */
3181 sipe_connection_cleanup(sip
);
3183 /* Create new connection */
3184 sip
->transport
= transport
;
3185 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
3186 hostname
, port
, TRANSPORT_DESCRIPTOR
);
3187 create_connection(sip
, hostname
, port
);
3193 if (sip
->registerstatus
!= 2) {
3194 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
3195 if (sip
->registrar
.retries
> 3) {
3196 sip
->gc
->wants_to_die
= TRUE
;
3197 purple_connection_error(sip
->gc
, _("Wrong Password"));
3200 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3201 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
3203 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3205 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
3206 fill_auth(sip
, tmp
, &sip
->registrar
);
3207 sip
->registerstatus
= 2;
3208 if (sip
->account
->disconnecting
) {
3209 do_register_exp(sip
, 0);
3217 gchar
*warning
= sipmsg_find_header(msg
, "Warning");
3218 if (warning
!= NULL
) {
3220 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
3222 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
3223 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
3226 warning
= g_strdup(_("You have been rejected by the server"));
3229 sip
->gc
->wants_to_die
= TRUE
;
3230 purple_connection_error(sip
->gc
, warning
);
3237 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3238 if (warning
!= NULL
) {
3239 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3240 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
3243 warning
= g_strdup(_("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator"));
3246 sip
->gc
->wants_to_die
= TRUE
;
3247 purple_connection_error(sip
->gc
, warning
);
3254 gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
3255 if (warning
!= NULL
) {
3256 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
3257 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
3260 warning
= g_strdup(_("Service unavailable: no reason given"));
3263 sip
->gc
->wants_to_die
= TRUE
;
3264 purple_connection_error(sip
->gc
, warning
);
3273 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3276 xmlnode
*xn_categories
;
3277 xmlnode
*xn_category
;
3279 const char *activity
= NULL
;
3281 xn_categories
= xmlnode_from_str(data
, len
);
3282 uri
= xmlnode_get_attrib(xn_categories
, "uri");
3284 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
3286 xn_category
= xmlnode_get_next_twin(xn_category
) )
3288 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
3290 if (!strcmp(attrVar
, "note"))
3293 struct sipe_buddy
*sbuddy
;
3294 xn_node
= xmlnode_get_child(xn_category
, "note");
3295 if (!xn_node
) continue;
3296 xn_node
= xmlnode_get_child(xn_node
, "body");
3297 if (!xn_node
) continue;
3299 note
= xmlnode_get_data(xn_node
);
3302 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3306 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3307 sbuddy
->annotation
= g_strdup(note
);
3312 else if(!strcmp(attrVar
, "state"))
3316 xn_node
= xmlnode_get_child(xn_category
, "state");
3317 if (!xn_node
) continue;
3318 xn_node
= xmlnode_get_child(xn_node
, "availability");
3319 if (!xn_node
) continue;
3321 data
= xmlnode_get_data(xn_node
);
3326 activity
= SIPE_STATUS_ID_UNKNOWN
;
3327 else if (avail
< 4500)
3328 activity
= SIPE_STATUS_ID_AVAILABLE
;
3329 else if (avail
< 6000)
3330 activity
= SIPE_STATUS_ID_BRB
;
3331 else if (avail
< 7500)
3332 activity
= SIPE_STATUS_ID_ONPHONE
;
3333 else if (avail
< 9000)
3334 activity
= SIPE_STATUS_ID_BUSY
;
3335 else if (avail
< 12000)
3336 activity
= SIPE_STATUS_ID_DND
;
3337 else if (avail
< 18000)
3338 activity
= SIPE_STATUS_ID_AWAY
;
3340 activity
= SIPE_STATUS_ID_OFFLINE
;
3344 purple_debug_info("sipe", "process_incoming_notify_rlmi: %s\n", activity
);
3345 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
3348 xmlnode_free(xn_categories
);
3351 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3353 const char *uri
,*state
;
3355 xmlnode
*xn_resource
;
3356 xmlnode
*xn_instance
;
3358 xn_list
= xmlnode_from_str(data
, len
);
3360 for (xn_resource
= xmlnode_get_child(xn_list
, "resource");
3362 xn_resource
= xmlnode_get_next_twin(xn_resource
) )
3364 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
3365 if (!xn_instance
) return;
3367 state
= xmlnode_get_attrib(xn_instance
, "state");
3368 uri
= xmlnode_get_attrib(xn_instance
, "cid");
3369 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n",uri
,state
);
3370 if(strstr(state
,"resubscribe")){
3371 sipe_subscribe_presence_single(sip
, uri
);
3376 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3380 gchar
*activity
= NULL
;
3382 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
3383 gboolean isonline
= FALSE
;
3384 xmlnode
*display_name_node
;
3386 pidf
= xmlnode_from_str(data
, len
);
3388 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",data
);
3392 uri
= xmlnode_get_attrib(pidf
, "entity");
3394 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
3396 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3397 basicstatus
= xmlnode_get_child(status
, "basic");
3402 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
3407 getbasic
= xmlnode_get_data(basicstatus
);
3409 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
3414 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
3415 if (strstr(getbasic
, "open")) {
3420 display_name_node
= xmlnode_get_child(pidf
, "display-name");
3421 // updating display name if alias was just URI
3422 if (display_name_node
) {
3423 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3424 GSList
*entry
= buddies
;
3425 PurpleBuddy
*p_buddy
;
3426 char * display_name
= xmlnode_get_data(display_name_node
);
3429 const char *server_alias
;
3432 p_buddy
= entry
->data
;
3434 alias
= (char *)purple_buddy_get_alias(p_buddy
);
3435 alias
= alias
? g_strdup_printf("sip:%s", alias
) : NULL
;
3436 if (!alias
|| !g_ascii_strcasecmp(uri
, alias
)) {
3437 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3438 purple_blist_alias_buddy(p_buddy
, display_name
);
3442 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3444 ( (server_alias
&& strcmp(display_name
, server_alias
))
3445 || !server_alias
|| strlen(server_alias
) == 0 )
3447 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3450 entry
= entry
->next
;
3452 g_free(display_name
);
3455 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
3456 if ((status
= xmlnode_get_child(tuple
, "status"))) {
3457 if ((basicstatus
= xmlnode_get_child(status
, "activities"))) {
3458 if ((basicstatus
= xmlnode_get_child(basicstatus
, "activity"))) {
3459 activity
= xmlnode_get_data(basicstatus
);
3460 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
3467 const gchar
* status_id
= NULL
;
3469 if (strstr(activity
, "busy")) {
3470 status_id
= SIPE_STATUS_ID_BUSY
;
3471 } else if (strstr(activity
, "away")) {
3472 status_id
= SIPE_STATUS_ID_AWAY
;
3477 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3480 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
3481 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
3483 purple_prpl_got_user_status(sip
->account
, uri
, SIPE_STATUS_ID_OFFLINE
, NULL
);
3490 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
3492 const char *availability
;
3493 const char *activity
;
3494 const char *display_name
= NULL
;
3495 const char *activity_name
;
3500 struct sipe_buddy
*sbuddy
;
3502 xmlnode
*xn_presentity
= xmlnode_from_str(data
, len
);
3504 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
3505 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
3506 xmlnode
*xn_display_name
= xmlnode_get_child(xn_presentity
, "displayName");
3507 xmlnode
*xn_email
= xmlnode_get_child(xn_presentity
, "email");
3508 const char *email
= xn_email
? xmlnode_get_attrib(xn_email
, "email") : NULL
;
3509 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
3510 xmlnode
*xn_note
= xn_userinfo
? xmlnode_get_child(xn_userinfo
, "note") : NULL
;
3511 char *note
= xn_note
? xmlnode_get_data(xn_note
) : NULL
;
3512 xmlnode
*xn_devices
= xmlnode_get_child(xn_presentity
, "devices");
3513 xmlnode
*xn_device_presence
= xn_devices
? xmlnode_get_child(xn_devices
, "devicePresence") : NULL
;
3514 xmlnode
*xn_device_name
= xn_device_presence
? xmlnode_get_child(xn_device_presence
, "deviceName") : NULL
;
3515 const char *device_name
= xn_device_name
? xmlnode_get_attrib(xn_device_name
, "name") : NULL
;
3517 name
= xmlnode_get_attrib(xn_presentity
, "uri");
3518 uri
= g_strdup_printf("sip:%s", name
);
3519 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
3520 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
3522 // updating display name if alias was just URI
3523 if (xn_display_name
) {
3524 GSList
*buddies
= purple_find_buddies(sip
->account
, uri
); //all buddies in different groups
3525 GSList
*entry
= buddies
;
3526 PurpleBuddy
*p_buddy
;
3527 display_name
= xmlnode_get_attrib(xn_display_name
, "displayName");
3530 const char *email_str
, *server_alias
;
3532 p_buddy
= entry
->data
;
3534 if (!g_ascii_strcasecmp(name
, purple_buddy_get_alias(p_buddy
))) {
3535 purple_debug_info("sipe", "Replacing alias for %s with %s\n", uri
, display_name
);
3536 purple_blist_alias_buddy(p_buddy
, display_name
);
3539 server_alias
= purple_buddy_get_server_alias(p_buddy
);
3541 ( (server_alias
&& strcmp(display_name
, server_alias
))
3542 || !server_alias
|| strlen(server_alias
) == 0 )
3544 purple_blist_server_alias_buddy(p_buddy
, display_name
);
3548 email_str
= purple_blist_node_get_string((PurpleBlistNode
*)p_buddy
, "email");
3549 if (!email_str
|| g_ascii_strcasecmp(email_str
, email
)) {
3550 purple_blist_node_set_string((PurpleBlistNode
*)p_buddy
, "email", email
);
3554 entry
= entry
->next
;
3558 avl
= atoi(availability
);
3559 act
= atoi(activity
);
3562 activity_name
= SIPE_STATUS_ID_AWAY
;
3563 else if (act
<= 150)
3564 activity_name
= SIPE_STATUS_ID_LUNCH
;
3565 else if (act
<= 300)
3566 activity_name
= SIPE_STATUS_ID_BRB
;
3567 else if (act
<= 400)
3568 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3569 else if (act
<= 500)
3570 activity_name
= SIPE_STATUS_ID_ONPHONE
;
3571 else if (act
<= 600)
3572 activity_name
= SIPE_STATUS_ID_BUSY
;
3574 activity_name
= SIPE_STATUS_ID_AVAILABLE
;
3577 activity_name
= SIPE_STATUS_ID_OFFLINE
;
3579 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
3582 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
3583 sbuddy
->annotation
= NULL
;
3584 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
3586 if (sbuddy
->device_name
) { g_free(sbuddy
->device_name
); }
3587 sbuddy
->device_name
= NULL
;
3588 if (device_name
) { sbuddy
->device_name
= g_strdup(device_name
); }
3591 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
3592 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
3594 xmlnode_free(xn_presentity
);
3598 static void sipe_process_presence(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
3600 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3602 purple_debug_info("sipe", "sipe_process_presence: Content-Type: %s\n", ctype
? ctype
: "");
3604 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
3605 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
3607 const char *content
= msg
->body
;
3608 unsigned length
= msg
->bodylen
;
3609 PurpleMimeDocument
*mime
= NULL
;
3611 if (strstr(ctype
, "multipart"))
3613 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
3614 const char *content_type
;
3616 mime
= purple_mime_document_parse(doc
);
3617 parts
= purple_mime_document_get_parts(mime
);
3619 content
= purple_mime_part_get_data(parts
->data
);
3620 length
= purple_mime_part_get_length(parts
->data
);
3621 content_type
=purple_mime_part_get_field(parts
->data
,"Content-Type");
3622 if(content_type
&& strstr(content_type
,"application/rlmi+xml"))
3624 process_incoming_notify_rlmi_resub(sip
, content
, length
);
3626 else if(content_type
&& strstr(content_type
, "text/xml+msrtc.pidf"))
3628 process_incoming_notify_msrtc(sip
, content
, length
);
3632 process_incoming_notify_rlmi(sip
, content
, length
);
3634 parts
= parts
->next
;
3640 purple_mime_document_free(mime
);
3643 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
3645 process_incoming_notify_rlmi(sip
, msg
->body
, msg
->bodylen
);
3647 else if(strstr(ctype
, "application/rlmi+xml"))
3649 process_incoming_notify_rlmi_resub(sip
, msg
->body
, msg
->bodylen
);
3652 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
3654 process_incoming_notify_msrtc(sip
, msg
->body
, msg
->bodylen
);
3658 process_incoming_notify_pidf(sip
, msg
->body
, msg
->bodylen
);
3663 * Dispatcher for all incoming subscription information
3664 * whether it comes from NOTIFY, BENOTIFY requests or
3665 * piggy-backed to subscription's OK responce.
3667 * @param request whether initiated from BE/NOTIFY request or OK-response message.
3668 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
3670 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
, gboolean request
, gboolean benotify
)
3672 gchar
*event
= sipmsg_find_header(msg
, "Event");
3673 gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
3674 const char *uri
,*state
;
3676 xmlnode
*xn_resource
;
3677 xmlnode
*xn_instance
;
3681 purple_debug_info("sipe", "process_incoming_notify: Event: %s\n\n%s\n", event
? event
: "", msg
->body
);
3682 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n", subscription_state
);
3686 const gchar
*expires_header
;
3687 expires_header
= sipmsg_find_header(msg
, "Expires");
3688 expires
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
3689 purple_debug_info("sipe", "process_incoming_notify: expires:%d\n\n", expires
);
3692 if (!subscription_state
|| strstr(subscription_state
, "active"))
3694 if (event
&& !g_ascii_strcasecmp(event
, "presence"))
3696 sipe_process_presence(sip
, msg
);
3698 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-contacts"))
3700 sipe_process_roaming_contacts(sip
, msg
, NULL
);
3702 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-self") && benotify
)
3704 sipe_process_roaming_self(sip
, msg
);
3706 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-provisioning-v2"))
3709 purple_debug_info("sipe", "vnd-microsoft-provisioning-v2 data is not supported yet.");
3711 else if (event
&& !g_ascii_strcasecmp(event
, "vnd-microsoft-roaming-ACL"))
3713 sipe_process_roaming_acl(sip
, msg
);
3715 else if (event
&& !g_ascii_strcasecmp(event
, "presence.wpending"))
3717 sipe_process_presence_wpending(sip
, msg
);
3721 purple_debug_info("sipe", "Unable to process (BE)NOTIFY. Event is not supported:%s\n", event
? event
: "");
3725 //The server sends a (BE)NOTIFY with the status 'terminated'
3726 if(request
&& subscription_state
&& strstr(subscription_state
, "terminated") )
3728 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3729 purple_debug_info("sipe", "process_incoming_notify: (BE)NOTIFY says that subscription to buddy %s was terminated. \n", from
);
3733 if (event
&& !g_ascii_strcasecmp(event
, "registration-notify"))
3735 sipe_process_registration_notify(sip
, msg
);
3738 //The client responses 'Ok' when receive a NOTIFY message (lcs2005)
3739 if (request
&& !benotify
)
3741 sipmsg_remove_header(msg
, "Expires");
3742 sipmsg_remove_header(msg
, "subscription-state");
3743 sipmsg_remove_header(msg
, "Event");
3744 sipmsg_remove_header(msg
, "Require");
3745 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3752 static gchar* gen_xpidf(struct sipe_account_data *sip)
3754 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3756 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
3757 "<display name=\"sip:%s\"/>\r\n"
3758 "<atom id=\"1234\">\r\n"
3759 "<address uri=\"sip:%s\">\r\n"
3760 "<status status=\"%s\"/>\r\n"
3773 static gchar* gen_pidf(struct sipe_account_data *sip)
3775 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
3776 "<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"
3777 "<tuple id=\"0\">\r\n"
3779 "<basic>open</basic>\r\n"
3780 "<ep:activities>\r\n"
3781 " <ep:activity>%s</ep:activity>\r\n"
3785 "<ci:display-name>%s</ci:display-name>\r\n"
3794 static void send_presence_soap(struct sipe_account_data
*sip
, const char * note
)
3796 int availability
= 300; // online
3797 int activity
= 400; // Available
3800 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
)) {
3802 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
3804 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
3806 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
3808 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
3810 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
3812 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_INVISIBLE
) ||
3813 !strcmp(sip
->status
, SIPE_STATUS_ID_OFFLINE
)) {
3814 availability
= 0; // offline
3817 activity
= 400; // available
3820 name
= g_strdup_printf("sip: sip:%s", sip
->username
);
3821 //@TODO: send user data - state; add hostname in upper case
3822 body
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
3823 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
3829 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3831 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3832 if (msg
->response
== 200) {
3833 sip
->status_version
= 0;
3834 send_presence_status(sip
);
3840 process_send_presence_category_publish_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3842 if (msg
->response
== 409) {
3843 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3844 // TODO need to parse the version #'s?
3845 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
3846 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
3850 purple_debug_info("sipe", "process_send_presence_category_publish_response = %s\n", msg
->body
);
3852 tmp
= get_contact(sip
);
3853 hdr
= g_strdup_printf("Contact: %s\r\n"
3854 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3856 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
3866 static void send_presence_category_publish(struct sipe_account_data
*sip
, const char * note
)
3873 if (!strcmp(sip
->status
, SIPE_STATUS_ID_AWAY
) ||
3874 !strcmp(sip
->status
, SIPE_STATUS_ID_LUNCH
)) {
3876 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_DND
)) {
3878 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BUSY
)) {
3880 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_ONPHONE
)) {
3882 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_BRB
)) {
3884 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) {
3886 } else if (!strcmp(sip
->status
, SIPE_STATUS_ID_UNKNOWN
)) {
3889 // Offline or invisible
3893 uri
= g_strdup_printf("sip:%s", sip
->username
);
3894 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
3895 sip
->status_version
, code
,
3896 sip
->status_version
, code
,
3897 sip
->status_version
, note
? note
: "",
3898 sip
->status_version
, note
? note
: "",
3899 sip
->status_version
, note
? note
: ""
3901 sip
->status_version
++;
3903 tmp
= get_contact(sip
);
3904 hdr
= g_strdup_printf("Contact: %s\r\n"
3905 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3907 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_category_publish_response
);
3915 static void send_presence_status(struct sipe_account_data
*sip
)
3917 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
3919 if (!status
) return;
3921 note
= purple_status_get_attr_string(status
, "message");
3923 if(sip
->msrtc_event_categories
){
3924 send_presence_category_publish(sip
, note
);
3926 send_presence_soap(sip
, note
);
3930 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
3932 gboolean found
= FALSE
;
3933 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
3934 if (msg
->response
== 0) { /* request */
3935 if (!strcmp(msg
->method
, "MESSAGE")) {
3936 process_incoming_message(sip
, msg
);
3938 } else if (!strcmp(msg
->method
, "NOTIFY")) {
3939 purple_debug_info("sipe","send->process_incoming_notify\n");
3940 process_incoming_notify(sip
, msg
, TRUE
, FALSE
);
3942 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
3943 purple_debug_info("sipe","send->process_incoming_benotify\n");
3944 process_incoming_notify(sip
, msg
, TRUE
, TRUE
);
3946 } else if (!strcmp(msg
->method
, "INVITE")) {
3947 process_incoming_invite(sip
, msg
);
3949 } else if (!strcmp(msg
->method
, "OPTIONS")) {
3950 process_incoming_options(sip
, msg
);
3952 } else if (!strcmp(msg
->method
, "INFO")) {
3954 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
3956 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3959 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3961 } else if (!strcmp(msg
->method
, "ACK")) {
3962 // ACK's don't need any response
3964 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
3965 // LCS 2005 sends us these - just respond 200 OK
3967 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3968 } else if (!strcmp(msg
->method
, "BYE")) {
3969 struct sip_im_session
*session
;
3971 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3973 from
= parse_from(sipmsg_find_header(msg
, "From"));
3974 session
= find_im_session (sip
, from
);
3978 // TODO Let the user know the other user left the conversation?
3979 im_session_destroy(sip
, session
);
3984 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3986 } else { /* response */
3987 struct transaction
*trans
= transactions_find(sip
, msg
);
3989 if (msg
->response
== 407) {
3990 gchar
*resend
, *auth
, *ptmp
;
3992 if (sip
->proxy
.retries
> 30) return;
3993 sip
->proxy
.retries
++;
3994 /* do proxy authentication */
3996 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
3998 fill_auth(sip
, ptmp
, &sip
->proxy
);
3999 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
4000 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
4001 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
4003 resend
= sipmsg_to_string(trans
->msg
);
4004 /* resend request */
4005 sendout_pkt(sip
->gc
, resend
);
4008 if (msg
->response
== 100 || msg
->response
== 180) {
4009 /* ignore provisional response */
4010 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
4012 sip
->proxy
.retries
= 0;
4013 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
4014 if (msg
->response
== 401)
4016 sip
->registrar
.retries
++;
4017 sip
->registrar
.expires
= 0;
4021 sip
->registrar
.retries
= 0;
4023 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
4025 if (msg
->response
== 401) {
4026 gchar
*resend
, *auth
, *ptmp
;
4028 if (sip
->registrar
.retries
> 4) return;
4029 sip
->registrar
.retries
++;
4031 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
4032 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
4034 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
4037 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
4039 fill_auth(sip
, ptmp
, &sip
->registrar
);
4040 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
4041 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
4042 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
4044 //sipmsg_remove_header(trans->msg, "Authorization");
4045 //sipmsg_add_header(trans->msg, "Authorization", auth);
4047 resend
= sipmsg_to_string(trans
->msg
);
4048 /* resend request */
4049 sendout_pkt(sip
->gc
, resend
);
4054 if (trans
->callback
) {
4055 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
4056 /* call the callback to process response*/
4057 (trans
->callback
)(sip
, msg
, trans
);
4059 /* Not sure if this is needed or what needs to be done
4060 but transactions seem to be removed prematurely so
4061 this only removes them if the response is 200 OK */
4062 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
4063 /*Has a bug and it's unneccesary*/
4064 /*transactions_remove(sip, trans);*/
4070 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction\n");
4074 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
4078 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
4086 /* according to the RFC remove CRLF at the beginning */
4087 while (*cur
== '\r' || *cur
== '\n') {
4090 if (cur
!= conn
->inbuf
) {
4091 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
4092 conn
->inbufused
= strlen(conn
->inbuf
);
4095 /* Received a full Header? */
4096 sip
->processing_input
= TRUE
;
4097 while (sip
->processing_input
&&
4098 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
4099 time_t currtime
= time(NULL
);
4102 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
4103 msg
= sipmsg_parse_header(conn
->inbuf
);
4106 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
4107 if (restlen
>= msg
->bodylen
) {
4108 dummy
= g_malloc(msg
->bodylen
+ 1);
4109 memcpy(dummy
, cur
, msg
->bodylen
);
4110 dummy
[msg
->bodylen
] = '\0';
4112 cur
+= msg
->bodylen
;
4113 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
4114 conn
->inbufused
= strlen(conn
->inbuf
);
4116 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
4117 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
4123 purple_debug_info("sipe", "body:\n%s", msg->body);
4126 // Verify the signature before processing it
4127 if (sip
->registrar
.ntlm_key
) {
4128 struct sipmsg_breakdown msgbd
;
4129 gchar
*signature_input_str
;
4130 gchar
*signature
= NULL
;
4133 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
4134 signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
4135 if (signature_input_str
!= NULL
) {
4136 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
4138 g_free(signature_input_str
);
4140 rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
4142 if (signature
!= NULL
) {
4143 if (rspauth
!= NULL
) {
4144 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
4145 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
4146 process_input_message(sip
, msg
);
4148 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
4149 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
4150 sip
->gc
->wants_to_die
= TRUE
;
4152 } else if (msg
->response
== 401) {
4153 purple_connection_error(sip
->gc
, _("Wrong Password"));
4154 sip
->gc
->wants_to_die
= TRUE
;
4160 sipmsg_breakdown_free(&msgbd
);
4162 process_input_message(sip
, msg
);
4169 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
4171 PurpleConnection
*gc
= data
;
4172 struct sipe_account_data
*sip
= gc
->proto_data
;
4177 static char buffer
[65536];
4178 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
4180 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
4181 msg
= sipmsg_parse_msg(buffer
);
4182 if (msg
) process_input_message(sip
, msg
);
4186 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
4188 struct sipe_account_data
*sip
= gc
->proto_data
;
4189 PurpleSslConnection
*gsc
= sip
->gsc
;
4191 purple_debug_error("sipe", "%s",debug
);
4192 purple_connection_error(gc
, msg
);
4194 /* Invalidate this connection. Next send will open a new one */
4196 connection_remove(sip
, gsc
->fd
);
4197 purple_ssl_close(gsc
);
4203 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4205 PurpleConnection
*gc
= data
;
4206 struct sipe_account_data
*sip
;
4207 struct sip_connection
*conn
;
4209 gboolean firstread
= TRUE
;
4211 /* NOTE: This check *IS* necessary */
4212 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
4213 purple_ssl_close(gsc
);
4217 sip
= gc
->proto_data
;
4218 conn
= connection_find(sip
, gsc
->fd
);
4220 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
4221 gc
->wants_to_die
= TRUE
;
4222 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
4226 /* Read all available data from the SSL connection */
4228 /* Increase input buffer size as needed */
4229 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4230 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4231 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4232 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
4235 /* Try to read as much as there is space left in the buffer */
4236 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
4237 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
4239 if (len
< 0 && errno
== EAGAIN
) {
4240 /* Try again later */
4242 } else if (len
< 0) {
4243 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
4245 } else if (firstread
&& (len
== 0)) {
4246 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
4250 conn
->inbufused
+= len
;
4253 /* Equivalence indicates that there is possibly more data to read */
4254 } while (len
== readlen
);
4256 conn
->inbuf
[conn
->inbufused
] = '\0';
4257 process_input(sip
, conn
);
4261 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4263 PurpleConnection
*gc
= data
;
4264 struct sipe_account_data
*sip
= gc
->proto_data
;
4266 struct sip_connection
*conn
= connection_find(sip
, source
);
4268 purple_debug_error("sipe", "Connection not found!\n");
4272 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
4273 conn
->inbuflen
+= SIMPLE_BUF_INC
;
4274 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
4277 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
4279 if (len
< 0 && errno
== EAGAIN
)
4281 else if (len
<= 0) {
4282 purple_debug_info("sipe", "sipe_input_cb: read error\n");
4283 connection_remove(sip
, source
);
4284 if (sip
->fd
== source
) sip
->fd
= -1;
4288 conn
->inbufused
+= len
;
4289 conn
->inbuf
[conn
->inbufused
] = '\0';
4291 process_input(sip
, conn
);
4294 /* Callback for new connections on incoming TCP port */
4295 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
4297 PurpleConnection
*gc
= data
;
4298 struct sipe_account_data
*sip
= gc
->proto_data
;
4299 struct sip_connection
*conn
;
4301 int newfd
= accept(source
, NULL
, NULL
);
4303 conn
= connection_create(sip
, newfd
);
4305 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4308 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
4310 PurpleConnection
*gc
= data
;
4311 struct sipe_account_data
*sip
;
4312 struct sip_connection
*conn
;
4314 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4322 purple_connection_error(gc
, _("Could not connect"));
4326 sip
= gc
->proto_data
;
4328 sip
->last_keepalive
= time(NULL
);
4330 conn
= connection_create(sip
, source
);
4334 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
4337 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
4339 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
4340 if (sip
== NULL
) return;
4345 static guint
sipe_ht_hash_nick(const char *nick
)
4347 char *lc
= g_utf8_strdown(nick
, -1);
4348 guint bucket
= g_str_hash(lc
);
4354 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
4356 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
4359 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
4361 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4363 sip
->listen_data
= NULL
;
4365 if (listenfd
== -1) {
4366 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4372 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
4373 sip
->listenfd
= sip
->fd
;
4375 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
4377 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
4381 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
4383 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4386 sip
->query_data
= NULL
;
4388 if (!hosts
|| !hosts
->data
) {
4389 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
4393 addr_size
= GPOINTER_TO_INT(hosts
->data
);
4394 hosts
= g_slist_remove(hosts
, hosts
->data
);
4395 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
4396 g_free(hosts
->data
);
4397 hosts
= g_slist_remove(hosts
, hosts
->data
);
4399 hosts
= g_slist_remove(hosts
, hosts
->data
);
4400 g_free(hosts
->data
);
4401 hosts
= g_slist_remove(hosts
, hosts
->data
);
4404 /* create socket for incoming connections */
4405 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
4406 sipe_udp_host_resolved_listen_cb
, sip
);
4407 if (sip
->listen_data
== NULL
) {
4408 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4413 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
4416 PurpleConnection
*gc
= data
;
4417 struct sipe_account_data
*sip
;
4419 /* If the connection is already disconnected, we don't need to do anything else */
4420 if (!PURPLE_CONNECTION_IS_VALID(gc
))
4423 sip
= gc
->proto_data
;
4428 case PURPLE_SSL_CONNECT_FAILED
:
4429 purple_connection_error(gc
, _("Connection Failed"));
4431 case PURPLE_SSL_HANDSHAKE_FAILED
:
4432 purple_connection_error(gc
, _("SSL Handshake Failed"));
4434 case PURPLE_SSL_CERTIFICATE_INVALID
:
4435 purple_connection_error(gc
, _("SSL Certificate Invalid"));
4441 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
4443 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
4444 PurpleProxyConnectData
*connect_data
;
4446 sip
->listen_data
= NULL
;
4448 sip
->listenfd
= listenfd
;
4449 if (sip
->listenfd
== -1) {
4450 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
4454 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
4455 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
4456 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
4457 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
4458 sipe_newconn_cb
, sip
->gc
);
4459 purple_debug_info("sipe", "connecting to %s port %d\n",
4460 sip
->realhostname
, sip
->realport
);
4461 /* open tcp connection to the server */
4462 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
4463 sip
->realport
, login_cb
, sip
->gc
);
4465 if (connect_data
== NULL
) {
4466 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
4471 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
4473 PurpleAccount
*account
= sip
->account
;
4474 PurpleConnection
*gc
= sip
->gc
;
4476 if (purple_account_get_bool(account
, "useport", FALSE
)) {
4477 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
4478 port
= purple_account_get_int(account
, "port", 0);
4480 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
4483 sip
->realhostname
= hostname
;
4484 sip
->realport
= port
;
4486 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
4489 /* TODO: is there a good default grow size? */
4490 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
4491 sip
->txbuf
= purple_circ_buffer_new(0);
4493 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
4495 if (!purple_ssl_is_supported()) {
4496 gc
->wants_to_die
= TRUE
;
4497 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
4501 purple_debug_info("sipe", "using SSL\n");
4503 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
4504 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
4505 if (sip
->gsc
== NULL
) {
4506 purple_connection_error(gc
, _("Could not create SSL context"));
4509 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
4511 purple_debug_info("sipe", "using UDP\n");
4513 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
4514 if (sip
->query_data
== NULL
) {
4515 purple_connection_error(gc
, _("Could not resolve hostname"));
4519 purple_debug_info("sipe", "using TCP\n");
4520 /* create socket for incoming connections */
4521 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
4522 sipe_tcp_connect_listen_cb
, sip
);
4523 if (sip
->listen_data
== NULL
) {
4524 purple_connection_error(gc
, _("Could not create listen socket"));
4530 /* Service list for autodection */
4531 static const struct sipe_service_data service_autodetect
[] = {
4532 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4533 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4534 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4535 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4539 /* Service list for SSL/TLS */
4540 static const struct sipe_service_data service_tls
[] = {
4541 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
4542 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
4546 /* Service list for TCP */
4547 static const struct sipe_service_data service_tcp
[] = {
4548 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
4549 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
4553 /* Service list for UDP */
4554 static const struct sipe_service_data service_udp
[] = {
4555 { "sip", "udp", SIPE_TRANSPORT_UDP
},
4559 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
4560 static void resolve_next_service(struct sipe_account_data
*sip
,
4561 const struct sipe_service_data
*start
)
4564 sip
->service_data
= start
;
4566 sip
->service_data
++;
4567 if (sip
->service_data
->service
== NULL
) {
4569 /* Try connecting to the SIP hostname directly */
4570 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
4571 if (sip
->auto_transport
) {
4572 // If SSL is supported, default to using it; OCS servers aren't configured
4573 // by default to accept TCP
4574 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
4575 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
4576 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
4579 hostname
= g_strdup(sip
->sipdomain
);
4580 create_connection(sip
, hostname
, 0);
4585 /* Try to resolve next service */
4586 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
4587 sip
->service_data
->transport
,
4592 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
4594 struct sipe_account_data
*sip
= data
;
4596 sip
->srv_query_data
= NULL
;
4598 /* find the host to connect to */
4600 gchar
*hostname
= g_strdup(resp
->hostname
);
4601 int port
= resp
->port
;
4602 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
4606 sip
->transport
= sip
->service_data
->type
;
4608 create_connection(sip
, hostname
, port
);
4610 resolve_next_service(sip
, NULL
);
4614 static void sipe_login(PurpleAccount
*account
)
4616 PurpleConnection
*gc
;
4617 struct sipe_account_data
*sip
;
4618 gchar
**signinname_login
, **userserver
, **domain_user
;
4619 const char *transport
;
4621 const char *username
= purple_account_get_username(account
);
4622 gc
= purple_account_get_connection(account
);
4624 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
4625 gc
->wants_to_die
= TRUE
;
4626 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
4630 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
4631 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
4632 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
4634 sip
->account
= account
;
4635 sip
->reregister_set
= FALSE
;
4636 sip
->reauthenticate_set
= FALSE
;
4637 sip
->subscribed
= FALSE
;
4638 sip
->subscribed_buddies
= FALSE
;
4640 signinname_login
= g_strsplit(username
, ",", 2);
4642 userserver
= g_strsplit(signinname_login
[0], "@", 2);
4643 purple_connection_set_display_name(gc
, userserver
[0]);
4644 sip
->username
= g_strjoin("@", userserver
[0], userserver
[1], NULL
);
4645 sip
->sipdomain
= g_strdup(userserver
[1]);
4647 domain_user
= g_strsplit(signinname_login
[1], "\\", 2);
4648 sip
->authdomain
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[0]) : NULL
;
4649 sip
->authuser
= (domain_user
&& domain_user
[1]) ? g_strdup(domain_user
[1]) : (signinname_login
? g_strdup(signinname_login
[1]) : NULL
);
4651 sip
->password
= g_strdup(purple_connection_get_password(gc
));
4653 g_strfreev(userserver
);
4654 g_strfreev(domain_user
);
4655 g_strfreev(signinname_login
);
4657 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
4659 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
4661 /* TODO: Set the status correctly. */
4662 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
4664 transport
= purple_account_get_string(account
, "transport", "auto");
4665 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
4666 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
4669 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
4670 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
4671 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
4672 } else if (strcmp(transport
, "auto") == 0) {
4673 sip
->auto_transport
= TRUE
;
4674 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
4675 } else if (strcmp(transport
, "tls") == 0) {
4676 resolve_next_service(sip
, service_tls
);
4677 } else if (strcmp(transport
, "tcp") == 0) {
4678 resolve_next_service(sip
, service_tcp
);
4680 resolve_next_service(sip
, service_udp
);
4684 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
4686 connection_free_all(sip
);
4691 if (sip
->query_data
!= NULL
)
4692 purple_dnsquery_destroy(sip
->query_data
);
4693 sip
->query_data
= NULL
;
4695 if (sip
->srv_query_data
!= NULL
)
4696 purple_srv_cancel(sip
->srv_query_data
);
4697 sip
->srv_query_data
= NULL
;
4699 if (sip
->listen_data
!= NULL
)
4700 purple_network_listen_cancel(sip
->listen_data
);
4701 sip
->listen_data
= NULL
;
4703 if (sip
->gsc
!= NULL
)
4704 purple_ssl_close(sip
->gsc
);
4707 sipe_auth_free(&sip
->registrar
);
4708 sipe_auth_free(&sip
->proxy
);
4711 purple_circ_buffer_destroy(sip
->txbuf
);
4714 g_free(sip
->realhostname
);
4715 sip
->realhostname
= NULL
;
4718 purple_input_remove(sip
->listenpa
);
4720 if (sip
->tx_handler
)
4721 purple_input_remove(sip
->tx_handler
);
4722 sip
->tx_handler
= 0;
4723 if (sip
->resendtimeout
)
4724 purple_timeout_remove(sip
->resendtimeout
);
4725 sip
->resendtimeout
= 0;
4726 if (sip
->timeouts
) {
4727 GSList
*entry
= sip
->timeouts
;
4729 struct scheduled_action
*sched_action
= entry
->data
;
4730 purple_debug_info("sipe", "purple_timeout_remove: action name=%s\n", sched_action
->name
);
4731 purple_timeout_remove(sched_action
->timeout_handler
);
4732 g_free(sched_action
->payload
);
4733 g_free(sched_action
->name
);
4734 g_free(sched_action
);
4735 entry
= entry
->next
;
4738 g_slist_free(sip
->timeouts
);
4740 g_slist_free(sip
->allow_events
);
4743 g_free(sip
->contact
);
4744 sip
->contact
= NULL
;
4746 g_free(sip
->regcallid
);
4747 sip
->regcallid
= NULL
;
4750 sip
->processing_input
= FALSE
;
4754 * A callback for g_hash_table_foreach_remove
4756 static gboolean
sipe_buddy_remove(gpointer key
, struct sipe_buddy
*buddy
, gpointer user_data
)
4758 sipe_free_buddy(buddy
);
4761 static void sipe_close(PurpleConnection
*gc
)
4763 struct sipe_account_data
*sip
= gc
->proto_data
;
4766 /* leave all conversations */
4767 im_session_close_all(sip
);
4770 do_register_exp(sip
, 0);
4772 sipe_connection_cleanup(sip
);
4773 g_free(sip
->sipdomain
);
4774 g_free(sip
->username
);
4775 g_free(sip
->password
);
4776 g_free(sip
->authdomain
);
4777 g_free(sip
->authuser
);
4778 g_free(sip
->status
);
4780 g_hash_table_foreach_remove(sip
->buddies
, (GHRFunc
) sipe_buddy_remove
, NULL
);
4781 g_hash_table_destroy(sip
->buddies
);
4783 g_free(gc
->proto_data
);
4784 gc
->proto_data
= NULL
;
4787 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4789 PurpleAccount
*acct
= purple_connection_get_account(gc
);
4790 char *id
= g_strdup_printf("sip:%s", (char *)g_list_nth_data(row
, 0));
4791 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
4793 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
4794 purple_conversation_present(conv
);
4798 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
4801 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
4802 g_list_nth_data(row
, 0), NULL
, g_list_nth_data(row
, 1));
4805 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
4807 PurpleNotifySearchResults
*results
;
4808 PurpleNotifySearchColumn
*column
;
4809 xmlnode
*searchResults
;
4811 int match_count
= 0;
4812 gboolean more
= FALSE
;
4815 purple_debug_info("sipe", "process_search_contact_response: body:\n%s n", msg
->body
? msg
->body
: "");
4817 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
4818 if (!searchResults
) {
4819 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
4823 results
= purple_notify_searchresults_new();
4825 if (results
== NULL
) {
4826 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
4827 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
4829 xmlnode_free(searchResults
);
4833 column
= purple_notify_searchresults_column_new(_("User Name"));
4834 purple_notify_searchresults_column_add(results
, column
);
4836 column
= purple_notify_searchresults_column_new(_("Name"));
4837 purple_notify_searchresults_column_add(results
, column
);
4839 column
= purple_notify_searchresults_column_new(_("Company"));
4840 purple_notify_searchresults_column_add(results
, column
);
4842 column
= purple_notify_searchresults_column_new(_("Country"));
4843 purple_notify_searchresults_column_add(results
, column
);
4845 column
= purple_notify_searchresults_column_new(_("Email"));
4846 purple_notify_searchresults_column_add(results
, column
);
4848 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
4851 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
4852 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
4853 g_strfreev(uri_parts
);
4855 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
4856 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
4857 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
4858 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
4860 purple_notify_searchresults_row_add(results
, row
);
4864 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
4865 char *data
= xmlnode_get_data_unescaped(mrow
);
4866 more
= (g_strcasecmp(data
, "true") == 0);
4870 secondary
= g_strdup_printf(
4871 dngettext(GETTEXT_PACKAGE
,
4872 "Found %d contact%s:",
4873 "Found %d contacts%s:", match_count
),
4874 match_count
, more
? _(" (more matched your query)") : "");
4876 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
4877 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
4878 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
4881 xmlnode_free(searchResults
);
4885 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
4887 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
4888 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
4892 PurpleRequestField
*field
= entries
->data
;
4893 const char *id
= purple_request_field_get_id(field
);
4894 const char *value
= purple_request_field_string_get_value(field
);
4896 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
4898 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
4899 } while ((entries
= g_list_next(entries
)) != NULL
);
4903 gchar
*query
= g_strjoinv(NULL
, attrs
);
4904 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
4905 purple_debug_info("sipe", "sipe_search_contact_with_cb: body:\n%s n", body
? body
: "");
4906 send_soap_request_with_cb(gc
->proto_data
, body
,
4907 (TransCallback
) process_search_contact_response
, NULL
);
4915 static void sipe_show_find_contact(PurplePluginAction
*action
)
4917 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
4918 PurpleRequestFields
*fields
;
4919 PurpleRequestFieldGroup
*group
;
4920 PurpleRequestField
*field
;
4922 fields
= purple_request_fields_new();
4923 group
= purple_request_field_group_new(NULL
);
4924 purple_request_fields_add_group(fields
, group
);
4926 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
4927 purple_request_field_group_add_field(group
, field
);
4928 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
4929 purple_request_field_group_add_field(group
, field
);
4930 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
4931 purple_request_field_group_add_field(group
, field
);
4932 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
4933 purple_request_field_group_add_field(group
, field
);
4935 purple_request_fields(gc
,
4937 _("Search for a Contact"),
4938 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
4940 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
4942 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
4945 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
4948 PurplePluginAction
*act
;
4950 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
4951 menu
= g_list_prepend(menu
, act
);
4953 menu
= g_list_reverse(menu
);
4958 static void dummy_permit_deny(PurpleConnection
*gc
)
4962 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
4968 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
4974 static char *sipe_status_text(PurpleBuddy
*buddy
)
4976 struct sipe_account_data
*sip
;
4977 struct sipe_buddy
*sbuddy
;
4980 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
4981 if (sip
) //happens on pidgin exit
4983 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
4984 if (sbuddy
&& sbuddy
->annotation
)
4986 text
= g_strdup(sbuddy
->annotation
);
4993 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
4995 const PurplePresence
*presence
= purple_buddy_get_presence(buddy
);
4996 const PurpleStatus
*status
= purple_presence_get_active_status(presence
);
4997 struct sipe_account_data
*sip
;
4998 struct sipe_buddy
*sbuddy
;
4999 char *annotation
= NULL
;
5001 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
5002 if (sip
) //happens on pidgin exit
5004 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
5007 annotation
= sbuddy
->annotation
? g_strdup(sbuddy
->annotation
) : NULL
;
5012 if (purple_presence_is_online(presence
))
5014 purple_notify_user_info_add_pair(user_info
, _("Status"), purple_status_get_name(status
));
5019 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
5026 sipe_get_account_text_table(PurpleAccount
*account
)
5029 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
5030 g_hash_table_insert(table
, "login_label", (gpointer
)_("Sign-In Name..."));
5034 static PurpleBuddy
*
5035 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
5038 const gchar
*server_alias
, *email
;
5039 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
5041 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
5043 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
5045 server_alias
= g_strdup(purple_buddy_get_server_alias(buddy
));
5047 purple_blist_server_alias_buddy(clone
, server_alias
);
5050 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
5052 purple_blist_node_set_string((PurpleBlistNode
*)clone
, "email", email
);
5055 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
5057 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
5062 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
5064 PurpleBuddy
*buddy
, *b
;
5065 PurpleConnection
*gc
;
5066 PurpleGroup
* group
= purple_find_group(group_name
);
5068 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
5070 buddy
= (PurpleBuddy
*)node
;
5072 purple_debug_info("sipe", "sipe_buddy_menu_copy_to_cb: copying %s to %s\n", buddy
->name
, group_name
);
5073 gc
= purple_account_get_connection(buddy
->account
);
5075 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
5077 b
= purple_blist_add_buddy_clone(group
, buddy
);
5080 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
5084 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
5087 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: buddy->name=%s\n", buddy
->name
);
5089 email
= purple_blist_node_get_string((PurpleBlistNode
*)buddy
, "email");
5092 char *mailto
= g_strdup_printf("mailto:%s", email
);
5093 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: going to call default mail client with email: %s\n", email
);
5097 char *const parmList
[] = {mailto
, NULL
};
5098 if ((pid
= fork()) == -1)
5100 purple_debug_info("sipe", "fork() error\n");
5104 execvp("xdg-email", parmList
);
5105 purple_debug_info("sipe", "Return not expected. Must be an execvp() error.\n");
5113 //@TODO resolve env variable %WINDIR% first
5114 ret
= spawnl(_P_NOWAIT
, "c:/WINDOWS/system32/cmd", "/c", "start", mailto
, NULL
);
5117 purple_debug_info("sipe", "spawnl returned (%s)!\n", strerror(errno
));
5126 purple_debug_info("sipe", "sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s\n", buddy
->name
);
5131 * A menu which appear when right-clicking on buddy in contact list.
5134 sipe_buddy_menu(PurpleBuddy
*buddy
)
5136 PurpleBlistNode
*g_node
;
5137 PurpleGroup
*group
, *gr_parent
;
5138 PurpleMenuAction
*act
;
5140 GList
*menu_groups
= NULL
;
5142 act
= purple_menu_action_new(_("Send Email..."),
5143 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
5145 menu
= g_list_prepend(menu
, act
);
5147 gr_parent
= purple_buddy_get_group(buddy
);
5148 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
5149 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
5152 group
= (PurpleGroup
*)g_node
;
5153 if (group
== gr_parent
)
5156 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
5159 act
= purple_menu_action_new(purple_group_get_name(group
),
5160 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
5162 menu_groups
= g_list_prepend(menu_groups
, act
);
5164 menu_groups
= g_list_reverse(menu_groups
);
5166 act
= purple_menu_action_new(_("Copy to"),
5169 menu
= g_list_prepend(menu
, act
);
5170 menu
= g_list_reverse(menu
);
5175 GList
*sipe_blist_node_menu(PurpleBlistNode
*node
) {
5176 if(PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
5177 return sipe_buddy_menu((PurpleBuddy
*) node
);
5184 process_get_info_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
5186 gboolean ret
= TRUE
;
5187 char *username
= (char *)trans
->payload
;
5189 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
5190 PurpleBuddy
*pbuddy
;
5191 struct sipe_buddy
*sbuddy
;
5193 char *server_alias
= NULL
;
5195 const char *device_name
= NULL
;
5197 purple_debug_info("sipe", "Fetching %s's user info for %s\n", username
, sip
->username
);
5199 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, username
);
5200 alias
= purple_buddy_get_local_alias(pbuddy
);
5204 //will query buddy UA's capabilities and send answer to log
5205 sipe_options_request(sip
, username
);
5207 sbuddy
= g_hash_table_lookup(sip
->buddies
, username
);
5210 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
5214 if (msg
->response
!= 200) {
5215 purple_debug_info("sipe", "process_options_response: SERVICE response is %d\n", msg
->response
);
5217 xmlnode
*searchResults
;
5220 purple_debug_info("sipe", "process_options_response: body:\n%s\n", msg
->body
? msg
->body
: "");
5221 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
5222 if (!searchResults
) {
5223 purple_debug_info("sipe", "process_get_info_response: no parseable searchResults\n");
5224 } else if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
))) {
5225 server_alias
= g_strdup(xmlnode_get_attrib(mrow
, "displayName"));
5226 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
5227 purple_notify_user_info_add_pair(info
, _("Job Title"), g_strdup(xmlnode_get_attrib(mrow
, "title")));
5228 purple_notify_user_info_add_pair(info
, _("Office"), g_strdup(xmlnode_get_attrib(mrow
, "office")));
5229 purple_notify_user_info_add_pair(info
, _("Business Phone"), g_strdup(xmlnode_get_attrib(mrow
, "phone")));
5230 purple_notify_user_info_add_pair(info
, _("Company"), g_strdup(xmlnode_get_attrib(mrow
, "company")));
5231 purple_notify_user_info_add_pair(info
, _("City"), g_strdup(xmlnode_get_attrib(mrow
, "city")));
5232 purple_notify_user_info_add_pair(info
, _("State"), g_strdup(xmlnode_get_attrib(mrow
, "state")));
5233 purple_notify_user_info_add_pair(info
, _("Country"), g_strdup(xmlnode_get_attrib(mrow
, "country")));
5234 email
= g_strdup(xmlnode_get_attrib(mrow
, "email"));
5235 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
5236 if (!email
|| strcmp("", email
)) {
5237 if (!purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email")) {
5238 purple_blist_node_set_string((PurpleBlistNode
*)pbuddy
, "email", email
);
5242 xmlnode_free(searchResults
);
5245 purple_notify_user_info_add_section_break(info
);
5247 if (!server_alias
|| !strcmp("", server_alias
)) {
5248 g_free(server_alias
);
5249 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
5251 purple_notify_user_info_add_pair(info
, _("Display Name"), server_alias
);
5255 // same as server alias, do not present
5256 alias
= (alias
&& server_alias
&& !strcmp(alias
, server_alias
)) ? NULL
: alias
;
5259 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
5262 if (!email
|| !strcmp("", email
)) {
5264 email
= g_strdup(purple_blist_node_get_string((PurpleBlistNode
*)pbuddy
, "email"));
5266 purple_notify_user_info_add_pair(info
, _("E-Mail Address"), email
);
5272 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
5275 /* show a buddy's user info in a nice dialog box */
5276 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
5277 username
, /* buddy's username */
5279 NULL
, /* callback called when dialog closed */
5280 NULL
); /* userdata for callback */
5286 * AD search first, LDAP based
5288 static void sipe_get_info(PurpleConnection
*gc
, const char *username
)
5290 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
5291 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
5293 purple_debug_info("sipe", "sipe_get_contact_data: body:\n%s\n", body
? body
: "");
5294 send_soap_request_with_cb((struct sipe_account_data
*)gc
->proto_data
, body
,
5295 (TransCallback
) process_get_info_response
, (gpointer
)g_strdup(username
));
5300 static PurplePlugin
*my_protocol
= NULL
;
5302 static PurplePluginProtocolInfo prpl_info
=
5305 NULL
, /* user_splits */
5306 NULL
, /* protocol_options */
5307 NO_BUDDY_ICONS
, /* icon_spec */
5308 sipe_list_icon
, /* list_icon */
5309 NULL
, /* list_emblems */
5310 sipe_status_text
, /* status_text */
5311 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
5312 sipe_status_types
, /* away_states */
5313 sipe_blist_node_menu
, /* blist_node_menu */
5314 NULL
, /* chat_info */
5315 NULL
, /* chat_info_defaults */
5316 sipe_login
, /* login */
5317 sipe_close
, /* close */
5318 sipe_im_send
, /* send_im */
5319 NULL
, /* set_info */ // TODO maybe
5320 sipe_send_typing
, /* send_typing */
5321 sipe_get_info
, /* get_info */
5322 sipe_set_status
, /* set_status */
5323 NULL
, /* set_idle */
5324 NULL
, /* change_passwd */
5325 sipe_add_buddy
, /* add_buddy */
5326 NULL
, /* add_buddies */
5327 sipe_remove_buddy
, /* remove_buddy */
5328 NULL
, /* remove_buddies */
5329 sipe_add_permit
, /* add_permit */
5330 sipe_add_deny
, /* add_deny */
5331 sipe_add_deny
, /* rem_permit */
5332 sipe_add_permit
, /* rem_deny */
5333 dummy_permit_deny
, /* set_permit_deny */
5334 NULL
, /* join_chat */
5335 NULL
, /* reject_chat */
5336 NULL
, /* get_chat_name */
5337 NULL
, /* chat_invite */
5338 NULL
, /* chat_leave */
5339 NULL
, /* chat_whisper */
5340 NULL
, /* chat_send */
5341 sipe_keep_alive
, /* keepalive */
5342 NULL
, /* register_user */
5343 NULL
, /* get_cb_info */ // deprecated
5344 NULL
, /* get_cb_away */ // deprecated
5345 sipe_alias_buddy
, /* alias_buddy */
5346 sipe_group_buddy
, /* group_buddy */
5347 sipe_rename_group
, /* rename_group */
5348 NULL
, /* buddy_free */
5349 sipe_convo_closed
, /* convo_closed */
5350 purple_normalize_nocase
, /* normalize */
5351 NULL
, /* set_buddy_icon */
5352 sipe_remove_group
, /* remove_group */
5353 NULL
, /* get_cb_real_name */ // TODO?
5354 NULL
, /* set_chat_topic */
5355 NULL
, /* find_blist_chat */
5356 NULL
, /* roomlist_get_list */
5357 NULL
, /* roomlist_cancel */
5358 NULL
, /* roomlist_expand_category */
5359 NULL
, /* can_receive_file */
5360 NULL
, /* send_file */
5361 NULL
, /* new_xfer */
5362 NULL
, /* offline_message */
5363 NULL
, /* whiteboard_prpl_ops */
5364 sipe_send_raw
, /* send_raw */
5365 NULL
, /* roomlist_room_serialize */
5366 NULL
, /* unregister_user */
5367 NULL
, /* send_attention */
5368 NULL
, /* get_attention_types */
5370 sizeof(PurplePluginProtocolInfo
), /* struct_size */
5371 sipe_get_account_text_table
, /* get_account_text_table */
5375 static PurplePluginInfo info
= {
5376 PURPLE_PLUGIN_MAGIC
,
5377 PURPLE_MAJOR_VERSION
,
5378 PURPLE_MINOR_VERSION
,
5379 PURPLE_PLUGIN_PROTOCOL
, /**< type */
5380 NULL
, /**< ui_requirement */
5382 NULL
, /**< dependencies */
5383 PURPLE_PRIORITY_DEFAULT
, /**< priority */
5384 "prpl-sipe", /**< id */
5385 "Microsoft LCS/OCS", /**< name */
5386 VERSION
, /**< version */
5387 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
5388 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
5389 "Anibal Avelar <avelar@gmail.com>, " /**< author */
5390 "Gabriel Burt <gburt@novell.com>", /**< author */
5391 PURPLE_WEBSITE
, /**< homepage */
5392 sipe_plugin_load
, /**< load */
5393 sipe_plugin_unload
, /**< unload */
5394 sipe_plugin_destroy
, /**< destroy */
5395 NULL
, /**< ui_info */
5396 &prpl_info
, /**< extra_info */
5405 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
5409 entry
= prpl_info
.protocol_options
;
5411 purple_account_option_destroy(entry
->data
);
5412 entry
= g_list_delete_link(entry
, entry
);
5414 prpl_info
.protocol_options
= NULL
;
5416 entry
= prpl_info
.user_splits
;
5418 purple_account_user_split_destroy(entry
->data
);
5419 entry
= g_list_delete_link(entry
, entry
);
5421 prpl_info
.user_splits
= NULL
;
5424 static void init_plugin(PurplePlugin
*plugin
)
5426 PurpleAccountUserSplit
*split
;
5427 PurpleAccountOption
*option
;
5430 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
5431 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
5432 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
5435 purple_plugin_register(plugin
);
5437 split
= purple_account_user_split_new(_("Login \n domain\\user or\n someone@linux.com "), NULL
, ',');
5438 purple_account_user_split_set_reverse(split
, FALSE
);
5439 prpl_info
.user_splits
= g_list_append(prpl_info
.user_splits
, split
);
5441 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
5442 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5443 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
5444 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5446 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
5447 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5448 // Translators: noun (networking port)
5449 option
= purple_account_option_int_new(_("Port"), "port", 5061);
5450 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5452 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
5453 purple_account_option_add_list_item(option
, _("Auto"), "auto");
5454 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
5455 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
5456 purple_account_option_add_list_item(option
, _("UDP"), "udp");
5457 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5459 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
5460 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
5462 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
5463 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5465 // TODO commented out so won't show in the preferences until we fix krb message signing
5466 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
5467 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5469 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
5470 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
5471 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
5474 option
= purple_account_option_bool_new(_("Use Client-specified Keepalive"), "clientkeepalive", FALSE
);
5475 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5476 option
= purple_account_option_int_new(_("Keepalive Timeout"), "keepalive", 300);
5477 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
5478 my_protocol
= plugin
;
5481 /* I had to redefined the function for it load, but works */
5482 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
5483 plugin
->info
= &(info
);
5484 init_plugin((plugin
));
5485 sipe_plugin_load((plugin
));
5486 return purple_plugin_register(plugin
);