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])
93 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
96 static gchar
*get_epid()
98 return sipe_uuid_get_macaddr();
101 static char *genbranch()
103 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
104 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
105 rand() & 0xFFFF, rand() & 0xFFFF);
108 static char *gencallid()
110 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
111 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
112 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
113 rand() & 0xFFFF, rand() & 0xFFFF);
116 static gchar
*find_tag(const gchar
*hdr
)
118 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
120 // In case it's at the end and there's no trailing ;
121 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
127 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
132 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
134 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
135 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
138 static void sipe_close(PurpleConnection
*gc
);
140 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
);
141 static void send_presence_info(struct sipe_account_data
*sip
);
143 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
145 static void sipe_keep_alive_timeout(struct sipe_account_data
*sip
, const gchar
*hdr
)
147 gchar
*timeout
= sipmsg_find_part_of_header(hdr
, "timeout=", ";", NULL
);
148 if (timeout
!= NULL
) {
149 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
150 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
151 sip
->keepalive_timeout
);
155 static void sipe_keep_alive(PurpleConnection
*gc
)
157 struct sipe_account_data
*sip
= gc
->proto_data
;
158 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
159 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
160 gchar buf
[2] = {0, 0};
161 purple_debug_info("sipe", "sending keep alive\n");
162 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
164 time_t now
= time(NULL
);
165 if ((sip
->keepalive_timeout
> 0) &&
166 ((now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
167 #if PURPLE_VERSION_CHECK(2,4,0)
168 && ((now
- gc
->last_received
) >= sip
->keepalive_timeout
)
171 purple_debug_info("sipe", "sending keep alive\n");
172 sendout_pkt(gc
, "\r\n\r\n");
173 sip
->last_keepalive
= now
;
178 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
180 struct sip_connection
*ret
= NULL
;
181 GSList
*entry
= sip
->openconns
;
184 if (ret
->fd
== fd
) return ret
;
190 static void sipe_auth_free(struct sip_auth
*auth
)
194 g_free(auth
->opaque
);
198 g_free(auth
->target
);
200 g_free(auth
->digest_session_key
);
201 auth
->digest_session_key
= NULL
;
202 g_free(auth
->ntlm_key
);
203 auth
->ntlm_key
= NULL
;
204 auth
->type
= AUTH_TYPE_UNSET
;
209 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
211 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
213 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
217 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
219 struct sip_connection
*conn
= connection_find(sip
, fd
);
221 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
222 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
228 static void connection_free_all(struct sipe_account_data
*sip
)
230 struct sip_connection
*ret
= NULL
;
231 GSList
*entry
= sip
->openconns
;
234 connection_remove(sip
, ret
->fd
);
235 entry
= sip
->openconns
;
239 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
241 const gchar
*method
= msg
->method
;
242 const gchar
*target
= msg
->target
;
247 const char *authdomain
;
248 const char *authuser
;
249 const char *krb5_realm
;
251 gchar
*krb5_token
= NULL
;
253 authdomain
= purple_account_get_string(sip
->account
, "authdomain", "");
254 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
256 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
257 // and do error checking
259 // KRB realm should always be uppercase
260 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
262 if (sip
->realhostname
) {
263 host
= sip
->realhostname
;
264 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
265 host
= purple_account_get_string(sip
->account
, "proxy", "");
267 host
= sip
->sipdomain
;
270 /*gboolean new_auth = krb5_auth.gss_context == NULL;
272 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
275 if (new_auth || force_reauth) {
276 krb5_token = krb5_auth.base64_token;
279 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
280 krb5_token = krb5_auth.base64_token;*/
282 if (!authuser
|| strlen(authuser
) < 1) {
283 authuser
= sip
->username
;
286 if (auth
->type
== AUTH_TYPE_DIGEST
) { /* Digest */
287 sprintf(noncecount
, "%08d", auth
->nc
++);
288 response
= purple_cipher_http_digest_calculate_response(
289 "md5", method
, target
, NULL
, NULL
,
290 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
291 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
293 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
);
296 } else if (auth
->type
== AUTH_TYPE_NTLM
) { /* NTLM */
297 // If we have a signature for the message, include that
298 if (msg
->signature
) {
299 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
);
303 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
304 const gchar
* ntlm_key
;
305 #if GLIB_CHECK_VERSION(2,8,0)
306 const gchar
* hostname
= g_get_host_name();
308 static char hostname
[256];
309 int ret
= gethostname(hostname
, sizeof(hostname
));
310 hostname
[sizeof(hostname
) - 1] = '\0';
311 if (ret
== -1 || hostname
[0] == '\0') {
312 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Error when getting host name: %s. Using \"localhost.\"\n");
314 strcpy(hostname
, "localhost");
317 /*const gchar * hostname = purple_get_host_name();*/
319 gchar
* gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, hostname
, authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
320 auth
->ntlm_key
= (gchar
*)ntlm_key
;
321 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
326 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
328 } else if (auth
->type
== AUTH_TYPE_KERBEROS
) {
331 /*if (new_auth || force_reauth) {
332 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
334 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);
336 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
339 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
340 gchar * mic = "MICTODO";
341 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
342 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
343 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
344 //auth->opaque ? auth->opaque : "", auth->target);
345 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
350 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
353 sprintf(noncecount
, "%08d", auth
->nc
++);
354 response
= purple_cipher_http_digest_calculate_response(
355 "md5", method
, target
, NULL
, NULL
,
356 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
357 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
359 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
);
364 static char *parse_attribute(const char *attrname
, const char *source
)
366 const char *tmp
, *tmp2
;
368 int len
= strlen(attrname
);
370 if (!strncmp(source
, attrname
, len
)) {
372 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
374 retval
= g_strndup(tmp
, tmp2
- tmp
);
376 retval
= g_strdup(tmp
);
382 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
385 const char *authuser
;
388 const char *krb5_realm
;
391 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
392 // and do error checking
394 // KRB realm should always be uppercase
395 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
397 if (sip->realhostname) {
398 host = sip->realhostname;
399 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
400 host = purple_account_get_string(sip->account, "proxy", "");
402 host = sip->sipdomain;
405 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
407 if (!authuser
|| strlen(authuser
) < 1) {
408 authuser
= sip
->username
;
412 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
416 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
417 auth
->type
= AUTH_TYPE_NTLM
;
418 parts
= g_strsplit(hdr
+5, "\", ", 0);
421 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
422 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
423 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
426 if ((tmp
= parse_attribute("targetname=\"",
430 else if ((tmp
= parse_attribute("realm=\"",
434 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
441 if (!strstr(hdr
, "gssapi-data")) {
449 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
450 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
451 auth
->type
= AUTH_TYPE_KERBEROS
;
452 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
453 parts
= g_strsplit(hdr
+9, "\", ", 0);
456 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
457 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
458 /*if (krb5_auth.gss_context == NULL) {
459 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
461 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
464 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
466 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
468 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
478 auth
->type
= AUTH_TYPE_DIGEST
;
479 parts
= g_strsplit(hdr
, " ", 0);
481 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
484 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
491 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
493 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
494 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
500 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
502 PurpleConnection
*gc
= data
;
503 struct sipe_account_data
*sip
= gc
->proto_data
;
507 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
509 if (max_write
== 0) {
510 if (sip
->tx_handler
!= 0){
511 purple_input_remove(sip
->tx_handler
);
517 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
519 if (written
< 0 && errno
== EAGAIN
)
521 else if (written
<= 0) {
522 /*TODO: do we really want to disconnect on a failure to write?*/
523 purple_connection_error(gc
, _("Could not write"));
527 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
530 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
532 PurpleConnection
*gc
= data
;
533 struct sipe_account_data
*sip
= gc
->proto_data
;
537 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
539 if (max_write
== 0) {
540 if (sip
->tx_handler
!= 0) {
541 purple_input_remove(sip
->tx_handler
);
547 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
549 if (written
< 0 && errno
== EAGAIN
)
551 else if (written
<= 0) {
552 /*TODO: do we really want to disconnect on a failure to write?*/
553 purple_connection_error(gc
, _("Could not write"));
557 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
560 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
562 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
564 PurpleConnection
*gc
= data
;
565 struct sipe_account_data
*sip
;
566 struct sip_connection
*conn
;
568 if (!PURPLE_CONNECTION_IS_VALID(gc
))
576 purple_connection_error(gc
, _("Could not connect"));
580 sip
= gc
->proto_data
;
582 sip
->connecting
= FALSE
;
583 sip
->last_keepalive
= time(NULL
);
585 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
587 /* If there is more to write now, we need to register a handler */
588 if (sip
->txbuf
->bufused
> 0)
589 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
591 conn
= connection_create(sip
, source
);
592 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
595 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
597 struct sipe_account_data
*sip
;
598 struct sip_connection
*conn
;
600 if (!PURPLE_CONNECTION_IS_VALID(gc
))
602 if (gsc
) purple_ssl_close(gsc
);
606 sip
= gc
->proto_data
;
609 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
610 sip
->connecting
= FALSE
;
611 sip
->last_keepalive
= time(NULL
);
613 conn
= connection_create(sip
, gsc
->fd
);
615 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
620 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
622 PurpleConnection
*gc
= data
;
623 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
624 if (sip
== NULL
) return;
626 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
628 /* If there is more to write now */
629 if (sip
->txbuf
->bufused
> 0) {
630 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
635 static void sendlater(PurpleConnection
*gc
, const char *buf
)
637 struct sipe_account_data
*sip
= gc
->proto_data
;
639 if (!sip
->connecting
) {
640 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
641 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
642 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
644 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
645 purple_connection_error(gc
, _("Couldn't create socket"));
648 sip
->connecting
= TRUE
;
651 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
652 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
654 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
657 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
659 struct sipe_account_data
*sip
= gc
->proto_data
;
660 time_t currtime
= time(NULL
);
661 int writelen
= strlen(buf
);
663 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
664 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
665 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
666 purple_debug_info("sipe", "could not send packet\n");
675 if (sip
->tx_handler
) {
680 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
682 ret
= write(sip
->fd
, buf
, writelen
);
686 if (ret
< 0 && errno
== EAGAIN
)
688 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
693 if (ret
< writelen
) {
694 if (!sip
->tx_handler
){
696 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
699 sip
->tx_handler
= purple_input_add(sip
->fd
,
700 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
705 /* XXX: is it OK to do this? You might get part of a request sent
706 with part of another. */
707 if (sip
->txbuf
->bufused
> 0)
708 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
710 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
716 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
718 sendout_pkt(gc
, buf
);
722 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
724 GSList
*tmp
= msg
->headers
;
727 GString
*outstr
= g_string_new("");
728 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
730 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
731 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
732 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
733 tmp
= g_slist_next(tmp
);
735 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
736 sendout_pkt(sip
->gc
, outstr
->str
);
737 g_string_free(outstr
, TRUE
);
740 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
743 if (sip
->registrar
.ntlm_key
) {
744 struct sipmsg_breakdown msgbd
;
746 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
747 msgbd
.rand
= g_strdup_printf("%08x", g_random_int());
748 sip
->registrar
.ntlm_num
++;
749 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
750 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
751 if (signature_input_str
!= NULL
) {
752 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
753 msg
->rand
= g_strdup(msgbd
.rand
);
754 msg
->num
= g_strdup(msgbd
.num
);
756 sipmsg_breakdown_free(&msgbd
);
759 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
760 buf
= auth_header(sip
, &sip
->registrar
, msg
);
761 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
762 sipmsg_add_header(msg
, "Authorization", buf
);
764 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
767 } 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")) {
768 sip
->registrar
.nc
= 3;
769 sip
->registrar
.type
= AUTH_TYPE_NTLM
;
771 buf
= auth_header(sip
, &sip
->registrar
, msg
);
772 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
775 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
779 static char *get_contact(struct sipe_account_data
*sip
)
781 return g_strdup(sip
->contact
);
785 static char *get_contact_service(struct sipe_account_data
*sip
)
787 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()));
788 //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);
791 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
792 const char *text
, const char *body
)
796 GString
*outstr
= g_string_new("");
797 struct sipe_account_data
*sip
= gc
->proto_data
;
799 sipmsg_remove_header(msg
, "ms-user-data");
802 contact
= get_contact(sip
);
803 sipmsg_remove_header(msg
, "Contact");
804 sipmsg_add_header(msg
, "Contact", contact
);
807 /* When sending the acknowlegements and errors, the content length from the original
808 message is still here, but there is no body; we need to make sure we're sending the
809 correct content length */
810 sipmsg_remove_header(msg
, "Content-Length");
813 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
814 sipmsg_add_header(msg
, "Content-Length", len
);
816 sipmsg_add_header(msg
, "Content-Length", "0");
819 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
820 //gchar * mic = "MICTODO";
821 msg
->response
= code
;
823 sipmsg_remove_header(msg
, "Authentication-Info");
824 sign_outgoing_message(msg
, sip
, msg
->method
);
826 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
827 GSList
*tmp
= msg
->headers
;
829 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
830 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
832 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
833 tmp
= g_slist_next(tmp
);
835 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
836 sendout_pkt(gc
, outstr
->str
);
837 g_string_free(outstr
, TRUE
);
840 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
842 if (trans
->msg
) sipmsg_free(trans
->msg
);
843 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
847 static struct transaction
*
848 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
850 struct transaction
*trans
= g_new0(struct transaction
, 1);
851 trans
->time
= time(NULL
);
852 trans
->msg
= (struct sipmsg
*)msg
;
853 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
854 trans
->callback
= callback
;
855 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
859 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
861 struct transaction
*trans
;
862 GSList
*transactions
= sip
->transactions
;
863 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
865 while (transactions
) {
866 trans
= transactions
->data
;
867 if (!strcmp(trans
->cseq
, cseq
)) {
870 transactions
= transactions
->next
;
876 static struct transaction
*
877 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
878 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
879 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
881 struct sipe_account_data
*sip
= gc
->proto_data
;
882 const char *addh
= "";
886 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
887 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
888 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
889 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
890 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
891 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
892 gchar
*route
= strdup("");
894 if (dialog
&& dialog
->routes
)
896 GSList
*iter
= dialog
->routes
;
901 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, (char *)iter
->data
);
903 iter
= g_slist_next(iter
);
907 if (!ourtag
&& !dialog
) {
911 if (!strcmp(method
, "REGISTER")) {
912 if (sip
->regcallid
) {
914 callid
= g_strdup(sip
->regcallid
);
916 sip
->regcallid
= g_strdup(callid
);
920 if (addheaders
) addh
= addheaders
;
922 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
923 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
924 "From: <sip:%s>%s%s;epid=%s\r\n"
925 "To: <%s>%s%s%s%s\r\n"
926 "Max-Forwards: 70\r\n"
931 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
933 dialog
&& dialog
->request
? dialog
->request
: url
,
934 TRANSPORT_DESCRIPTOR
,
935 purple_network_get_my_ip(-1),
937 branch
? ";branch=" : "",
938 branch
? branch
: "",
940 ourtag
? ";tag=" : "",
941 ourtag
? ourtag
: "",
942 get_epid(), // TODO generate one per account/login
944 theirtag
? ";tag=" : "",
945 theirtag
? theirtag
: "",
946 theirepid
? ";epid=" : "",
947 theirepid
? theirepid
: "",
948 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
954 body
? strlen(body
) : 0,
958 //printf ("parsing msg buf:\n%s\n\n", buf);
959 msg
= sipmsg_parse_msg(buf
);
969 sign_outgoing_message (msg
, sip
, method
);
971 buf
= sipmsg_to_string (msg
);
973 /* add to ongoing transactions */
974 struct transaction
* trans
= transactions_add_buf(sip
, msg
, tc
);
975 sendout_pkt(gc
, buf
);
980 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
982 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
983 gchar
*contact
= get_contact(sip
);
984 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
985 "Content-Type: application/SOAP+xml\r\n",contact
);
987 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
988 tr
->payload
= payload
;
994 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
996 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
999 static char *get_contact_register(struct sipe_account_data
*sip
)
1001 return g_strdup_printf("<sip:%s:%d;transport=%s;ms-opaque=d3470f2e1d>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace;+sip.instance=\"<urn:uuid:%s>\"", purple_network_get_my_ip(-1), sip
->listenport
, TRANSPORT_DESCRIPTOR
, generateUUIDfromEPID(get_epid()));
1004 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1006 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1007 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1008 char *contact
= get_contact_register(sip
);
1009 char *hdr
= g_strdup_printf("Contact: %s\r\n"
1010 "Supported: gruu-10, adhoclist\r\n"
1011 "Event: registration\r\n"
1012 "Allow-Events: presence\r\n"
1013 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1014 "Expires: %d\r\n", contact
,expire
);
1017 sip
->registerstatus
= 1;
1020 sip
->reregister
= time(NULL
) + expire
- 50;
1022 sip
->reregister
= time(NULL
) + 600;
1025 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1026 process_register_response
);
1033 static void do_register(struct sipe_account_data
*sip
)
1035 do_register_exp(sip
, sip
->registerexpire
);
1038 static gchar
*parse_from(const gchar
*hdr
)
1041 const gchar
*tmp
, *tmp2
= hdr
;
1043 if (!hdr
) return NULL
;
1044 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1045 tmp
= strchr(hdr
, '<');
1047 /* i hate the different SIP UA behaviours... */
1048 if (tmp
) { /* sip address in <...> */
1050 tmp
= strchr(tmp2
, '>');
1052 from
= g_strndup(tmp2
, tmp
- tmp2
);
1054 purple_debug_info("sipe", "found < without > in From\n");
1058 tmp
= strchr(tmp2
, ';');
1060 from
= g_strndup(tmp2
, tmp
- tmp2
);
1062 from
= g_strdup(tmp2
);
1065 purple_debug_info("sipe", "got %s\n", from
);
1069 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1075 va_start(args
, parent
);
1076 while ((name
= va_arg(args
, const char *)) != NULL
) {
1077 node
= xmlnode_get_child(parent
, name
);
1078 if (node
== NULL
) return NULL
;
1088 sipe_contact_set_acl (struct sipe_account_data
*sip
, const gchar
* who
, gchar
* rights
)
1090 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
1091 send_soap_request(sip
, body
);
1096 sipe_contact_allow_deny (struct sipe_account_data
*sip
, const gchar
* who
, gboolean allow
)
1099 purple_debug_info("sipe", "Authorizing contact %s\n", who
);
1101 purple_debug_info("sipe", "Blocking contact %s\n", who
);
1104 sipe_contact_set_acl (sip
, who
, allow
? "AA" : "BD");
1108 void sipe_auth_user_cb(void * data
)
1110 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1113 sipe_contact_allow_deny (job
->sip
, job
->who
, TRUE
);
1118 void sipe_deny_user_cb(void * data
)
1120 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
1123 sipe_contact_allow_deny (job
->sip
, job
->who
, FALSE
);
1128 sipe_add_permit(PurpleConnection
*gc
, const char *name
)
1130 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1131 sipe_contact_allow_deny(sip
, name
, TRUE
);
1135 sipe_add_deny(PurpleConnection
*gc
, const char *name
)
1137 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1138 sipe_contact_allow_deny(sip
, name
, FALSE
);
1142 sipe_remove_permit_deny(PurpleConnection *gc, const char *name)
1144 struct sipe_account_data *sip = (struct sipe_account_data *)gc->proto_data;
1145 sipe_contact_set_acl(sip, name, "");
1149 sipe_process_incoming_pending (struct sipe_account_data
*sip
, struct sipmsg
* msg
)
1151 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
1152 if (msg
->response
!= 0 && msg
->response
!= 200) return;
1154 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| !strcmp(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
1156 xmlnode
* watchers
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1157 if (!watchers
) return;
1160 for (watcher
= xmlnode_get_child(watchers
, "watcher"); watcher
; watcher
= xmlnode_get_next_twin(watcher
)) {
1161 gchar
* remote_user
= g_strdup(xmlnode_get_attrib(watcher
, "uri"));
1162 gchar
* alias
= g_strdup(xmlnode_get_attrib(watcher
, "displayName"));
1163 gboolean on_list
= g_hash_table_lookup(sip
->buddies
, remote_user
) != NULL
;
1165 // TODO pull out optional displayName to pass as alias
1167 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
1168 job
->who
= remote_user
;
1170 purple_account_request_authorization(
1184 xmlnode_free(watchers
);
1189 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1191 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1192 if (!purple_group
) {
1193 purple_group
= purple_group_new(group
->name
);
1194 purple_blist_add_group(purple_group
, NULL
);
1198 group
->purple_group
= purple_group
;
1199 sip
->groups
= g_slist_append(sip
->groups
, group
);
1200 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1202 purple_debug_info("sipe", "did not add group %s\n", group
->name
);
1206 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1212 struct sipe_group
*group
;
1213 GSList
*entry
= sip
->groups
;
1215 group
= entry
->data
;
1216 if (group
->id
== id
) {
1219 entry
= entry
->next
;
1224 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1230 struct sipe_group
*group
;
1231 GSList
*entry
= sip
->groups
;
1233 group
= entry
->data
;
1234 if (!strcmp(group
->name
, name
)) {
1237 entry
= entry
->next
;
1243 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1245 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1246 gchar
* body
= g_strdup_printf(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
1247 send_soap_request(sip
, body
);
1249 g_free(group
->name
);
1250 group
->name
= g_strdup(name
);
1254 sipe_group_set_user (struct sipe_account_data
*sip
, struct sipe_group
* group
, const gchar
* who
)
1256 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1257 PurpleBuddy
* purple_buddy
= purple_find_buddy (sip
->account
, who
);
1260 group
= sipe_group_find_by_id (sip
, buddy
->group_id
);
1262 buddy
->group_id
= group
? group
->id
: 1;
1264 if (buddy
&& purple_buddy
) {
1265 gchar
* alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1266 purple_debug_info("sipe", "Saving buddy %s with alias %s and group_id %d\n", who
, alias
, buddy
->group_id
);
1267 gchar
* body
= g_strdup_printf(SIPE_SOAP_SET_CONTACT
,
1268 alias
, buddy
->group_id
, "true", buddy
->name
, sip
->contacts_delta
++
1270 send_soap_request(sip
, body
);
1275 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1277 if (msg
->response
== 200) {
1278 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1280 struct group_user_context
* ctx
= (struct group_user_context
*)tc
->payload
;
1281 group
->name
= ctx
->group_name
;
1283 xmlnode
* xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1284 if (!xml
) return FALSE
;
1286 xmlnode
* node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1287 if (!node
) return FALSE
;
1289 char * group_id
= xmlnode_get_data(node
);
1290 if (!group_id
) return FALSE
;
1292 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1294 sipe_group_add(sip
, group
);
1295 sipe_group_set_user(sip
, group
, ctx
->user_name
);
1304 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1306 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1307 ctx
->group_name
= g_strdup(name
);
1308 ctx
->user_name
= g_strdup(who
);
1310 gchar
* body
= g_strdup_printf(SIPE_SOAP_ADD_GROUP
, name
, sip
->contacts_delta
++);
1311 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1315 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1319 if (msg
->response
== 200 || msg
->response
== 202) {
1323 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* cant be NULL since it is our own msg */
1325 /* we can not subscribe -> user is offline (TODO unknown status?) */
1327 purple_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
1332 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
)
1334 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1335 gchar
*tmp
= get_contact(sip
);
1339 //Add the the extend SUBSCRIBE request
1340 if (sip
->presence_method_version
== 1)
1342 request
= g_strdup_printf(
1343 "Require: adhoclist, categoryList\r\n"
1344 "Supported: eventlist\r\n"
1345 "Accept: application/msrtc-event-categories+xml, application/xpidf+xml, text/xml+msrtc.pidf, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1346 "Event: presence\r\n"
1347 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1348 "Contact: %s\r\n", tmp
);
1350 else{ //To send a single SUSCRIBE request
1351 request
= g_strdup_printf(
1352 "Accept: application/msrtc-event-categories+xml, application/xpidf+xml, text/xml+msrtc.pidf, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
1353 "Event: presence\r\n"
1354 "Content-Type: application/msrtc-adrl-categorylist+xml\r\n"
1355 "Contact: %s\r\n", tmp
);
1358 content
= g_strdup_printf(
1359 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
1360 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
1361 "<resource uri=\"%s\"/>\n"
1363 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
1364 "<category name=\"note\"/>\n"
1365 "<category name=\"state\"/>\n"
1368 "</batchSub>", sip
->username
, to
1373 /* subscribe to buddy presence */
1374 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, request
, content
, NULL
, process_subscribe_response
);
1381 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1383 const char *status_id
= purple_status_get_id(status
);
1384 struct sipe_account_data
*sip
= NULL
;
1386 if (!purple_status_is_active(status
))
1390 sip
= account
->gc
->proto_data
;
1393 g_free(sip
->status
);
1394 sip
->status
= g_strdup(status_id
);
1395 send_presence_info(sip
);
1400 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1402 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1403 sipe_group_set_user(sip
, NULL
, name
);
1407 sipe_group_buddy(PurpleConnection
*gc
,
1409 const char *old_group_name
,
1410 const char *new_group_name
)
1412 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1413 struct sipe_group
* group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1415 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1417 sipe_group_set_user(sip
, group
, who
);
1421 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1423 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1424 struct sipe_buddy
*b
;
1426 // Prepend sip: if needed
1427 if (strncmp("sip:", buddy
->name
, 4)) {
1428 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1429 purple_blist_rename_buddy(buddy
, buf
);
1433 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1434 b
= g_new0(struct sipe_buddy
, 1);
1435 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1436 b
->name
= g_strdup(buddy
->name
);
1437 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1438 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1440 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1444 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1446 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1447 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1450 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1453 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1454 send_soap_request(sip
, body
);
1463 sipe_rename_group(PurpleConnection
*gc
,
1464 const char *old_name
,
1466 GList
*moved_buddies
)
1468 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1469 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1471 sipe_group_rename(sip
, s_group
, group
->name
);
1473 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1478 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1480 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1481 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1483 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1484 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1485 send_soap_request(sip
, body
);
1488 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1489 g_free(s_group
->name
);
1491 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1495 static GList
*sipe_status_types(PurpleAccount
*acc
)
1497 PurpleStatusType
*type
;
1498 GList
*types
= NULL
;
1501 type
= purple_status_type_new_with_attrs(
1502 PURPLE_STATUS_AVAILABLE
, NULL
, "Online", TRUE
, TRUE
, FALSE
,
1503 // Translators: noun
1504 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1506 types
= g_list_append(types
, type
);
1509 type
= purple_status_type_new_with_attrs(
1510 PURPLE_STATUS_UNAVAILABLE
, "busy", _("Busy"), TRUE
, TRUE
, FALSE
,
1511 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1513 types
= g_list_append(types
, type
);
1515 // Do Not Disturb (Not let user set it)
1516 type
= purple_status_type_new_with_attrs(
1517 PURPLE_STATUS_UNAVAILABLE
, "do-not-disturb", "Do Not Disturb", TRUE
, FALSE
, FALSE
,
1518 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1520 types
= g_list_append(types
, type
);
1523 type
= purple_status_type_new_with_attrs(
1524 PURPLE_STATUS_AWAY
, "be-right-back", _("Be Right Back"), TRUE
, TRUE
, FALSE
,
1525 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1527 types
= g_list_append(types
, type
);
1530 type
= purple_status_type_new_with_attrs(
1531 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1532 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1534 types
= g_list_append(types
, type
);
1537 type
= purple_status_type_new_with_attrs(
1538 PURPLE_STATUS_UNAVAILABLE
, "on-the-phone", _("On The Phone"), TRUE
, TRUE
, FALSE
,
1539 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1541 types
= g_list_append(types
, type
);
1544 type
= purple_status_type_new_with_attrs(
1545 PURPLE_STATUS_AWAY
, "out-to-lunch", "Out To Lunch", TRUE
, TRUE
, FALSE
,
1546 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1548 types
= g_list_append(types
, type
);
1551 type
= purple_status_type_new_full(
1552 PURPLE_STATUS_INVISIBLE
, NULL
, "Appear Offline", TRUE
, TRUE
, FALSE
);
1553 types
= g_list_append(types
, type
);
1556 type
= purple_status_type_new_full(
1557 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1558 types
= g_list_append(types
, type
);
1563 static gboolean
sipe_add_lcs_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1565 int len
= msg
->bodylen
;
1567 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1568 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1572 /* Convert the contact from XML to Purple Buddies */
1573 xmlnode
* isc
= xmlnode_from_str(msg
->body
, len
);
1578 gchar
* contacts_delta
= g_strdup(xmlnode_get_attrib(isc
, "deltaNum"));
1579 if (contacts_delta
) {
1580 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1584 xmlnode
*group_node
;
1585 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1586 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1588 group
->name
= g_strdup(xmlnode_get_attrib(group_node
, "name"));
1589 if (!strncmp(group
->name
, "~", 1)){
1591 group
->name
= "General";
1593 group
->name
= g_strdup(group
->name
);
1594 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1596 sipe_group_add(sip
, group
);
1599 // Make sure we have at least one group
1600 if (g_slist_length(sip
->groups
) == 0) {
1601 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1603 group
->name
= g_strdup("General");
1605 PurpleGroup
* purple_group
= purple_group_new(group
->name
);
1606 purple_blist_add_group(purple_group
, NULL
);
1607 sip
->groups
= g_slist_append(sip
->groups
, group
);
1610 /* Parse contacts */
1612 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1613 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1614 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1615 gchar
**item_groups
= g_strsplit(xmlnode_get_attrib(item
, "groups"), " ", 0);
1617 struct sipe_group
* group
= NULL
;
1619 // Find the first group this contact belongs to; that's where we'll place it in the buddy list
1620 if (item_groups
[0]) {
1621 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[0], NULL
));
1624 // If couldn't find the right group for this contact, just put them in the first group we have
1625 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1626 group
= sip
->groups
->data
;
1629 if (group
!= NULL
) {
1630 char * buddy_name
= g_strdup_printf("sip:%s", uri
);
1632 //b = purple_find_buddy(sip->account, buddy_name);
1633 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1635 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1639 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1641 if (name
!= NULL
&& strlen(name
) != 0) {
1642 purple_blist_alias_buddy(b
, name
);
1644 purple_blist_alias_buddy(b
, uri
);
1647 struct sipe_buddy
* buddy
= g_new0(struct sipe_buddy
, 1);
1648 buddy
->name
= g_strdup(b
->name
);
1649 buddy
->group_id
= group
->id
;
1650 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1652 purple_debug_info("sipe", "Added buddy %s to group %s\n", buddy
->name
, group
->name
);
1654 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
1664 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1666 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1667 gchar
*tmp
= get_contact(sip
);
1668 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-roaming-contacts\r\n"
1669 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
1670 "Supported: com.microsoft.autoextend\r\n"
1671 "Supported: ms-benotify\r\n"
1672 "Proxy-Require: ms-benotify\r\n"
1673 "Supported: ms-piggyback-first-notify\r\n"
1674 "Contact: %s\r\n", tmp
);
1677 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_add_lcs_contacts
);
1683 sipe_process_pending_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1685 sipe_process_incoming_pending (sip
, msg
);
1689 static void sipe_subscribe_pending_buddies(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1691 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1692 gchar
*tmp
= get_contact(sip
);
1693 gchar
*hdr
= g_strdup_printf("Event: presence.wpending\r\n"
1694 "Accept: text/xml+msrtc.wpending\r\n"
1695 "Supported: com.microsoft.autoextend\r\n"
1696 "Supported: ms-benotify\r\n"
1697 "Proxy-Require: ms-benotify\r\n"
1698 "Supported: ms-piggyback-first-notify\r\n"
1699 "Contact: %s\r\n", tmp
);
1702 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_process_pending_response
);
1707 static void process_incoming_benotify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1709 gchar
* event
= sipmsg_find_header(msg
, "Event");
1712 if (!strcmp(event
, "presence.wpending")) {
1713 sipe_process_incoming_pending (sip
, msg
);
1717 xmlnode
*xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1720 gchar
* contacts_delta
= g_strdup(xmlnode_get_attrib(xml
, "deltaNum"));
1721 if (contacts_delta
) {
1722 int new_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1723 if (!strcmp(event
, "vnd-microsoft-roaming-ACL")) {
1724 sip
->acl_delta
= new_delta
;
1726 sip
->contacts_delta
= new_delta
;
1734 sipe_process_acl_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1736 process_incoming_benotify (sip
, msg
);
1740 static void sipe_subscribe_acl(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1742 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1743 gchar
*tmp
= get_contact(sip
);
1744 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-roaming-ACL\r\n"
1745 "Accept: application/vnd-microsoft-roaming-acls+xml\r\n"
1746 "Supported: com.microsoft.autoextend\r\n"
1747 "Supported: ms-benotify\r\n"
1748 "Proxy-Require: ms-benotify\r\n"
1749 "Supported: ms-piggyback-first-notify\r\n"
1750 "Contact: %s\r\n", tmp
);
1753 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_process_acl_response
);
1758 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1760 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1761 gchar
*tmp
= get_contact(sip
);
1762 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-roaming-self\r\n"
1763 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
1764 "Supported: com.microsoft.autoextend\r\n"
1765 "Supported: ms-benotify\r\n"
1766 "Proxy-Require: ms-benotify\r\n"
1767 "Supported: ms-piggyback-first-notify\r\n"
1769 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n", tmp
);
1773 gchar
*body
=g_strdup("<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\"><roaming type=\"categories\"/><roaming type=\"containers\"/><roaming type=\"subscribers\"/></roamingList>");
1775 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
1781 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1783 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1784 gchar
*tmp
= get_contact(sip
);
1785 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-provisioning-v2\r\n"
1786 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
1787 "Supported: com.microsoft.autoextend\r\n"
1788 "Supported: ms-benotify\r\n"
1789 "Proxy-Require: ms-benotify\r\n"
1790 "Supported: ms-piggyback-first-notify\r\n"
1793 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n", tmp
);
1797 gchar
*body
=g_strdup("<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\"><provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/><provisioningGroup name=\"ucPolicy\"/></provisioningGroupList>");
1798 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
1804 /* IM Session (INVITE and MESSAGE methods) */
1806 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
1808 if (sip
== NULL
|| who
== NULL
) {
1812 struct sip_im_session
*session
;
1813 GSList
*entry
= sip
->im_sessions
;
1815 session
= entry
->data
;
1816 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
1819 entry
= entry
->next
;
1824 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
1826 struct sip_im_session
*session
= find_im_session(sip
, who
);
1828 session
= g_new0(struct sip_im_session
, 1);
1829 session
->with
= g_strdup(who
);
1830 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
1835 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1837 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
1838 // TODO free session resources
1841 static void sipe_present_message_undelivered_err(gchar
*with
, struct sipe_account_data
*sip
, gchar
*message
)
1843 char *msg
, *msg_tmp
;
1844 msg_tmp
= message
? purple_markup_strip_html(message
) : NULL
;
1845 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
1847 msg_tmp
= g_strdup_printf( _("The following message could not be delivered to all recipients, "\
1848 "possibly because one or more persons are offline:\n%s") ,
1850 purple_conv_present_error(with
, sip
->account
, msg_tmp
);
1855 static void sipe_im_remove_first_from_queue (struct sip_im_session
* session
);
1856 static void sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
);
1859 process_message_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1861 gboolean ret
= TRUE
;
1862 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
1863 struct sip_im_session
* session
= find_im_session(sip
, with
);
1866 purple_debug_info("sipe", "process_message_response: unable to find IM session\n");
1870 if (msg
->response
!= 200) {
1871 purple_debug_info("sipe", "process_message_response: MESSAGE response not 200\n");
1874 if (session
->outgoing_message_queue
) {
1875 queued_msg
= session
->outgoing_message_queue
->data
;
1877 sipe_present_message_undelivered_err(with
, sip
, queued_msg
);
1878 im_session_destroy(sip
, session
);
1882 struct sip_dialog
* dialog
= session
->dialog
;
1884 purple_debug_info("sipe", "process_message_response: session outgoing dialog is NULL\n");
1888 sipe_im_remove_first_from_queue(session
);
1889 sipe_im_process_queue(sip
, session
);
1894 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
1900 if (strncmp("sip:", session
->with
, 4)) {
1901 fullto
= g_strdup_printf("sip:%s", session
->with
);
1903 fullto
= g_strdup(session
->with
);
1908 sipe_parse_html(msg
, &msgformat
, &msgtext
);
1909 purple_debug_info("sipe", "sipe_send_message: msgformat=%s", msgformat
);
1911 gchar
*msgr_value
= sipmsg_get_msgr_string(msgformat
);
1915 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
1919 hdr
= g_strdup_printf("Content-Type: text/plain; charset=UTF-8%s\r\n", msgr
);
1921 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1922 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1923 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1925 tmp
= get_contact(sip
);
1926 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1929 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msgtext
, session
->dialog
, process_message_response
);
1938 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
1940 GSList
*entry
= session
->outgoing_message_queue
;
1942 char *queued_msg
= entry
->data
;
1943 sipe_send_message(sip
, session
, queued_msg
);
1948 sipe_im_remove_first_from_queue (struct sip_im_session
* session
)
1950 if (session
&& session
->outgoing_message_queue
) {
1951 char *queued_msg
= session
->outgoing_message_queue
->data
;
1952 // Remove from the queue and free the string
1953 session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
1959 sipe_get_route_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
1961 GSList
*hdr
= msg
->headers
;
1962 struct siphdrelement
*elem
;
1968 if(!strcmp(elem
->name
, "Record-Route"))
1970 gchar
*route
= sipmsg_find_part_of_header(elem
->value
, "<", ">", NULL
);
1971 dialog
->routes
= g_slist_append(dialog
->routes
, route
);
1973 hdr
= g_slist_next(hdr
);
1978 dialog
->routes
= g_slist_reverse(dialog
->routes
);
1983 dialog
->request
= dialog
->routes
->data
;
1984 dialog
->routes
= g_slist_remove(dialog
->routes
, dialog
->routes
->data
);
1987 contact
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "<", ">", NULL
);
1988 dialog
->routes
= g_slist_append(dialog
->routes
, contact
);
1992 sipe_get_supported_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
1994 GSList
*hdr
= msg
->headers
;
1995 struct siphdrelement
*elem
;
1999 if(!strcmp(elem
->name
, "Supported")
2000 && !g_slist_find_custom(dialog
->supported
, elem
->value
, (GCompareFunc
)strcmp
))
2002 dialog
->supported
= g_slist_append(dialog
->supported
, g_strdup(elem
->value
));
2005 hdr
= g_slist_next(hdr
);
2010 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
2012 gchar
*us
= outgoing
? "From" : "To";
2013 gchar
*them
= outgoing
? "To" : "From";
2015 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
2016 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
2017 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
2018 if (!dialog
->theirepid
) {
2019 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
2021 if (!dialog
->theirepid
) {
2022 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
2025 sipe_get_route_header(msg
, dialog
, outgoing
);
2026 sipe_get_supported_header(msg
, dialog
, outgoing
);
2031 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
2033 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
2034 struct sip_im_session
* session
= find_im_session(sip
, with
);
2037 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
2042 if (msg
->response
!= 200) {
2043 purple_debug_info("sipe", "process_invite_response: INVITE response not 200\n");
2046 if (session
->outgoing_message_queue
) {
2047 queued_msg
= session
->outgoing_message_queue
->data
;
2049 sipe_present_message_undelivered_err(with
, sip
, queued_msg
);
2051 im_session_destroy(sip
, session
);
2056 struct sip_dialog
* dialog
= session
->dialog
;
2058 purple_debug_info("sipe", "process_invite_response: session outgoing dialog is NULL\n");
2063 sipe_parse_dialog(msg
, dialog
, TRUE
);
2066 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
2067 session
->outgoing_invite
= NULL
;
2068 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)strcmp
)) {
2069 sipe_im_remove_first_from_queue(session
);
2071 sipe_im_process_queue(sip
, session
);
2079 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
* session
, gchar
* msg_body
)
2086 if (session
->dialog
) {
2087 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
2091 session
->dialog
= g_new0(struct sip_dialog
, 1);
2093 if (strstr(session
->with
, "sip:")) {
2094 to
= g_strdup(session
->with
);
2096 to
= g_strdup_printf("sip:%s", session
->with
);
2101 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
2102 purple_debug_info("sipe", "sipe_invite: msgformat=%s", msgformat
);
2104 gchar
*msgr_value
= sipmsg_get_msgr_string(msgformat
);
2108 msgr
= g_strdup_printf(";msgr=%s", msgr_value
);
2112 char * base64_msg
= purple_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
2114 char * ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, msgr
, base64_msg
);
2118 contact
= get_contact(sip
);
2119 hdr
= g_strdup_printf(
2121 "Content-Type: application/sdp\r\n",
2122 contact
, ms_text_format
, sip
->username
, sip
->username
, to
);
2123 g_free(ms_text_format
);
2125 body
= g_strdup_printf(
2127 "o=- 0 0 IN IP4 %s\r\n"
2131 "m=message %d sip null\r\n"
2132 "a=accept-types:text/plain text/html image/gif "
2133 "multipart/alternative application/im-iscomposing+xml\r\n",
2134 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
2136 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
2137 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
2146 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
2149 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
2150 im_session_destroy(sip
, session
);
2155 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
2157 struct sipe_account_data
*sip
= gc
->proto_data
;
2159 purple_debug_info("sipe", "conversation with %s closed\n", who
);
2160 im_session_close(sip
, find_im_session(sip
, who
));
2164 im_session_close_all (struct sipe_account_data
*sip
)
2166 GSList
*entry
= sip
->im_sessions
;
2168 im_session_close (sip
, entry
->data
);
2169 entry
= sip
->im_sessions
;
2173 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
2175 purple_debug_info("sipe", "sipe_im_send what=%s\n", what
);
2177 struct sipe_account_data
*sip
= gc
->proto_data
;
2178 char *to
= g_strdup(who
);
2179 char *text
= g_strdup(what
);
2181 struct sip_im_session
* session
= find_or_create_im_session(sip
, who
);
2183 // Queue the message
2184 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, text
);
2186 if (session
->dialog
&& session
->dialog
->callid
) {
2187 sipe_im_process_queue(sip
, session
);
2188 } else if (!session
->outgoing_invite
) {
2189 // Need to send the INVITE to get the outgoing dialog setup
2190 sipe_invite(sip
, session
, text
);
2198 /* End IM Session (INVITE and MESSAGE methods) */
2201 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
2203 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
2205 if (state
== PURPLE_NOT_TYPING
)
2208 struct sip_im_session
* session
= find_im_session(sip
, who
);
2210 if (session
&& session
->dialog
) {
2211 send_sip_request(gc
, "INFO", who
, who
,
2212 "Content-Type: application/xml\r\n",
2213 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
2216 return SIPE_TYPING_SEND_TIMEOUT
;
2220 static void sipe_buddy_resub(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
2222 time_t curtime
= time(NULL
);
2223 if (buddy
->resubscribe
< curtime
) {
2224 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_buddy_resub %s\n", name
);
2225 sipe_subscribe_to_name(sip
, buddy
->name
);
2227 /* resubscribe before subscription expires */
2228 /* add some jitter */
2229 buddy
->resubscribe
= time(NULL
)+1140+(rand()%50);
2233 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
2235 GSList
*tmp
= sip
->transactions
;
2236 time_t currtime
= time(NULL
);
2238 struct transaction
*trans
= tmp
->data
;
2240 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime
-trans
->time
);
2241 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
2244 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
2246 sendout_sipmsg(sip
, trans
->msg
);
2253 static gboolean
subscribe_timeout(struct sipe_account_data
*sip
)
2256 time_t curtime
= time(NULL
);
2257 /* register again if first registration or security token expires */
2258 if ( (sip
->reregister
< curtime
)
2259 || (sip
->registrar
.expires
!= 0 && sip
->registrar
.expires
< curtime
) )
2261 /* time to do a full reauthentication? */
2262 if (sip
->registrar
.expires
< curtime
)
2264 /* we have to start a new authentication as the security token
2265 * is almost expired by sending a not signed REGISTER message */
2266 purple_debug_info("sipe", "do a full reauthentication");
2267 sipe_auth_free(&sip
->registrar
);
2268 sip
->registerstatus
= 0;
2273 /* check for every subscription if we need to resubscribe */
2274 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_resub
, (gpointer
)sip
);
2279 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2283 gboolean found
= FALSE
;
2285 from
= parse_from(sipmsg_find_header(msg
, "From"));
2289 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
2291 contenttype
= sipmsg_find_header(msg
, "Content-Type");
2292 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
2293 gchar
*msgr
= sipmsg_find_part_of_header(contenttype
, "msgr=", NULL
, NULL
);
2294 gchar
*x_mms_im_format
= sipmsg_get_x_mms_im_format(msgr
);
2297 gchar
*body_esc
= g_markup_escape_text(msg
->body
, -1);
2298 gchar
*body_html
= sipmsg_apply_x_mms_im_format(x_mms_im_format
, body_esc
);
2300 g_free(x_mms_im_format
);
2302 serv_got_im(sip
->gc
, from
, body_html
, 0, time(NULL
));
2304 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2307 if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
2308 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2313 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
2317 state
= xmlnode_get_child(isc
, "state");
2320 purple_debug_info("sipe", "process_incoming_message: no state found\n");
2325 statedata
= xmlnode_get_data(state
);
2327 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
2328 else serv_got_typing_stopped(sip
->gc
, from
);
2333 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2337 purple_debug_info("sipe", "got unknown mime-type");
2338 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
2343 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2345 // Only accept text invitations
2346 if (msg
->body
&& !(strstr(msg
->body
, "m=message") || strstr(msg
->body
, "m=x-ms-message"))) {
2347 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2351 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
2352 struct sip_im_session
* session
= find_or_create_im_session (sip
, from
);
2354 if (session
->dialog
) {
2355 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
2357 session
->dialog
= g_new0(struct sip_dialog
, 1);
2359 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
2361 session
->dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
2362 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
2363 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
2364 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
2367 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
2370 //ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0...DIADQAKAA0ACgA;ms-body=SGk=
2371 gchar
*ms_text_format
= sipmsg_find_header(msg
, "ms-text-format");
2372 if (ms_text_format
&& !strncmp(ms_text_format
, "text/plain", 10)) {
2373 gchar
*msgr
= sipmsg_find_part_of_header(ms_text_format
, "msgr=", ";", NULL
);
2374 gchar
*x_mms_im_format
= sipmsg_get_x_mms_im_format(msgr
);
2377 gchar
*ms_body
= sipmsg_find_part_of_header(ms_text_format
, "ms-body=", NULL
, NULL
);
2379 gchar
*body
= purple_base64_decode(ms_body
, NULL
);
2381 gchar
*body_esc
= g_markup_escape_text(body
, -1);
2382 gchar
*body_html
= sipmsg_apply_x_mms_im_format(x_mms_im_format
, body_esc
);
2385 serv_got_im(sip
->gc
, from
, body_html
, 0, time(NULL
));
2387 sipmsg_add_header(msg
, "Supported", "ms-text-format"); // accepts message reciept
2389 g_free(x_mms_im_format
);
2393 sipmsg_remove_header(msg
, "Ms-Conversation-ID");
2394 sipmsg_remove_header(msg
, "Ms-Text-Format");
2395 sipmsg_remove_header(msg
, "EndPoints");
2396 sipmsg_remove_header(msg
, "User-Agent");
2397 sipmsg_remove_header(msg
, "Roster-Manager");
2399 sipmsg_add_header(msg
, "User-Agent", purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
));
2400 //sipmsg_add_header(msg, "Supported", "ms-renders-gif");
2402 send_sip_response(sip
->gc
, msg
, 200, "OK", g_strdup_printf(
2404 "o=- 0 0 IN IP4 %s\r\n"
2408 "m=message %d sip sip:%s\r\n"
2409 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
2410 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
2411 sip
->realport
, sip
->username
));
2414 static void sipe_connection_cleanup(struct sipe_account_data
*);
2415 static void create_connection(struct sipe_account_data
*, gchar
*, int);
2417 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2419 gchar
*tmp
, krb5_token
;
2420 const gchar
*expires_header
;
2423 expires_header
= sipmsg_find_header(msg
, "Expires");
2424 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
2425 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
2427 switch (msg
->response
) {
2430 sip
->registerstatus
= 0;
2432 sip
->reregister
+= expires
- sip
->registerexpire
; //adjust to allowed expire
2433 sip
->registerexpire
= expires
;
2434 sip
->registerstatus
= 3;
2435 if (sip
->registrar
.expires
== 0)
2437 /* we have to reauthenticate as our security token expires
2438 after eight hours (be five minutes early) */
2439 sip
->registrar
.expires
= time(NULL
) + (8 * 3600) - 360;
2441 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
2444 gchar
*contact_hdr
= NULL
;
2446 gchar
* uuid
= generateUUIDfromEPID(get_epid());
2447 // There can be multiple Contact headers (one per location where the user is logged in) so
2448 // make sure to only get the one for this uuid
2449 for (i
= 0; contact_hdr
= sipmsg_find_header_instance (msg
, "Contact", i
); i
++) {
2450 gchar
* valid_contact
= sipmsg_find_part_of_header (contact_hdr
, uuid
, NULL
, NULL
);
2451 if (valid_contact
) {
2452 gruu
= sipmsg_find_part_of_header(contact_hdr
, "gruu=\"", "\"", NULL
);
2453 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "got gruu %s from contact hdr w/ right uuid: %s\n", gruu, contact_hdr);
2454 g_free(valid_contact
);
2457 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "ignoring contact hdr b/c not right uuid: %s\n", contact_hdr);
2462 sip
->contact
= g_strdup_printf("<%s>", gruu
);
2465 //purple_debug(PURPLE_DEBUG_MISC, "sipe", "didn't find gruu in a Contact hdr\n");
2466 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
);
2469 /* get buddies from blist; Has a bug */
2470 subscribe_timeout(sip
);
2472 tmp
= sipmsg_find_header(msg
, "Allow-Events");
2473 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
2474 sipe_subscribe_buddylist(sip
, msg
);
2477 if (purple_account_get_bool(sip
->account
, "clientkeepalive", FALSE
)) {
2478 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "Setting user defined keepalive\n");
2479 sip
->keepalive_timeout
= purple_account_get_int(sip
->account
, "keepalive", 0);
2481 tmp
= sipmsg_find_header(msg
, "ms-keep-alive");
2483 sipe_keep_alive_timeout(sip
, tmp
);
2487 sipe_subscribe_acl(sip
, msg
);
2488 sipe_subscribe_roaming_self(sip
, msg
);
2489 sipe_subscribe_roaming_provisioning(sip
, msg
);
2490 sipe_subscribe_pending_buddies(sip
, msg
);
2491 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
2493 // Should we remove the transaction here?
2494 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
2495 transactions_remove(sip
, tc
);
2500 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
2502 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
2503 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
2507 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
2510 tmp
= g_strsplit(parts
[0], ":", 0);
2511 hostname
= g_strdup(tmp
[0]);
2512 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
2516 tmp
= g_strsplit(parts
[i
], "=", 0);
2518 if (g_strcasecmp("transport", tmp
[0]) == 0) {
2519 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
2520 transport
= SIPE_TRANSPORT_TCP
;
2521 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
2522 transport
= SIPE_TRANSPORT_UDP
;
2531 /* Close old connection */
2532 sipe_connection_cleanup(sip
);
2534 /* Create new connection */
2535 sip
->transport
= transport
;
2536 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
2537 hostname
, port
, TRANSPORT_DESCRIPTOR
);
2538 create_connection(sip
, hostname
, port
);
2543 if (sip
->registerstatus
!= 2) {
2544 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
2545 if (sip
->registrar
.retries
> 3) {
2546 sip
->gc
->wants_to_die
= TRUE
;
2547 purple_connection_error(sip
->gc
, _("Wrong Password"));
2550 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2551 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
2553 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2555 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
2556 fill_auth(sip
, tmp
, &sip
->registrar
);
2557 sip
->registerstatus
= 2;
2558 if (sip
->account
->disconnecting
) {
2559 do_register_exp(sip
, 0);
2567 const gchar
*warning
= sipmsg_find_header(msg
, "Warning");
2568 if (warning
!= NULL
) {
2570 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
2572 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
2573 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
2576 warning
= _("You have been rejected by the server");
2579 sip
->gc
->wants_to_die
= TRUE
;
2580 purple_connection_error(sip
->gc
, warning
);
2586 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2587 if (warning
!= NULL
) {
2588 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2589 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
2592 warning
= _("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator");
2595 sip
->gc
->wants_to_die
= TRUE
;
2596 purple_connection_error(sip
->gc
, warning
);
2602 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2603 if (warning
!= NULL
) {
2604 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2605 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
2608 warning
= _("Service unavailable: no reason given");
2611 sip
->gc
->wants_to_die
= TRUE
;
2612 purple_connection_error(sip
->gc
, warning
);
2620 static void process_incoming_notify_rlmi_resub(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
2622 const char *uri
,*state
;
2624 xmlnode
*xn_resource
;
2625 xmlnode
*xn_instance
;
2627 xn_list
= xmlnode_from_str(data
, len
);
2628 xn_resource
= xmlnode_get_child(xn_list
, "resource");
2629 if (!xn_resource
) return;
2630 xn_instance
= xmlnode_get_child(xn_resource
, "instance");
2631 if (!xn_instance
) return;
2632 state
= xmlnode_get_attrib(xn_instance
, "state");
2633 uri
= xmlnode_get_attrib(xn_instance
, "cid");
2634 purple_debug_info("sipe", "process_incoming_notify_rlmi_resub: uri(%s),state(%s)\n",uri
,state
);
2635 sip
->presence_method_version
= 0;
2636 sipe_subscribe_to_name(sip
, uri
);
2640 static void process_incoming_notify_rlmi(struct sipe_account_data
*sip
, const gchar
*data
, unsigned len
)
2643 xmlnode
*xn_categories
;
2644 xmlnode
*xn_category
;
2647 const char *activity
= NULL
;
2649 xn_categories
= xmlnode_from_str(data
, len
);
2650 uri
= xmlnode_get_attrib(xn_categories
, "uri");
2652 purple_debug_info("sipe", "process_incoming_notify_rlmi\n");
2654 for (xn_category
= xmlnode_get_child(xn_categories
, "category");
2656 xn_category
= xmlnode_get_next_twin(xn_category
) )
2658 const char *attrVar
= xmlnode_get_attrib(xn_category
, "name");
2660 if (!strcmp(attrVar
, "note"))
2662 xn_node
= xmlnode_get_child(xn_category
, "note");
2663 if (!xn_node
) continue;
2664 xn_node
= xmlnode_get_child(xn_node
, "body");
2665 if (!xn_node
) continue;
2667 char *note
= xmlnode_get_data(xn_node
);
2668 struct sipe_buddy
*sbuddy
;
2670 sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
2673 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
2674 sbuddy
->annotation
= g_strdup(note
);
2678 else if(!strcmp(attrVar
, "state"))
2680 xn_node
= xmlnode_get_child(xn_category
, "state");
2681 if (!xn_node
) continue;
2682 xn_node
= xmlnode_get_child(xn_node
, "availability");
2683 if (!xn_node
) continue;
2685 char *data
= xmlnode_get_data(xn_node
);
2686 int avail
= atoi(data
);
2689 activity
= "unknown";
2690 else if (avail
< 4500)
2691 activity
= "available";
2692 else if (avail
< 6000)
2694 else if (avail
< 7500)
2696 else if (avail
< 9000)
2698 else if (avail
< 12000)
2700 else if (avail
< 18000)
2703 activity
= "offline";
2710 purple_prpl_got_user_status(sip
->account
, uri
, activity
, NULL
);
2712 xmlnode_free(xn_categories
);
2715 static void process_incoming_notify_pidf(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2719 gchar
*getbasic
= g_strdup("closed");
2720 gchar
*activity
= g_strdup("available");
2722 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
2723 gboolean isonline
= FALSE
;
2725 fromhdr
= sipmsg_find_header(msg
, "From");
2726 from
= parse_from(fromhdr
);
2732 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2734 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf:%s\n",msg
->body
);
2738 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
2740 if ((status
= xmlnode_get_child(tuple
, "status"))) {
2741 basicstatus
= xmlnode_get_child(status
, "basic");
2746 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
2751 getbasic
= xmlnode_get_data(basicstatus
);
2753 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
2758 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", getbasic
);
2759 if (strstr(getbasic
, "open")) {
2763 xmlnode
*display_name_node
= xmlnode_get_child(pidf
, "display-name");
2764 if (display_name_node
) {
2765 PurpleBuddy
* buddy
= purple_find_buddy (sip
->account
, from
);
2766 char * display_name
= xmlnode_get_data(display_name_node
);
2767 if (buddy
&& display_name
) {
2768 purple_blist_server_alias_buddy (buddy
, g_strdup(display_name
));
2772 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
2773 if ((status
= xmlnode_get_child(tuple
, "status"))) {
2774 if (basicstatus
= xmlnode_get_child(status
, "activities")) {
2775 if (basicstatus
= xmlnode_get_child(basicstatus
, "activity")) {
2776 activity
= xmlnode_get_data(basicstatus
);
2782 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
2785 gchar
* status_id
= NULL
;
2787 if (strstr(activity
, "busy")) {
2789 } else if (strstr(activity
, "away")) {
2795 status_id
= "available";
2798 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
2799 purple_prpl_got_user_status(sip
->account
, from
, status_id
, NULL
);
2801 purple_prpl_got_user_status(sip
->account
, from
, "offline", NULL
);
2810 static void process_incoming_notify_msrtc(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2812 const char *availability
;
2813 const char *activity
;
2814 const char *note
= NULL
;
2815 const char *activity_name
;
2818 xmlnode
*xn_presentity
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2819 xmlnode
*xn_availability
= xmlnode_get_child(xn_presentity
, "availability");
2820 xmlnode
*xn_activity
= xmlnode_get_child(xn_presentity
, "activity");
2821 xmlnode
*xn_userinfo
= xmlnode_get_child(xn_presentity
, "userInfo");
2822 xmlnode
*xn_note
= xmlnode_get_child(xn_userinfo
, "note");
2824 uri
= g_strdup_printf("sip:%s", xmlnode_get_attrib(xn_presentity
, "uri"));
2825 availability
= xmlnode_get_attrib(xn_availability
, "aggregate");
2826 activity
= xmlnode_get_attrib(xn_activity
, "aggregate");
2828 note
= xmlnode_get_data(xn_note
);
2831 int avl
= atoi(availability
);
2832 int act
= atoi(activity
);
2835 activity_name
= "away";
2836 else if (act
<= 150)
2837 activity_name
= "out-to-lunch";
2838 else if (act
<= 300)
2839 activity_name
= "be-right-back";
2840 else if (act
<= 400)
2841 activity_name
= "available";
2842 else if (act
<= 500)
2843 activity_name
= "on-the-phone";
2844 else if (act
<= 600)
2845 activity_name
= "busy";
2847 activity_name
= "available";
2850 activity_name
= "offline";
2852 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sip
->buddies
, uri
);
2855 if (sbuddy
->annotation
) { g_free(sbuddy
->annotation
); }
2856 sbuddy
->annotation
= NULL
;
2857 if (note
) { sbuddy
->annotation
= g_strdup(note
); }
2860 purple_debug_info("sipe", "process_incoming_notify_msrtc: status(%s)\n", activity_name
);
2861 purple_prpl_got_user_status(sip
->account
, uri
, activity_name
, NULL
);
2862 xmlnode_free(xn_presentity
);
2866 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2868 char *ctype
= sipmsg_find_header(msg
, "Content-Type");
2870 purple_debug_info("sipe", "process_incoming_notify: Content-Type: %s\n\n%s\n", ctype
, msg
->body
);
2872 if ( ctype
&& ( strstr(ctype
, "application/rlmi+xml")
2873 || strstr(ctype
, "application/msrtc-event-categories+xml") ) )
2875 const char *content
= msg
->body
;
2876 unsigned length
= msg
->bodylen
;
2877 PurpleMimeDocument
*mime
= NULL
;
2879 if (strstr(ctype
, "multipart"))
2881 char *doc
= g_strdup_printf("Content-Type: %s\r\n\r\n%s", ctype
, msg
->body
);
2882 mime
= purple_mime_document_parse(doc
);
2883 GList
* parts
= purple_mime_document_get_parts(mime
);
2884 content
= purple_mime_part_get_data(parts
->data
);
2885 length
= purple_mime_part_get_length(parts
->data
);
2888 char *subscription_state
= sipmsg_find_header(msg
, "subscription-state");
2889 if (strstr(subscription_state
, "active")){
2890 process_incoming_notify_rlmi(sip
, content
, length
);
2892 else if (strstr(subscription_state
, "terminated")){
2893 purple_debug_info("sipe", "process_incoming_notify: subscription_state:%s\n\n",subscription_state
);
2894 process_incoming_notify_rlmi_resub(sip
,content
,length
);
2899 purple_mime_document_free(mime
);
2902 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
2904 process_incoming_notify_msrtc(sip
, msg
);
2908 process_incoming_notify_pidf(sip
, msg
);
2910 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2913 static gchar
* gen_xpidf(struct sipe_account_data
*sip
)
2915 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
2917 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
2918 "<display name=\"sip:%s\"/>\r\n"
2919 "<atom id=\"1234\">\r\n"
2920 "<address uri=\"sip:%s\">\r\n"
2921 "<status status=\"%s\"/>\r\n"
2934 static gchar
* gen_pidf(struct sipe_account_data
*sip
)
2936 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
2937 "<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"
2938 "<tuple id=\"0\">\r\n"
2940 "<basic>open</basic>\r\n"
2941 "<ep:activities>\r\n"
2942 " <ep:activity>%s</ep:activity>\r\n"
2946 "<ci:display-name>%s</ci:display-name>\r\n"
2954 static void send_clear_notes(struct sipe_account_data
*sip
)
2959 process_send_presence_info_v0_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2961 if (msg
->response
== 488) {
2962 sip
->presence_method_version
= 1;
2963 send_presence_info(sip
);
2968 static void send_presence_info_v0(struct sipe_account_data
*sip
, char * note
)
2970 int availability
, activity
;
2971 availability
= 300; // online
2972 activity
= 400; // Available
2973 if (!strcmp(sip
->status
, "away")) {
2975 } else if (!strcmp(sip
->status
, "out-to-lunch")) {
2977 } else if (!strcmp(sip
->status
, "be-right-back")) {
2979 } else if (!strcmp(sip
->status
, "on-the-phone")) {
2981 } else if (!strcmp(sip
->status
, "do-not-disturb")) {
2983 } else if (!strcmp(sip
->status
, "busy")) {
2985 } else if (!strcmp(sip
->status
, "invisible")) {
2986 availability
= 0; // offline
2990 gchar
*name
= g_strdup_printf("sip: sip:%s", sip
->username
);
2991 //@TODO: send user data - state; add hostname in upper case
2992 gchar
* body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
, name
, availability
, activity
, note
? note
: "");
2993 send_soap_request_with_cb(sip
, body
, process_send_presence_info_v0_response
, NULL
);
2999 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3001 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3002 if (msg
->response
== 200) {
3003 sip
->status_version
= 0;
3004 send_presence_info(sip
);
3010 process_send_presence_info_v1_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
3012 if (msg
->response
== 409) {
3013 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
3014 // TODO need to parse the version #'s?
3015 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
3016 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
3018 gchar
*tmp
= get_contact(sip
);
3019 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
3020 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3022 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
3032 static void send_presence_info_v1(struct sipe_account_data
*sip
, char * note
)
3035 if (!strcmp(sip
->status
, "away")) {
3037 } else if (!strcmp(sip
->status
, "busy")) {
3044 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
3045 gchar
*doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
3046 sip
->status_version
, code
,
3047 sip
->status_version
, code
,
3048 sip
->status_version
, note
? note
: "",
3049 sip
->status_version
, note
? note
: "",
3050 sip
->status_version
, note
? note
: ""
3052 sip
->status_version
++;
3054 gchar
*tmp
= get_contact(sip
);
3055 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
3056 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
3058 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_info_v1_response
);
3066 static void send_presence_info(struct sipe_account_data
*sip
)
3068 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
3069 if (!status
) return;
3071 gchar
*note
= g_strdup(purple_status_get_attr_string(status
, "message"));
3073 purple_debug_info("sipe", "sending presence info, version = %d\n", sip
->presence_method_version
);
3074 if (sip
->presence_method_version
!= 1) {
3075 send_presence_info_v0(sip
, note
);
3077 send_presence_info_v1(sip
, note
);
3081 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
3083 gboolean found
= FALSE
;
3084 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
3085 if (msg
->response
== 0) { /* request */
3086 if (!strcmp(msg
->method
, "MESSAGE")) {
3087 process_incoming_message(sip
, msg
);
3089 } else if (!strcmp(msg
->method
, "NOTIFY")) {
3090 purple_debug_info("sipe","send->process_incoming_notify\n");
3091 process_incoming_notify(sip
, msg
);
3093 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
3094 purple_debug_info("sipe","send->process_incoming_benotify\n");
3095 process_incoming_benotify(sip
, msg
);
3097 } else if (!strcmp(msg
->method
, "INVITE")) {
3098 process_incoming_invite(sip
, msg
);
3100 } else if (!strcmp(msg
->method
, "INFO")) {
3102 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
3104 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
3106 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3108 } else if (!strcmp(msg
->method
, "ACK")) {
3109 // ACK's don't need any response
3111 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
3112 // LCS 2005 sends us these - just respond 200 OK
3114 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3115 } else if (!strcmp(msg
->method
, "BYE")) {
3116 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
3118 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
3119 struct sip_im_session
* session
= find_im_session (sip
, from
);
3123 // TODO Let the user know the other user left the conversation?
3124 im_session_destroy(sip
, session
);
3129 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
3131 } else { /* response */
3132 struct transaction
*trans
= transactions_find(sip
, msg
);
3134 if (msg
->response
== 407) {
3135 gchar
*resend
, *auth
, *ptmp
;
3137 if (sip
->proxy
.retries
> 30) return;
3138 sip
->proxy
.retries
++;
3139 /* do proxy authentication */
3141 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
3143 fill_auth(sip
, ptmp
, &sip
->proxy
);
3144 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
3145 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
3146 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
3148 resend
= sipmsg_to_string(trans
->msg
);
3149 /* resend request */
3150 sendout_pkt(sip
->gc
, resend
);
3153 if (msg
->response
== 100 || msg
->response
== 180) {
3154 /* ignore provisional response */
3155 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
3157 sip
->proxy
.retries
= 0;
3158 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
3159 if (msg
->response
== 401)
3161 sip
->registrar
.retries
++;
3162 sip
->registrar
.expires
= 0;
3166 sip
->registrar
.retries
= 0;
3168 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
3170 if (msg
->response
== 401) {
3171 gchar
*resend
, *auth
, *ptmp
;
3173 if (sip
->registrar
.retries
> 4) return;
3174 sip
->registrar
.retries
++;
3176 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
3177 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
3179 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
3182 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
3184 fill_auth(sip
, ptmp
, &sip
->registrar
);
3185 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
3186 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
3187 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
3189 //sipmsg_remove_header(trans->msg, "Authorization");
3190 //sipmsg_add_header(trans->msg, "Authorization", auth);
3192 resend
= sipmsg_to_string(trans
->msg
);
3193 /* resend request */
3194 sendout_pkt(sip
->gc
, resend
);
3199 if (trans
->callback
) {
3200 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
3201 /* call the callback to process response*/
3202 (trans
->callback
)(sip
, msg
, trans
);
3204 /* Not sure if this is needed or what needs to be done
3205 but transactions seem to be removed prematurely so
3206 this only removes them if the response is 200 OK */
3207 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
3208 /*Has a bug and it's unneccesary*/
3209 /*transactions_remove(sip, trans);*/
3215 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
3219 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
3223 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
3231 /* according to the RFC remove CRLF at the beginning */
3232 while (*cur
== '\r' || *cur
== '\n') {
3235 if (cur
!= conn
->inbuf
) {
3236 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
3237 conn
->inbufused
= strlen(conn
->inbuf
);
3240 /* Received a full Header? */
3241 sip
->processing_input
= TRUE
;
3242 while (sip
->processing_input
&&
3243 ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
)) {
3244 time_t currtime
= time(NULL
);
3247 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
3248 msg
= sipmsg_parse_header(conn
->inbuf
);
3251 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
3252 if (restlen
>= msg
->bodylen
) {
3253 dummy
= g_malloc(msg
->bodylen
+ 1);
3254 memcpy(dummy
, cur
, msg
->bodylen
);
3255 dummy
[msg
->bodylen
] = '\0';
3257 cur
+= msg
->bodylen
;
3258 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
3259 conn
->inbufused
= strlen(conn
->inbuf
);
3261 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
3262 restlen
, msg
->bodylen
, (int)strlen(conn
->inbuf
));
3268 purple_debug_info("sipe", "body:\n%s", msg->body);
3271 // Verify the signature before processing it
3272 if (sip
->registrar
.ntlm_key
) {
3273 struct sipmsg_breakdown msgbd
;
3275 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
3276 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
3278 if (signature_input_str
!= NULL
) {
3279 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
3282 gchar
* rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
3284 if (signature
!= NULL
) {
3285 if (rspauth
!= NULL
) {
3286 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
3287 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
3288 process_input_message(sip
, msg
);
3290 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
3291 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
3292 sip
->gc
->wants_to_die
= TRUE
;
3294 } else if (msg
->response
== 401) {
3295 purple_connection_error(sip
->gc
, _("Wrong Password"));
3296 sip
->gc
->wants_to_die
= TRUE
;
3300 sipmsg_breakdown_free(&msgbd
);
3302 process_input_message(sip
, msg
);
3307 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
3309 PurpleConnection
*gc
= data
;
3310 struct sipe_account_data
*sip
= gc
->proto_data
;
3315 static char buffer
[65536];
3316 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
3318 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
3319 msg
= sipmsg_parse_msg(buffer
);
3320 if (msg
) process_input_message(sip
, msg
);
3324 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
3326 struct sipe_account_data
*sip
= gc
->proto_data
;
3327 PurpleSslConnection
*gsc
= sip
->gsc
;
3329 purple_debug_error("sipe", "%s",debug
);
3330 purple_connection_error(gc
, msg
);
3332 /* Invalidate this connection. Next send will open a new one */
3334 connection_remove(sip
, gsc
->fd
);
3335 purple_ssl_close(gsc
);
3341 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
3343 PurpleConnection
*gc
= data
;
3344 struct sipe_account_data
*sip
;
3345 struct sip_connection
*conn
;
3347 gboolean firstread
= TRUE
;
3349 /* NOTE: This check *IS* necessary */
3350 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
3351 purple_ssl_close(gsc
);
3355 sip
= gc
->proto_data
;
3356 conn
= connection_find(sip
, gsc
->fd
);
3358 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
3359 gc
->wants_to_die
= TRUE
;
3360 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
3364 /* Read all available data from the SSL connection */
3366 /* Increase input buffer size as needed */
3367 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
3368 conn
->inbuflen
+= SIMPLE_BUF_INC
;
3369 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
3370 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
3373 /* Try to read as much as there is space left in the buffer */
3374 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
3375 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
3377 if (len
< 0 && errno
== EAGAIN
) {
3378 /* Try again later */
3380 } else if (len
< 0) {
3381 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
3383 } else if (firstread
&& (len
== 0)) {
3384 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
3388 conn
->inbufused
+= len
;
3391 /* Equivalence indicates that there is possibly more data to read */
3392 } while (len
== readlen
);
3394 conn
->inbuf
[conn
->inbufused
] = '\0';
3395 process_input(sip
, conn
);
3399 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
3401 PurpleConnection
*gc
= data
;
3402 struct sipe_account_data
*sip
= gc
->proto_data
;
3404 struct sip_connection
*conn
= connection_find(sip
, source
);
3406 purple_debug_error("sipe", "Connection not found!\n");
3410 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
3411 conn
->inbuflen
+= SIMPLE_BUF_INC
;
3412 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
3415 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
3417 if (len
< 0 && errno
== EAGAIN
)
3419 else if (len
<= 0) {
3420 purple_debug_info("sipe", "sipe_input_cb: read error\n");
3421 connection_remove(sip
, source
);
3422 if (sip
->fd
== source
) sip
->fd
= -1;
3426 conn
->inbufused
+= len
;
3427 conn
->inbuf
[conn
->inbufused
] = '\0';
3429 process_input(sip
, conn
);
3432 /* Callback for new connections on incoming TCP port */
3433 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
3435 PurpleConnection
*gc
= data
;
3436 struct sipe_account_data
*sip
= gc
->proto_data
;
3437 struct sip_connection
*conn
;
3439 int newfd
= accept(source
, NULL
, NULL
);
3441 conn
= connection_create(sip
, newfd
);
3443 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
3446 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
3448 PurpleConnection
*gc
= data
;
3449 struct sipe_account_data
*sip
;
3450 struct sip_connection
*conn
;
3452 if (!PURPLE_CONNECTION_IS_VALID(gc
))
3460 purple_connection_error(gc
, _("Could not connect"));
3464 sip
= gc
->proto_data
;
3466 sip
->last_keepalive
= time(NULL
);
3468 conn
= connection_create(sip
, source
);
3470 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
3473 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
3476 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
3478 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
3479 if (sip
== NULL
) return;
3481 sip
->registertimeout
= purple_timeout_add((rand()%100) + 10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
3485 static guint
sipe_ht_hash_nick(const char *nick
)
3487 char *lc
= g_utf8_strdown(nick
, -1);
3488 guint bucket
= g_str_hash(lc
);
3494 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
3496 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
3499 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
3501 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
3503 sip
->listen_data
= NULL
;
3505 if (listenfd
== -1) {
3506 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
3512 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
3513 sip
->listenfd
= sip
->fd
;
3515 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
3517 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
3518 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
3522 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
3524 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
3527 sip
->query_data
= NULL
;
3529 if (!hosts
|| !hosts
->data
) {
3530 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
3534 addr_size
= GPOINTER_TO_INT(hosts
->data
);
3535 hosts
= g_slist_remove(hosts
, hosts
->data
);
3536 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
3537 g_free(hosts
->data
);
3538 hosts
= g_slist_remove(hosts
, hosts
->data
);
3540 hosts
= g_slist_remove(hosts
, hosts
->data
);
3541 g_free(hosts
->data
);
3542 hosts
= g_slist_remove(hosts
, hosts
->data
);
3545 /* create socket for incoming connections */
3546 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
3547 sipe_udp_host_resolved_listen_cb
, sip
);
3548 if (sip
->listen_data
== NULL
) {
3549 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
3554 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
3557 PurpleConnection
*gc
= data
;
3558 struct sipe_account_data
*sip
;
3560 /* If the connection is already disconnected, we don't need to do anything else */
3561 if (!PURPLE_CONNECTION_IS_VALID(gc
))
3564 sip
= gc
->proto_data
;
3569 case PURPLE_SSL_CONNECT_FAILED
:
3570 purple_connection_error(gc
, _("Connection Failed"));
3572 case PURPLE_SSL_HANDSHAKE_FAILED
:
3573 purple_connection_error(gc
, _("SSL Handshake Failed"));
3579 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
3581 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
3582 PurpleProxyConnectData
*connect_data
;
3584 sip
->listen_data
= NULL
;
3586 sip
->listenfd
= listenfd
;
3587 if (sip
->listenfd
== -1) {
3588 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
3592 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
3593 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
3594 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
3595 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
3596 sipe_newconn_cb
, sip
->gc
);
3597 purple_debug_info("sipe", "connecting to %s port %d\n",
3598 sip
->realhostname
, sip
->realport
);
3599 /* open tcp connection to the server */
3600 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
3601 sip
->realport
, login_cb
, sip
->gc
);
3603 if (connect_data
== NULL
) {
3604 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
3609 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
3611 PurpleAccount
*account
= sip
->account
;
3612 PurpleConnection
*gc
= sip
->gc
;
3614 if (purple_account_get_bool(account
, "useport", FALSE
)) {
3615 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
3616 port
= purple_account_get_int(account
, "port", 0);
3618 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
3621 sip
->realhostname
= hostname
;
3622 sip
->realport
= port
;
3624 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
3627 /* TODO: is there a good default grow size? */
3628 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
3629 sip
->txbuf
= purple_circ_buffer_new(0);
3631 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
3633 if (!purple_ssl_is_supported()) {
3634 gc
->wants_to_die
= TRUE
;
3635 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
3639 purple_debug_info("sipe", "using SSL\n");
3641 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
3642 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
3643 if (sip
->gsc
== NULL
) {
3644 purple_connection_error(gc
, _("Could not create SSL context"));
3647 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
3649 purple_debug_info("sipe", "using UDP\n");
3651 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
3652 if (sip
->query_data
== NULL
) {
3653 purple_connection_error(gc
, _("Could not resolve hostname"));
3657 purple_debug_info("sipe", "using TCP\n");
3658 /* create socket for incoming connections */
3659 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
3660 sipe_tcp_connect_listen_cb
, sip
);
3661 if (sip
->listen_data
== NULL
) {
3662 purple_connection_error(gc
, _("Could not create listen socket"));
3668 /* Service list for autodection */
3669 static const struct sipe_service_data service_autodetect
[] = {
3670 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
3671 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
3672 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
3673 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
3677 /* Service list for SSL/TLS */
3678 static const struct sipe_service_data service_tls
[] = {
3679 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
3680 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
3684 /* Service list for TCP */
3685 static const struct sipe_service_data service_tcp
[] = {
3686 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
3687 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
3691 /* Service list for UDP */
3692 static const struct sipe_service_data service_udp
[] = {
3693 { "sip", "udp", SIPE_TRANSPORT_UDP
},
3697 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
3698 static void resolve_next_service(struct sipe_account_data
*sip
,
3699 const struct sipe_service_data
*start
)
3702 sip
->service_data
= start
;
3704 sip
->service_data
++;
3705 if (sip
->service_data
->service
== NULL
) {
3706 /* Try connecting to the SIP hostname directly */
3707 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
3708 if (sip
->auto_transport
) {
3709 // If SSL is supported, default to using it; OCS servers aren't configured
3710 // by default to accept TCP
3711 // TODO: LCS 2007 is the opposite, only configured by default to accept TCP
3712 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
3713 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "set transport type..\n");
3716 gchar
* hostname
= g_strdup(sip
->sipdomain
);
3717 create_connection(sip
, hostname
, 0);
3722 /* Try to resolve next service */
3723 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
3724 sip
->service_data
->transport
,
3729 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
3731 struct sipe_account_data
*sip
= data
;
3733 sip
->srv_query_data
= NULL
;
3735 /* find the host to connect to */
3737 gchar
*hostname
= g_strdup(resp
->hostname
);
3738 int port
= resp
->port
;
3739 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
3743 sip
->transport
= sip
->service_data
->type
;
3745 create_connection(sip
, hostname
, port
);
3747 resolve_next_service(sip
, NULL
);
3751 static void sipe_login(PurpleAccount
*account
)
3753 PurpleConnection
*gc
;
3754 struct sipe_account_data
*sip
;
3756 const char *transport
;
3758 const char *username
= purple_account_get_username(account
);
3759 gc
= purple_account_get_connection(account
);
3761 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
3762 gc
->wants_to_die
= TRUE
;
3763 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
3767 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
3768 gc
->flags
|= PURPLE_CONNECTION_HTML
| PURPLE_CONNECTION_FORMATTING_WBFO
| PURPLE_CONNECTION_NO_BGCOLOR
|
3769 PURPLE_CONNECTION_NO_FONTSIZE
| PURPLE_CONNECTION_NO_URLDESC
| PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY
;
3771 sip
->account
= account
;
3772 sip
->registerexpire
= 900;
3774 userserver
= g_strsplit(username
, "@", 2);
3775 purple_connection_set_display_name(gc
, userserver
[0]);
3776 sip
->username
= g_strdup(g_strjoin("@", userserver
[0], userserver
[1], NULL
));
3777 sip
->sipdomain
= g_strdup(userserver
[1]);
3778 sip
->password
= g_strdup(purple_connection_get_password(gc
));
3779 g_strfreev(userserver
);
3781 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
3783 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
3785 /* TODO: Set the status correctly. */
3786 sip
->status
= g_strdup("available");
3788 transport
= purple_account_get_string(account
, "transport", "auto");
3789 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
3790 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
3793 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
3794 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
3795 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
3796 } else if (strcmp(transport
, "auto") == 0) {
3797 sip
->auto_transport
= TRUE
;
3798 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
3799 } else if (strcmp(transport
, "tls") == 0) {
3800 resolve_next_service(sip
, service_tls
);
3801 } else if (strcmp(transport
, "tcp") == 0) {
3802 resolve_next_service(sip
, service_tcp
);
3804 resolve_next_service(sip
, service_udp
);
3808 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
3810 connection_free_all(sip
);
3812 if (sip
->query_data
!= NULL
)
3813 purple_dnsquery_destroy(sip
->query_data
);
3814 sip
->query_data
== NULL
;
3816 if (sip
->srv_query_data
!= NULL
)
3817 purple_srv_cancel(sip
->srv_query_data
);
3818 sip
->srv_query_data
= NULL
;
3820 if (sip
->listen_data
!= NULL
)
3821 purple_network_listen_cancel(sip
->listen_data
);
3822 sip
->listen_data
= NULL
;
3824 if (sip
->gsc
!= NULL
)
3825 purple_ssl_close(sip
->gsc
);
3828 sipe_auth_free(&sip
->registrar
);
3829 sipe_auth_free(&sip
->proxy
);
3832 purple_circ_buffer_destroy(sip
->txbuf
);
3835 g_free(sip
->realhostname
);
3836 sip
->realhostname
= NULL
;
3839 purple_input_remove(sip
->listenpa
);
3841 if (sip
->tx_handler
)
3842 purple_input_remove(sip
->tx_handler
);
3843 sip
->tx_handler
= 0;
3844 if (sip
->resendtimeout
)
3845 purple_timeout_remove(sip
->resendtimeout
);
3846 sip
->resendtimeout
= 0;
3847 if (sip
->registertimeout
)
3848 purple_timeout_remove(sip
->registertimeout
);
3849 sip
->registertimeout
= 0;
3852 sip
->processing_input
= FALSE
;
3855 static void sipe_close(PurpleConnection
*gc
)
3857 struct sipe_account_data
*sip
= gc
->proto_data
;
3860 /* leave all conversations */
3861 im_session_close_all(sip
);
3864 do_register_exp(sip
, 0);
3866 sipe_connection_cleanup(sip
);
3867 g_free(sip
->sipdomain
);
3868 g_free(sip
->username
);
3869 g_free(sip
->password
);
3871 g_free(gc
->proto_data
);
3872 gc
->proto_data
= NULL
;
3875 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
3877 PurpleAccount
*acct
= purple_connection_get_account(gc
);
3878 char *id
= g_strdup_printf("sip:%s", (char *)g_list_nth_data(row
, 0));
3879 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
3881 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
3882 purple_conversation_present(conv
);
3886 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
3889 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
3890 g_list_nth_data(row
, 0), NULL
, g_list_nth_data(row
, 1));
3893 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
3895 PurpleNotifySearchResults
*results
;
3896 PurpleNotifySearchColumn
*column
;
3897 xmlnode
*searchResults
;
3900 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3901 if (!searchResults
) {
3902 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
3906 results
= purple_notify_searchresults_new();
3908 if (results
== NULL
) {
3909 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
3910 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
3912 xmlnode_free(searchResults
);
3916 column
= purple_notify_searchresults_column_new(_("User Name"));
3917 purple_notify_searchresults_column_add(results
, column
);
3919 column
= purple_notify_searchresults_column_new(_("Name"));
3920 purple_notify_searchresults_column_add(results
, column
);
3922 column
= purple_notify_searchresults_column_new(_("Company"));
3923 purple_notify_searchresults_column_add(results
, column
);
3925 column
= purple_notify_searchresults_column_new(_("Country"));
3926 purple_notify_searchresults_column_add(results
, column
);
3928 column
= purple_notify_searchresults_column_new(_("Email"));
3929 purple_notify_searchresults_column_add(results
, column
);
3931 int match_count
= 0;
3932 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
3935 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
3936 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
3937 g_strfreev(uri_parts
);
3939 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
3940 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
3941 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
3942 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
3944 purple_notify_searchresults_row_add(results
, row
);
3948 gboolean more
= FALSE
;
3949 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
3950 char *data
= xmlnode_get_data_unescaped(mrow
);
3951 more
= (g_strcasecmp(data
, "true") == 0);
3955 gchar
*secondary
= g_strdup_printf(
3956 dngettext(GETTEXT_PACKAGE
,
3957 "Found %d contact%s:",
3958 "Found %d contacts%s:", match_count
),
3959 match_count
, more
? _(" (more matched your query)") : "");
3961 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
3962 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
3963 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
3966 xmlnode_free(searchResults
);
3970 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
3972 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
3973 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
3977 PurpleRequestField
*field
= entries
->data
;
3978 const char *id
= purple_request_field_get_id(field
);
3979 const char *value
= purple_request_field_string_get_value(field
);
3981 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
? value
: "");
3983 if (value
!= NULL
) attrs
[i
++] = g_strdup_printf(SIPE_SOAP_SEARCH_ROW
, id
, value
);
3984 } while ((entries
= g_list_next(entries
)) != NULL
);
3988 gchar
*query
= g_strjoinv(NULL
, attrs
);
3989 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
3990 send_soap_request_with_cb(gc
->proto_data
, body
,
3991 (TransCallback
) process_search_contact_response
, NULL
);
3999 static void sipe_show_find_contact(PurplePluginAction
*action
)
4001 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
4002 PurpleRequestFields
*fields
;
4003 PurpleRequestFieldGroup
*group
;
4004 PurpleRequestField
*field
;
4006 fields
= purple_request_fields_new();
4007 group
= purple_request_field_group_new(NULL
);
4008 purple_request_fields_add_group(fields
, group
);
4010 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
4011 purple_request_field_group_add_field(group
, field
);
4012 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
4013 purple_request_field_group_add_field(group
, field
);
4014 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
4015 purple_request_field_group_add_field(group
, field
);
4016 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
4017 purple_request_field_group_add_field(group
, field
);
4019 purple_request_fields(gc
,
4021 _("Search for a Contact"),
4022 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
4024 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
4026 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
4029 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
4031 PurpleConnection
*gc
= (PurpleConnection
*) context
;
4032 struct sipe_account_data
*sip
= gc
->proto_data
;
4034 PurplePluginAction
*act
;
4036 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
4037 menu
= g_list_prepend(menu
, act
);
4039 menu
= g_list_reverse(menu
);
4044 /* not needed since privacy is checked for every subscribe */
4045 static void dummy_add_deny(PurpleConnection
*gc
, const char *name
) {
4048 static void dummy_permit_deny(PurpleConnection
*gc
)
4052 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
4058 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
4064 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
4068 static char *sipe_status_text(PurpleBuddy
*buddy
)
4070 struct sipe_account_data
*sip
;
4071 struct sipe_buddy
*sbuddy
;
4073 sip
= (struct sipe_account_data
*) buddy
->account
->gc
->proto_data
;
4074 if (sip
) //happens on pidgin exit
4076 sbuddy
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
4077 if (sbuddy
&& sbuddy
->annotation
)
4079 return g_strdup(sbuddy
->annotation
);
4088 static void sipe_tooltip_text(PurpleBuddy
*buddy
, PurpleNotifyUserInfo
*user_info
, gboolean full
)
4090 char *annotation
= sipe_status_text(buddy
);
4094 purple_notify_user_info_add_pair( user_info
, _("Note"), annotation
);
4099 static PurplePlugin
*my_protocol
= NULL
;
4101 static PurplePluginProtocolInfo prpl_info
=
4104 NULL
, /* user_splits */
4105 NULL
, /* protocol_options */
4106 NO_BUDDY_ICONS
, /* icon_spec */
4107 sipe_list_icon
, /* list_icon */
4108 NULL
, /* list_emblems */
4109 sipe_status_text
, /* status_text */
4110 sipe_tooltip_text
, /* tooltip_text */ // add custom info to contact tooltip
4111 sipe_status_types
, /* away_states */
4112 NULL
, /* blist_node_menu */
4113 NULL
, /* chat_info */
4114 NULL
, /* chat_info_defaults */
4115 sipe_login
, /* login */
4116 sipe_close
, /* close */
4117 sipe_im_send
, /* send_im */
4118 NULL
, /* set_info */ // TODO maybe
4119 sipe_send_typing
, /* send_typing */
4120 NULL
, /* get_info */ // TODO maybe
4121 sipe_set_status
, /* set_status */
4122 NULL
, /* set_idle */
4123 NULL
, /* change_passwd */
4124 sipe_add_buddy
, /* add_buddy */
4125 NULL
, /* add_buddies */
4126 sipe_remove_buddy
, /* remove_buddy */
4127 NULL
, /* remove_buddies */
4128 sipe_add_permit
, /* add_permit */
4129 sipe_add_deny
, /* add_deny */
4130 sipe_add_deny
, /* rem_permit */
4131 sipe_add_permit
, /* rem_deny */
4132 dummy_permit_deny
, /* set_permit_deny */
4133 NULL
, /* join_chat */
4134 NULL
, /* reject_chat */
4135 NULL
, /* get_chat_name */
4136 NULL
, /* chat_invite */
4137 NULL
, /* chat_leave */
4138 NULL
, /* chat_whisper */
4139 NULL
, /* chat_send */
4140 sipe_keep_alive
, /* keepalive */
4141 NULL
, /* register_user */
4142 NULL
, /* get_cb_info */ // deprecated
4143 NULL
, /* get_cb_away */ // deprecated
4144 sipe_alias_buddy
, /* alias_buddy */
4145 sipe_group_buddy
, /* group_buddy */
4146 sipe_rename_group
, /* rename_group */
4147 NULL
, /* buddy_free */
4148 sipe_convo_closed
, /* convo_closed */
4149 purple_normalize_nocase
, /* normalize */
4150 NULL
, /* set_buddy_icon */
4151 sipe_remove_group
, /* remove_group */
4152 NULL
, /* get_cb_real_name */ // TODO?
4153 NULL
, /* set_chat_topic */
4154 NULL
, /* find_blist_chat */
4155 NULL
, /* roomlist_get_list */
4156 NULL
, /* roomlist_cancel */
4157 NULL
, /* roomlist_expand_category */
4158 NULL
, /* can_receive_file */
4159 NULL
, /* send_file */
4160 NULL
, /* new_xfer */
4161 NULL
, /* offline_message */
4162 NULL
, /* whiteboard_prpl_ops */
4163 sipe_send_raw
, /* send_raw */
4167 static PurplePluginInfo info
= {
4168 PURPLE_PLUGIN_MAGIC
,
4169 PURPLE_MAJOR_VERSION
,
4170 PURPLE_MINOR_VERSION
,
4171 PURPLE_PLUGIN_PROTOCOL
, /**< type */
4172 NULL
, /**< ui_requirement */
4174 NULL
, /**< dependencies */
4175 PURPLE_PRIORITY_DEFAULT
, /**< priority */
4176 "prpl-sipe", /**< id */
4177 "Microsoft LCS/OCS", /**< name */
4178 VERSION
, /**< version */
4179 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
4180 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
4181 "Anibal Avelar <avelar@gmail.com>, " /**< author */
4182 "Gabriel Burt <gburt@novell.com>", /**< author */
4183 PURPLE_WEBSITE
, /**< homepage */
4184 sipe_plugin_load
, /**< load */
4185 sipe_plugin_unload
, /**< unload */
4186 sipe_plugin_destroy
, /**< destroy */
4187 NULL
, /**< ui_info */
4188 &prpl_info
, /**< extra_info */
4197 static void init_plugin(PurplePlugin
*plugin
)
4199 PurpleAccountUserSplit
*split
;
4200 PurpleAccountOption
*option
;
4201 PurpleKeyValuePair
*kvp
;
4204 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
4205 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
4206 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
4209 purple_plugin_register(plugin
);
4211 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
4212 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4213 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
4214 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4216 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
4217 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4218 // Translators: noun (networking port)
4219 option
= purple_account_option_int_new(_("Port"), "port", 5061);
4220 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4222 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
4223 purple_account_option_add_list_item(option
, _("Auto"), "auto");
4224 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
4225 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
4226 purple_account_option_add_list_item(option
, _("UDP"), "udp");
4227 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4229 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
4230 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
4232 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
4233 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4235 // TODO commented out so won't show in the preferences until we fix krb message signing
4236 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
4237 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
4239 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
4240 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
4241 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
4244 option
= purple_account_option_string_new(_("Auth User"), "authuser", "");
4245 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4246 option
= purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
4247 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4248 option
= purple_account_option_bool_new(_("Use Client-specified Keepalive"), "clientkeepalive", FALSE
);
4249 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4250 option
= purple_account_option_int_new(_("Keepalive Timeout"), "keepalive", 300);
4251 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
4252 my_protocol
= plugin
;
4255 /* I had to redefined the function for it load, but works */
4256 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
4257 plugin
->info
= &(info
);
4258 init_plugin((plugin
));
4259 sipe_plugin_load((plugin
));
4260 return purple_plugin_register(plugin
);