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>
42 #define _LIBC_INTERNAL_
56 # define _(String) ((const char *) gettext (String))
58 # define _(String) ((const char *) (String))
59 #endif /* ENABLE_NLS */
61 #include "accountopt.h"
63 #include "conversation.h"
79 #endif /*USE_KERBEROS*/
82 #include "sipe-sign.h"
86 /* Keep in sync with sipe_transport_type! */
87 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
88 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
92 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
95 static gchar
*get_epid()
97 return sipe_uuid_get_macaddr();
100 static char *genbranch()
102 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
103 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
104 rand() & 0xFFFF, rand() & 0xFFFF);
107 static char *gencallid()
109 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
110 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
111 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
112 rand() & 0xFFFF, rand() & 0xFFFF);
115 static gchar
*find_tag(const gchar
*hdr
)
117 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
119 // In case it's at the end and there's no trailing ;
120 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
126 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
131 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
133 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
134 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
137 static void sipe_close(PurpleConnection
*gc
);
139 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
);
140 static void send_presence_info(struct sipe_account_data
*sip
);
142 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
);
144 static void sipe_keep_alive_timeout(struct sipe_account_data
*sip
, const gchar
*hdr
)
146 gchar
*timeout
= sipmsg_find_part_of_header(hdr
, "timeout=", ";", NULL
);
147 if (timeout
!= NULL
) {
148 sscanf(timeout
, "%u", &sip
->keepalive_timeout
);
149 purple_debug_info("sipe", "server determined keep alive timeout is %u seconds\n",
150 sip
->keepalive_timeout
);
154 static void sipe_keep_alive(PurpleConnection
*gc
)
156 struct sipe_account_data
*sip
= gc
->proto_data
;
157 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
158 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
159 gchar buf
[2] = {0, 0};
160 purple_debug_info("sipe", "sending keep alive\n");
161 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
163 time_t now
= time(NULL
);
164 if ((sip
->keepalive_timeout
> 0) &&
165 ((now
- sip
->last_keepalive
) >= sip
->keepalive_timeout
)
166 #if PURPLE_VERSION_CHECK(2,4,0)
167 && ((now
- gc
->last_received
) >= sip
->keepalive_timeout
)
170 purple_debug_info("sipe", "sending keep alive\n");
171 sendout_pkt(gc
, "\r\n\r\n");
172 sip
->last_keepalive
= now
;
177 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
179 struct sip_connection
*ret
= NULL
;
180 GSList
*entry
= sip
->openconns
;
183 if (ret
->fd
== fd
) return ret
;
189 static struct sipe_watcher
*watcher_find(struct sipe_account_data
*sip
,
192 struct sipe_watcher
*watcher
;
193 GSList
*entry
= sip
->watcher
;
195 watcher
= entry
->data
;
196 if (!strcmp(name
, watcher
->name
)) return watcher
;
202 static struct sipe_watcher
*watcher_create(struct sipe_account_data
*sip
,
203 const gchar
*name
, const gchar
*callid
, const gchar
*ourtag
,
204 const gchar
*theirtag
, gboolean needsxpidf
)
206 struct sipe_watcher
*watcher
= g_new0(struct sipe_watcher
, 1);
207 watcher
->name
= g_strdup(name
);
208 watcher
->dialog
.callid
= g_strdup(callid
);
209 watcher
->dialog
.ourtag
= g_strdup(ourtag
);
210 watcher
->dialog
.theirtag
= g_strdup(theirtag
);
211 watcher
->needsxpidf
= needsxpidf
;
212 sip
->watcher
= g_slist_append(sip
->watcher
, watcher
);
216 static void watcher_remove(struct sipe_account_data
*sip
, const gchar
*name
)
218 struct sipe_watcher
*watcher
= watcher_find(sip
, name
);
219 sip
->watcher
= g_slist_remove(sip
->watcher
, watcher
);
220 g_free(watcher
->name
);
221 g_free(watcher
->dialog
.callid
);
222 g_free(watcher
->dialog
.ourtag
);
223 g_free(watcher
->dialog
.theirtag
);
227 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
229 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
231 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
235 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
237 struct sip_connection
*conn
= connection_find(sip
, fd
);
238 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
239 if (conn
&& conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
244 static void connection_free_all(struct sipe_account_data
*sip
)
246 struct sip_connection
*ret
= NULL
;
247 GSList
*entry
= sip
->openconns
;
250 connection_remove(sip
, ret
->fd
);
251 entry
= sip
->openconns
;
255 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
)
257 const gchar
*method
= msg
->method
;
258 const gchar
*target
= msg
->target
;
263 const char *authdomain
;
264 const char *authuser
;
265 const char *krb5_realm
;
267 gchar
*krb5_token
= NULL
;
269 authdomain
= purple_account_get_string(sip
->account
, "authdomain", "");
270 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
272 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
273 // and do error checking
275 // KRB realm should always be uppercase
276 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
278 if (sip
->realhostname
) {
279 host
= sip
->realhostname
;
280 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
281 host
= purple_account_get_string(sip
->account
, "proxy", "");
283 host
= sip
->sipdomain
;
286 /*gboolean new_auth = krb5_auth.gss_context == NULL;
288 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
291 if (new_auth || force_reauth) {
292 krb5_token = krb5_auth.base64_token;
295 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
296 krb5_token = krb5_auth.base64_token;*/
298 if (!authuser
|| strlen(authuser
) < 1) {
299 authuser
= sip
->username
;
302 if (auth
->type
== 1) { /* Digest */
303 sprintf(noncecount
, "%08d", auth
->nc
++);
304 response
= purple_cipher_http_digest_calculate_response(
305 "md5", method
, target
, NULL
, NULL
,
306 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
307 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
309 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
);
312 } else if (auth
->type
== 2) { /* NTLM */
313 // If we have a signature for the message, include that
314 if (msg
->signature
) {
315 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
);
319 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
320 const gchar
* ntlm_key
;
321 const gchar
* hostname
= purple_get_host_name();
322 gchar
* gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, hostname
, authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
323 auth
->ntlm_key
= (gchar
*)ntlm_key
;
324 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
329 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
331 } else if (auth
->type
== 3) {
334 /*if (new_auth || force_reauth) {
335 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
337 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);
339 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
342 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
343 gchar * mic = "MICTODO";
344 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
345 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
346 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
347 //auth->opaque ? auth->opaque : "", auth->target);
348 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
353 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
356 sprintf(noncecount
, "%08d", auth
->nc
++);
357 response
= purple_cipher_http_digest_calculate_response(
358 "md5", method
, target
, NULL
, NULL
,
359 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
360 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
362 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
);
367 static char *parse_attribute(const char *attrname
, const char *source
)
369 const char *tmp
, *tmp2
;
371 int len
= strlen(attrname
);
373 if (!strncmp(source
, attrname
, len
)) {
375 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
377 retval
= g_strndup(tmp
, tmp2
- tmp
);
379 retval
= g_strdup(tmp
);
385 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
388 const char *authuser
;
391 const char *krb5_realm
;
394 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
395 // and do error checking
397 // KRB realm should always be uppercase
398 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
400 if (sip->realhostname) {
401 host = sip->realhostname;
402 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
403 host = purple_account_get_string(sip->account, "proxy", "");
405 host = sip->sipdomain;
408 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
410 if (!authuser
|| strlen(authuser
) < 1) {
411 authuser
= sip
->username
;
415 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
419 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
421 parts
= g_strsplit(hdr
+5, "\", ", 0);
424 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
425 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
426 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
429 if ((tmp
= parse_attribute("targetname=\"",
433 else if ((tmp
= parse_attribute("realm=\"",
437 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
444 if (!strstr(hdr
, "gssapi-data")) {
452 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
453 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
455 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
456 parts
= g_strsplit(hdr
+9, "\", ", 0);
459 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
460 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
461 /*if (krb5_auth.gss_context == NULL) {
462 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
464 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
467 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
469 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
471 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
482 parts
= g_strsplit(hdr
, " ", 0);
484 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
487 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
494 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
496 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
497 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
503 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
505 PurpleConnection
*gc
= data
;
506 struct sipe_account_data
*sip
= gc
->proto_data
;
510 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
512 if (max_write
== 0) {
513 if (sip
->tx_handler
!= 0){
514 purple_input_remove(sip
->tx_handler
);
520 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
522 if (written
< 0 && errno
== EAGAIN
)
524 else if (written
<= 0) {
525 /*TODO: do we really want to disconnect on a failure to write?*/
526 purple_connection_error(gc
, _("Could not write"));
530 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
533 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
535 PurpleConnection
*gc
= data
;
536 struct sipe_account_data
*sip
= gc
->proto_data
;
540 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
542 if (max_write
== 0) {
543 if (sip
->tx_handler
!= 0) {
544 purple_input_remove(sip
->tx_handler
);
550 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
552 if (written
< 0 && errno
== EAGAIN
)
554 else if (written
<= 0) {
555 /*TODO: do we really want to disconnect on a failure to write?*/
556 purple_connection_error(gc
, _("Could not write"));
560 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
563 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
565 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
567 PurpleConnection
*gc
= data
;
568 struct sipe_account_data
*sip
;
569 struct sip_connection
*conn
;
571 if (!PURPLE_CONNECTION_IS_VALID(gc
))
579 purple_connection_error(gc
, _("Could not connect"));
583 sip
= gc
->proto_data
;
585 sip
->connecting
= FALSE
;
586 sip
->last_keepalive
= time(NULL
);
588 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
590 /* If there is more to write now, we need to register a handler */
591 if (sip
->txbuf
->bufused
> 0)
592 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
594 conn
= connection_create(sip
, source
);
595 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
598 static struct sipe_account_data
*sipe_setup_ssl(PurpleConnection
*gc
, PurpleSslConnection
*gsc
)
600 struct sipe_account_data
*sip
;
601 struct sip_connection
*conn
;
603 if (!PURPLE_CONNECTION_IS_VALID(gc
))
605 if (gsc
) purple_ssl_close(gsc
);
609 sip
= gc
->proto_data
;
612 sip
->listenport
= purple_network_get_port_from_fd(gsc
->fd
);
613 sip
->connecting
= FALSE
;
614 sip
->last_keepalive
= time(NULL
);
616 conn
= connection_create(sip
, gsc
->fd
);
618 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
623 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
625 PurpleConnection
*gc
= data
;
626 struct sipe_account_data
*sip
= sipe_setup_ssl(gc
, gsc
);
627 if (sip
== NULL
) return;
629 sipe_canwrite_cb_ssl(gc
, gsc
->fd
, PURPLE_INPUT_WRITE
);
631 /* If there is more to write now */
632 if (sip
->txbuf
->bufused
> 0) {
633 sip
->tx_handler
= purple_input_add(gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
638 static void sendlater(PurpleConnection
*gc
, const char *buf
)
640 struct sipe_account_data
*sip
= gc
->proto_data
;
642 if (!sip
->connecting
) {
643 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
644 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
645 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
647 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
648 purple_connection_error(gc
, _("Couldn't create socket"));
651 sip
->connecting
= TRUE
;
654 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
655 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
657 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
660 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
662 struct sipe_account_data
*sip
= gc
->proto_data
;
663 time_t currtime
= time(NULL
);
664 int writelen
= strlen(buf
);
666 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
667 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
668 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
669 purple_debug_info("sipe", "could not send packet\n");
678 if (sip
->tx_handler
) {
683 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
685 ret
= write(sip
->fd
, buf
, writelen
);
689 if (ret
< 0 && errno
== EAGAIN
)
691 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
696 if (ret
< writelen
) {
697 if (!sip
->tx_handler
){
699 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
702 sip
->tx_handler
= purple_input_add(sip
->fd
,
703 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
708 /* XXX: is it OK to do this? You might get part of a request sent
709 with part of another. */
710 if (sip
->txbuf
->bufused
> 0)
711 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
713 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
719 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
721 sendout_pkt(gc
, buf
);
725 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
727 GSList
*tmp
= msg
->headers
;
730 GString
*outstr
= g_string_new("");
731 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
733 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
734 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
735 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
736 tmp
= g_slist_next(tmp
);
738 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
739 sendout_pkt(sip
->gc
, outstr
->str
);
740 g_string_free(outstr
, TRUE
);
743 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
746 if (sip
->registrar
.ntlm_key
) {
747 struct sipmsg_breakdown msgbd
;
749 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
750 // TODO generate this
751 msgbd
.rand
= g_strdup("0878F41B");
752 sip
->registrar
.ntlm_num
++;
753 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
754 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
755 if (signature_input_str
!= NULL
) {
756 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
757 msg
->rand
= g_strdup(msgbd
.rand
);
758 msg
->num
= g_strdup(msgbd
.num
);
760 sipmsg_breakdown_free(&msgbd
);
763 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
764 buf
= auth_header(sip
, &sip
->registrar
, msg
);
765 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
766 sipmsg_add_header(msg
, "Authorization", buf
);
768 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
771 } 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")) {
773 sip
->registrar
.type
=2;
775 buf
= auth_header(sip
, &sip
->registrar
, msg
);
776 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
779 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
783 static char *get_contact(struct sipe_account_data
*sip
)
785 return g_strdup(sip
->contact
);
789 static char *get_contact_service(struct sipe_account_data
*sip
)
791 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()));
792 //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);
795 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
796 const char *text
, const char *body
)
800 GString
*outstr
= g_string_new("");
801 struct sipe_account_data
*sip
= gc
->proto_data
;
804 contact
= get_contact(sip
);
805 sipmsg_remove_header(msg
, "Contact");
806 sipmsg_add_header(msg
, "Contact", contact
);
809 /* When sending the acknowlegements and errors, the content length from the original
810 message is still here, but there is no body; we need to make sure we're sending the
811 correct content length */
812 sipmsg_remove_header(msg
, "Content-Length");
815 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
816 sipmsg_add_header(msg
, "Content-Length", len
);
818 sipmsg_add_header(msg
, "Content-Length", "0");
821 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
822 //gchar * mic = "MICTODO";
823 msg
->response
= code
;
825 sipmsg_remove_header(msg
, "Authentication-Info");
826 sign_outgoing_message(msg
, sip
, msg
->method
);
828 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
829 GSList
*tmp
= msg
->headers
;
831 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
832 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
834 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
835 tmp
= g_slist_next(tmp
);
837 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
838 sendout_pkt(gc
, outstr
->str
);
839 g_string_free(outstr
, TRUE
);
842 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
844 if (trans
->msg
) sipmsg_free(trans
->msg
);
845 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
849 static struct transaction
*
850 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
852 struct transaction
*trans
= g_new0(struct transaction
, 1);
853 trans
->time
= time(NULL
);
854 trans
->msg
= (struct sipmsg
*)msg
;
855 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
856 trans
->callback
= callback
;
857 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
861 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
863 struct transaction
*trans
;
864 GSList
*transactions
= sip
->transactions
;
865 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
867 while (transactions
) {
868 trans
= transactions
->data
;
869 if (!strcmp(trans
->cseq
, cseq
)) {
872 transactions
= transactions
->next
;
878 static struct transaction
*
879 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
880 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
881 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
883 struct sipe_account_data
*sip
= gc
->proto_data
;
884 const char *addh
= "";
888 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
889 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
890 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
891 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
892 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
893 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
894 gchar
*route
= strdup("");
896 if (dialog
&& dialog
->routes
)
898 GSList
*iter
= dialog
->routes
;
903 route
= g_strdup_printf("%sRoute: <%s>\r\n", route
, iter
->data
);
905 iter
= g_slist_next(iter
);
909 if (!ourtag
&& !dialog
) {
913 if (!strcmp(method
, "REGISTER")) {
914 if (sip
->regcallid
) {
916 callid
= g_strdup(sip
->regcallid
);
918 sip
->regcallid
= g_strdup(callid
);
922 if (addheaders
) addh
= addheaders
;
924 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
925 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
926 "From: <sip:%s>%s%s;epid=%s\r\n"
927 "To: <%s>%s%s%s%s\r\n"
928 "Max-Forwards: 70\r\n"
933 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
935 dialog
&& dialog
->request
? dialog
->request
: url
,
936 TRANSPORT_DESCRIPTOR
,
937 purple_network_get_my_ip(-1),
939 branch
? ";branch=" : "",
940 branch
? branch
: "",
942 ourtag
? ";tag=" : "",
943 ourtag
? ourtag
: "",
944 get_epid(), // TODO generate one per account/login
946 theirtag
? ";tag=" : "",
947 theirtag
? theirtag
: "",
948 theirepid
? ";epid=" : "",
949 theirepid
? theirepid
: "",
950 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
956 body
? strlen(body
) : 0,
960 //printf ("parsing msg buf:\n%s\n\n", buf);
961 msg
= sipmsg_parse_msg(buf
);
971 sign_outgoing_message (msg
, sip
, method
);
973 buf
= sipmsg_to_string (msg
);
975 /* add to ongoing transactions */
976 struct transaction
* trans
= transactions_add_buf(sip
, msg
, tc
);
977 sendout_pkt(gc
, buf
);
982 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
984 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
985 gchar
*contact
= get_contact(sip
);
986 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
987 "Content-Type: application/SOAP+xml\r\n",contact
);
989 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
990 tr
->payload
= payload
;
996 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
998 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
1001 static char *get_contact_register(struct sipe_account_data
*sip
)
1003 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()));
1006 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
1008 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
1009 char *to
= g_strdup_printf("sip:%s", sip
->username
);
1010 char *contact
= get_contact_register(sip
);
1011 char *hdr
= g_strdup_printf("Contact: %s\r\n"
1012 "Supported: gruu-10, adhoclist\r\n"
1013 "Event: registration\r\n"
1014 "Allow-Events: presence\r\n"
1015 "ms-keep-alive: UAC;hop-hop=yes\r\n"
1016 "Expires: %d\r\n", contact
,expire
);
1019 sip
->registerstatus
= 1;
1022 sip
->reregister
= time(NULL
) + expire
- 50;
1024 sip
->reregister
= time(NULL
) + 600;
1027 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1028 process_register_response
);
1035 static void do_register(struct sipe_account_data
*sip
)
1037 do_register_exp(sip
, sip
->registerexpire
);
1040 static gchar
*parse_from(const gchar
*hdr
)
1043 const gchar
*tmp
, *tmp2
= hdr
;
1045 if (!hdr
) return NULL
;
1046 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1047 tmp
= strchr(hdr
, '<');
1049 /* i hate the different SIP UA behaviours... */
1050 if (tmp
) { /* sip address in <...> */
1052 tmp
= strchr(tmp2
, '>');
1054 from
= g_strndup(tmp2
, tmp
- tmp2
);
1056 purple_debug_info("sipe", "found < without > in From\n");
1060 tmp
= strchr(tmp2
, ';');
1062 from
= g_strndup(tmp2
, tmp
- tmp2
);
1064 from
= g_strdup(tmp2
);
1067 purple_debug_info("sipe", "got %s\n", from
);
1071 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1077 va_start(args
, parent
);
1078 while ((name
= va_arg(args
, const char *)) != NULL
) {
1079 node
= xmlnode_get_child(parent
, name
);
1080 if (node
== NULL
) return NULL
;
1089 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1091 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1092 if (!purple_group
) {
1093 purple_group
= purple_group_new(group
->name
);
1094 purple_blist_add_group(purple_group
, NULL
);
1098 group
->purple_group
= purple_group
;
1099 sip
->groups
= g_slist_append(sip
->groups
, group
);
1100 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1102 purple_debug_info("sipe", "did not add group %s\n", group
->name
);
1106 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1112 struct sipe_group
*group
;
1113 GSList
*entry
= sip
->groups
;
1115 group
= entry
->data
;
1116 if (group
->id
== id
) {
1119 entry
= entry
->next
;
1124 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1130 struct sipe_group
*group
;
1131 GSList
*entry
= sip
->groups
;
1133 group
= entry
->data
;
1134 if (!strcmp(group
->name
, name
)) {
1137 entry
= entry
->next
;
1143 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1145 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1146 gchar
* body
= g_strdup_printf(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->delta_num
++);
1147 send_soap_request(sip
, body
);
1149 g_free(group
->name
);
1150 group
->name
= g_strdup(name
);
1154 sipe_group_set_user (struct sipe_account_data
*sip
, struct sipe_group
* group
, const gchar
* who
)
1156 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1157 PurpleBuddy
* purple_buddy
= purple_find_buddy (sip
->account
, who
);
1160 group
= sipe_group_find_by_id (sip
, buddy
->group_id
);
1162 buddy
->group_id
= group
? group
->id
: 1;
1164 if (buddy
&& purple_buddy
) {
1165 gchar
* alias
= (gchar
*)purple_buddy_get_alias(purple_buddy
);
1166 purple_debug_info("sipe", "Saving buddy %s with alias %s and group_id %d\n", who
, alias
, buddy
->group_id
);
1167 gchar
* body
= g_strdup_printf(SIPE_SOAP_SET_CONTACT
,
1168 alias
, buddy
->group_id
, "true", buddy
->name
, sip
->delta_num
++
1170 send_soap_request(sip
, body
);
1175 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1177 if (msg
->response
== 200) {
1178 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1180 struct group_user_context
* ctx
= (struct group_user_context
*)tc
->payload
;
1181 group
->name
= ctx
->group_name
;
1183 xmlnode
* xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1184 if (!xml
) return FALSE
;
1186 xmlnode
* node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1187 if (!node
) return FALSE
;
1189 char * group_id
= xmlnode_get_data(node
);
1190 if (!group_id
) return FALSE
;
1192 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1194 sipe_group_add(sip
, group
);
1195 sipe_group_set_user(sip
, group
, ctx
->user_name
);
1204 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1206 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1207 ctx
->group_name
= g_strdup(name
);
1208 ctx
->user_name
= g_strdup(who
);
1210 gchar
* body
= g_strdup_printf(SIPE_SOAP_ADD_GROUP
, name
, sip
->delta_num
++);
1211 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1215 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1219 if (msg
->response
== 200 || msg
->response
== 202) {
1223 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* cant be NULL since it is our own msg */
1225 /* we can not subscribe -> user is offline (TODO unknown status?) */
1227 purple_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
1232 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
)
1234 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1235 gchar
*tmp
= get_contact(sip
);
1236 gchar
*contact
= g_strdup_printf(
1237 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1238 "Event: presence\r\n"
1239 "Contact: %s\r\n", tmp
);
1242 /* subscribe to buddy presence */
1243 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
, process_subscribe_response
);
1249 static void sipe_subscribe(struct sipe_account_data
*sip
, struct sipe_buddy
*buddy
)
1251 sipe_subscribe_to_name(sip
, buddy
->name
);
1253 /* resubscribe before subscription expires */
1254 /* add some jitter */
1255 buddy
->resubscribe
= time(NULL
)+1140+(rand()%50);
1258 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1260 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(purple_status_get_type(status
));
1261 struct sipe_account_data
*sip
= NULL
;
1263 if (!purple_status_is_active(status
))
1267 sip
= account
->gc
->proto_data
;
1270 g_free(sip
->status
);
1272 if (primitive
== PURPLE_STATUS_AWAY
) {
1273 sip
->status
= g_strdup("away");
1274 } else if (primitive
== PURPLE_STATUS_AVAILABLE
) {
1275 sip
->status
= g_strdup("available");
1276 } else if (primitive
== PURPLE_STATUS_UNAVAILABLE
) {
1277 sip
->status
= g_strdup("busy");
1280 send_presence_info(sip
);
1285 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1287 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1288 sipe_group_set_user(sip
, NULL
, name
);
1292 sipe_group_buddy(PurpleConnection
*gc
,
1294 const char *old_group_name
,
1295 const char *new_group_name
)
1297 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1298 struct sipe_group
* group
= sipe_group_find_by_name(sip
, g_strdup(new_group_name
));
1300 sipe_group_create(sip
, g_strdup(new_group_name
), g_strdup(who
));
1302 sipe_group_set_user(sip
, group
, who
);
1306 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1308 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1309 struct sipe_buddy
*b
;
1311 // Prepend sip: if needed
1312 if (strncmp("sip:", buddy
->name
, 4)) {
1313 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1314 purple_blist_rename_buddy(buddy
, buf
);
1318 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1319 b
= g_new0(struct sipe_buddy
, 1);
1320 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1321 b
->name
= g_strdup(buddy
->name
);
1322 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1324 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1326 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1330 // Not Used; Remove?
1331 /*static void sipe_get_buddies(PurpleConnection *gc)
1333 PurpleBlistNode *gnode, *cnode, *bnode;
1335 purple_debug_info("sipe", "sipe_get_buddies\n");
1337 for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
1338 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
1339 for (cnode = gnode->child; cnode; cnode = cnode->next) {
1340 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue;
1341 for (bnode = cnode->child; bnode; bnode = bnode->next) {
1342 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue;
1343 if (((PurpleBuddy*)bnode)->account == gc->account)
1344 sipe_add_buddy(gc, (PurpleBuddy*)bnode, (PurpleGroup *)gnode);
1350 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1352 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1353 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1356 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1359 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->delta_num
++);
1360 send_soap_request(sip
, body
);
1369 sipe_rename_group(PurpleConnection
*gc
,
1370 const char *old_name
,
1372 GList
*moved_buddies
)
1374 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1375 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, g_strdup(old_name
));
1377 sipe_group_rename(sip
, s_group
, group
->name
);
1379 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1384 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1386 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1387 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1389 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1390 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->delta_num
++);
1391 send_soap_request(sip
, body
);
1394 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1395 g_free(s_group
->name
);
1397 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1401 static GList
*sipe_status_types(PurpleAccount
*acc
)
1403 PurpleStatusType
*type
;
1404 GList
*types
= NULL
;
1407 type
= purple_status_type_new_with_attrs(
1408 PURPLE_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1409 // Translators: noun
1410 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1412 types
= g_list_append(types
, type
);
1415 type
= purple_status_type_new_with_attrs(
1416 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1417 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1419 types
= g_list_append(types
, type
);
1422 type
= purple_status_type_new_with_attrs(
1423 PURPLE_STATUS_UNAVAILABLE
, "busy", _("Busy"), TRUE
, TRUE
, FALSE
,
1424 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1426 types
= g_list_append(types
, type
);
1429 type
= purple_status_type_new_full(
1430 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1431 types
= g_list_append(types
, type
);
1436 static gboolean
sipe_add_lcs_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1438 int len
= msg
->bodylen
;
1440 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1441 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1445 /* Convert the contact from XML to Purple Buddies */
1446 xmlnode
* isc
= xmlnode_from_str(msg
->body
, len
);
1451 gchar
* delta_num
= g_strdup(xmlnode_get_attrib(isc
, "deltaNum"));
1453 sip
->delta_num
= (int)g_ascii_strtod(delta_num
, NULL
);
1457 xmlnode
*group_node
;
1458 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1459 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1461 group
->name
= g_strdup(xmlnode_get_attrib(group_node
, "name"));
1462 if (!strncmp(group
->name
, "~", 1)){
1464 group
->name
= "General";
1466 group
->name
= g_strdup(group
->name
);
1467 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1469 sipe_group_add(sip
, group
);
1472 // Make sure we have at least one group
1473 if (g_slist_length(sip
->groups
) == 0) {
1474 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1476 group
->name
= g_strdup("General");
1478 PurpleGroup
* purple_group
= purple_group_new(group
->name
);
1479 purple_blist_add_group(purple_group
, NULL
);
1480 sip
->groups
= g_slist_append(sip
->groups
, group
);
1483 /* Parse contacts */
1485 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1486 gchar
* uri
= g_strdup(xmlnode_get_attrib(item
, "uri"));
1487 gchar
* name
= g_strdup(xmlnode_get_attrib(item
, "name"));
1488 gchar
**item_groups
= g_strsplit(xmlnode_get_attrib(item
, "groups"), " ", 0);
1490 struct sipe_group
* group
= NULL
;
1492 // Find the first group this contact belongs to; that's where we'll place it in the buddy list
1493 if (item_groups
[0]) {
1494 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[0], NULL
));
1497 // If couldn't find the right group for this contact, just put them in the first group we have
1498 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1499 group
= sip
->groups
->data
;
1502 if (group
!= NULL
) {
1503 char * buddy_name
= g_strdup_printf("sip:%s", uri
);
1505 //b = purple_find_buddy(sip->account, buddy_name);
1506 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1508 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1512 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1514 if (name
!= NULL
&& strlen(name
) != 0) {
1515 purple_blist_alias_buddy(b
, name
);
1517 purple_blist_alias_buddy(b
, uri
);
1520 struct sipe_buddy
* buddy
= g_new0(struct sipe_buddy
, 1);
1521 buddy
->name
= g_strdup(b
->name
);
1522 buddy
->group_id
= group
->id
;
1523 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1525 purple_debug_info("sipe", "Added buddy %s to group %s\n", buddy
->name
, group
->name
);
1527 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
1537 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1539 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1540 gchar
*tmp
= get_contact(sip
);
1541 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-roaming-contacts\r\n"
1542 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
1543 "Supported: com.microsoft.autoextend\r\n"
1544 "Supported: ms-benotify\r\n"
1545 "Proxy-Require: ms-benotify\r\n"
1546 "Supported: ms-piggyback-first-notify\r\n"
1547 "Contact: %s\r\n", tmp
);
1550 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_add_lcs_contacts
);
1555 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1557 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1558 gchar
*tmp
= get_contact(sip
);
1559 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-roaming-self\r\n"
1560 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
1561 "Supported: com.microsoft.autoextend\r\n"
1562 "Supported: ms-benotify\r\n"
1563 "Proxy-Require: ms-benotify\r\n"
1564 "Supported: ms-piggyback-first-notify\r\n"
1566 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n"
1571 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>");
1573 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
1579 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1581 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1582 gchar
*tmp
= get_contact(sip
);
1583 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-provisioning-v2\r\n"
1584 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
1585 "Supported: com.microsoft.autoextend\r\n"
1586 "Supported: ms-benotify\r\n"
1587 "Proxy-Require: ms-benotify\r\n"
1588 "Supported: ms-piggyback-first-notify\r\n"
1591 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
1596 gchar
*body
=g_strdup("<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\"><provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/><provisioningGroup name=\"ucPolicy\"/></provisioningGroupList>");
1597 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
1603 /* IM Session (INVITE and MESSAGE methods) */
1605 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
1607 if (sip
== NULL
|| who
== NULL
) {
1611 struct sip_im_session
*session
;
1612 GSList
*entry
= sip
->im_sessions
;
1614 session
= entry
->data
;
1615 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
1618 entry
= entry
->next
;
1623 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
1625 struct sip_im_session
*session
= find_im_session(sip
, who
);
1627 session
= g_new0(struct sip_im_session
, 1);
1628 session
->with
= g_strdup(who
);
1629 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
1634 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1636 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
1637 // TODO free session resources
1640 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
1646 if (strncmp("sip:", session
->with
, 4)) {
1647 fullto
= g_strdup_printf("sip:%s", session
->with
);
1649 fullto
= g_strdup(session
->with
);
1652 hdr
= g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1653 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1654 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1656 tmp
= get_contact(sip
);
1657 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1660 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msg
, session
->dialog
, NULL
);
1668 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
1670 GSList
*entry
= session
->outgoing_message_queue
;
1672 char *queued_msg
= entry
->data
;
1673 sipe_send_message(sip
, session
, queued_msg
);
1675 // Remove from the queue and free the string
1676 entry
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
1682 sipe_get_route_header(struct sipmsg
*msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
1684 GSList
*hdr
= msg
->headers
;
1685 struct siphdrelement
*elem
;
1691 if(!strcmp(elem
->name
, "Record-Route"))
1693 gchar
*route
= sipmsg_find_part_of_header(elem
->value
, "<", ">", NULL
);
1694 dialog
->routes
= g_slist_append(dialog
->routes
, route
);
1696 hdr
= g_slist_next(hdr
);
1701 dialog
->routes
= g_slist_reverse(dialog
->routes
);
1706 dialog
->request
= dialog
->routes
->data
;
1707 dialog
->routes
= g_slist_remove(dialog
->routes
, dialog
->routes
->data
);
1710 contact
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "<", ">", NULL
);
1711 dialog
->routes
= g_slist_append(dialog
->routes
, contact
);
1716 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
1718 gchar
*us
= outgoing
? "From" : "To";
1719 gchar
*them
= outgoing
? "To" : "From";
1721 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
1722 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
1723 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
1724 if (!dialog
->theirepid
) {
1725 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
1727 if (!dialog
->theirepid
) {
1728 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
1731 sipe_get_route_header(msg
, dialog
, outgoing
);
1736 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1738 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
1739 struct sip_im_session
* session
= find_im_session(sip
, with
);
1743 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
1747 if (msg
->response
!= 200) {
1748 purple_debug_info("sipe", "process_invite_response: INVITE response not 200, ignoring\n");
1749 im_session_destroy(sip
, session
);
1753 struct sip_dialog
* dialog
= session
->dialog
;
1755 purple_debug_info("sipe", "process_invite_response: session outgoign dialog is NULL\n");
1759 sipe_parse_dialog(msg
, dialog
, TRUE
);
1762 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
1763 session
->outgoing_invite
= NULL
;
1764 sipe_im_process_queue(sip
, session
);
1770 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
* session
, gchar
* msg_body
)
1777 if (session
->dialog
) {
1778 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
1782 session
->dialog
= g_new0(struct sip_dialog
, 1);
1784 if (strstr(session
->with
, "sip:")) {
1785 to
= g_strdup(session
->with
);
1787 to
= g_strdup_printf("sip:%s", session
->with
);
1790 char * base64_msg
= purple_base64_encode((guchar
*) msg_body
, strlen(msg_body
));
1791 char * ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, base64_msg
);
1794 contact
= get_contact(sip
);
1795 hdr
= g_strdup_printf(
1797 "Content-Type: application/sdp\r\n",
1798 contact
, ms_text_format
, sip
->username
, sip
->username
, to
);
1799 g_free(ms_text_format
);
1801 body
= g_strdup_printf(
1803 "o=- 0 0 IN IP4 %s\r\n"
1807 "m=message %d sip null\r\n"
1808 "a=accept-types:text/plain text/html image/gif "
1809 "multipart/alternative application/im-iscomposing+xml\r\n",
1810 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
1812 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
1813 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
1822 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1825 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
1826 im_session_destroy(sip
, session
);
1831 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
1833 struct sipe_account_data
*sip
= gc
->proto_data
;
1835 purple_debug_info("sipe", "conversation with %s closed\n", who
);
1836 im_session_close(sip
, find_im_session(sip
, who
));
1840 im_session_close_all (struct sipe_account_data
*sip
)
1842 GSList
*entry
= sip
->im_sessions
;
1844 im_session_close (sip
, entry
->data
);
1845 entry
= sip
->im_sessions
;
1849 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
1851 struct sipe_account_data
*sip
= gc
->proto_data
;
1852 char *to
= g_strdup(who
);
1853 char *text
= purple_unescape_html(what
);
1855 struct sip_im_session
* session
= find_or_create_im_session(sip
, who
);
1857 // Queue the message
1858 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, text
);
1860 if (session
->dialog
&& session
->dialog
->callid
) {
1861 sipe_im_process_queue(sip
, session
);
1862 } else if (!session
->outgoing_invite
) {
1863 // Need to send the INVITE to get the outgoing dialog setup
1864 sipe_invite(sip
, session
, text
);
1872 /* End IM Session (INVITE and MESSAGE methods) */
1875 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
1877 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1879 if (state
== PURPLE_NOT_TYPING
)
1882 struct sip_im_session
* session
= find_im_session(sip
, who
);
1884 if (session
&& session
->dialog
) {
1885 send_sip_request(gc
, "INFO", who
, who
,
1886 "Content-Type: application/xml\r\n",
1887 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
1890 return SIPE_TYPING_SEND_TIMEOUT
;
1894 static void sipe_buddy_resub(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1896 time_t curtime
= time(NULL
);
1897 if (buddy
->resubscribe
< curtime
) {
1898 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_buddy_resub %s\n", name
);
1899 sipe_subscribe(sip
, buddy
);
1903 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
1905 GSList
*tmp
= sip
->transactions
;
1906 time_t currtime
= time(NULL
);
1908 struct transaction
*trans
= tmp
->data
;
1910 purple_debug_info("sipe", "have open transaction age: %ld\n", currtime
-trans
->time
);
1911 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
1914 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
1916 sendout_sipmsg(sip
, trans
->msg
);
1923 static gboolean
subscribe_timeout(struct sipe_account_data
*sip
)
1926 time_t curtime
= time(NULL
);
1927 /* register again if first registration expires */
1928 if (sip
->reregister
< curtime
) {
1931 /* check for every subscription if we need to resubscribe */
1932 //Fixxxer we need resub?
1933 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_resub
, (gpointer
)sip
);
1935 /* remove a timed out suscriber */
1939 struct sipe_watcher
*watcher
= tmp
->data
;
1940 if (watcher
->expire
< curtime
) {
1941 watcher_remove(sip
, watcher
->name
);
1944 if (tmp
) tmp
= tmp
->next
;
1950 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1954 gboolean found
= FALSE
;
1956 from
= parse_from(sipmsg_find_header(msg
, "From"));
1960 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
1962 contenttype
= sipmsg_find_header(msg
, "Content-Type");
1963 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
1964 serv_got_im(sip
->gc
, from
, msg
->body
, 0, time(NULL
));
1965 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1968 if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
1969 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1974 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1978 state
= xmlnode_get_child(isc
, "state");
1981 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1986 statedata
= xmlnode_get_data(state
);
1988 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
1989 else serv_got_typing_stopped(sip
->gc
, from
);
1994 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1998 purple_debug_info("sipe", "got unknown mime-type");
1999 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
2004 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2006 // Only accept text invitations
2007 if (msg
->body
&& !strstr(msg
->body
, "m=message")) {
2008 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2012 gchar
* from
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "<", ">", NULL
);
2013 struct sip_im_session
* session
= find_or_create_im_session (sip
, from
);
2015 if (session
->dialog
) {
2016 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
2018 session
->dialog
= g_new0(struct sip_dialog
, 1);
2020 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
2022 session
->dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
2023 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
2024 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
2025 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
2028 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
2032 send_sip_response(sip
->gc
, msg
, 200, "OK", g_strdup_printf(
2034 "o=- 0 0 IN IP4 %s\r\n"
2038 "m=message %d sip sip:%s\r\n"
2039 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
2040 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
2041 sip
->realport
, sip
->username
));
2044 static void sipe_connection_cleanup(struct sipe_account_data
*);
2045 static void create_connection(struct sipe_account_data
*, gchar
*, int);
2047 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2049 gchar
*tmp
, krb5_token
;
2050 const gchar
*expires_header
;
2053 expires_header
= sipmsg_find_header(msg
, "Expires");
2054 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
2055 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
2057 switch (msg
->response
) {
2060 sip
->registerstatus
= 0;
2062 sip
->registerstatus
= 3;
2063 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
2065 gchar
*gruu
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "gruu=\"", "\"", NULL
);
2067 sip
->contact
= g_strdup_printf("<%s>", gruu
);
2069 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
);
2072 /* get buddies from blist; Has a bug */
2073 subscribe_timeout(sip
);
2075 tmp
= sipmsg_find_header(msg
, "Allow-Events");
2076 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
2077 sipe_subscribe_buddylist(sip
, msg
);
2080 tmp
= sipmsg_find_header(msg
, "ms-keep-alive");
2082 sipe_keep_alive_timeout(sip
, tmp
);
2085 sipe_subscribe_roaming_self(sip
, msg
);
2086 sipe_subscribe_roaming_provisioning(sip
, msg
);
2087 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
2089 // Should we remove the transaction here?
2090 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
2091 transactions_remove(sip
, tc
);
2096 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
2098 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
2099 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
2103 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
2106 tmp
= g_strsplit(parts
[0], ":", 0);
2107 hostname
= g_strdup(tmp
[0]);
2108 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
2112 tmp
= g_strsplit(parts
[i
], "=", 0);
2114 if (g_strcasecmp("transport", tmp
[0]) == 0) {
2115 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
2116 transport
= SIPE_TRANSPORT_TCP
;
2117 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
2118 transport
= SIPE_TRANSPORT_UDP
;
2127 /* Close old connection */
2128 sipe_connection_cleanup(sip
);
2130 /* Create new connection */
2131 sip
->transport
= transport
;
2132 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
2133 hostname
, port
, TRANSPORT_DESCRIPTOR
);
2134 create_connection(sip
, hostname
, port
);
2139 if (sip
->registerstatus
!= 2) {
2140 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
2141 if (sip
->registrar
.retries
> 3) {
2142 sip
->gc
->wants_to_die
= TRUE
;
2143 purple_connection_error(sip
->gc
, _("Wrong Password"));
2146 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2147 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
2149 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2151 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
2152 fill_auth(sip
, tmp
, &sip
->registrar
);
2153 sip
->registerstatus
= 2;
2154 if (sip
->account
->disconnecting
) {
2155 do_register_exp(sip
, 0);
2163 const gchar
*warning
= sipmsg_find_header(msg
, "Warning");
2164 if (warning
!= NULL
) {
2166 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
2168 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
2169 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), tmp
[1] ? tmp
[1] : _("no reason given"));
2172 warning
= _("You have been rejected by the server");
2175 sip
->gc
->wants_to_die
= TRUE
;
2176 purple_connection_error(sip
->gc
, warning
);
2182 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2183 if (warning
!= NULL
) {
2184 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2185 warning
= g_strdup_printf(_("Not Found: %s. Please, contact with your Administrator"), reason
? reason
: _("no reason given"));
2188 warning
= _("Not Found: Destination URI either not enabled for SIP or does not exist. Please, contact with your Administrator");
2191 sip
->gc
->wants_to_die
= TRUE
;
2192 purple_connection_error(sip
->gc
, warning
);
2198 const gchar
*warning
= sipmsg_find_header(msg
, "ms-diagnostics");
2199 if (warning
!= NULL
) {
2200 gchar
*reason
= sipmsg_find_part_of_header(warning
, "reason=\"", "\"", NULL
);
2201 warning
= g_strdup_printf(_("Service unavailable: %s"), reason
? reason
: _("no reason given"));
2204 warning
= _("Service unavailable: no reason given");
2207 sip
->gc
->wants_to_die
= TRUE
;
2208 purple_connection_error(sip
->gc
, warning
);
2216 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2221 gchar
*activity
= g_strdup("available");
2223 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
2224 gboolean isonline
= FALSE
;
2226 fromhdr
= sipmsg_find_header(msg
, "From");
2227 from
= parse_from(fromhdr
);
2233 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2235 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
2239 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
2240 if ((status
= xmlnode_get_child(tuple
, "status"))) {
2241 basicstatus
= xmlnode_get_child(status
, "basic");
2246 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
2251 tmp2
= xmlnode_get_data(basicstatus
);
2253 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
2258 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", tmp2
);
2259 if (strstr(tmp2
, "open")) {
2263 xmlnode
*display_name_node
= xmlnode_get_child(pidf
, "display-name");
2264 if (display_name_node
) {
2265 PurpleBuddy
* buddy
= purple_find_buddy (sip
->account
, from
);
2266 char * display_name
= xmlnode_get_data(display_name_node
);
2267 if (buddy
&& display_name
) {
2268 purple_blist_server_alias_buddy (buddy
, g_strdup(display_name
));
2272 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
2273 if ((status
= xmlnode_get_child(tuple
, "status"))) {
2274 if (basicstatus
= xmlnode_get_child(status
, "activities")) {
2275 if (basicstatus
= xmlnode_get_child(basicstatus
, "activity")) {
2276 activity
= xmlnode_get_data(basicstatus
);
2281 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
2284 gchar
* status_id
= NULL
;
2286 if (strstr(activity
, "busy")) {
2288 } else if (strstr(activity
, "away")) {
2294 status_id
= "available";
2297 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
2298 purple_prpl_got_user_status(sip
->account
, from
, status_id
, NULL
);
2300 purple_prpl_got_user_status(sip
->account
, from
, "offline", NULL
);
2308 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2311 static void process_incoming_benotify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2313 xmlnode
*xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2316 gchar
* delta_num
= g_strdup(xmlnode_get_attrib(xml
, "deltaNum"));
2318 sip
->delta_num
= (int)g_ascii_strtod(delta_num
, NULL
);
2325 static gchar
* gen_xpidf(struct sipe_account_data
*sip
)
2327 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
2329 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
2330 "<display name=\"sip:%s\"/>\r\n"
2331 "<atom id=\"1234\">\r\n"
2332 "<address uri=\"sip:%s\">\r\n"
2333 "<status status=\"%s\"/>\r\n"
2346 static gchar
* gen_pidf(struct sipe_account_data
*sip
)
2348 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
2349 "<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"
2350 "<tuple id=\"0\">\r\n"
2352 "<basic>open</basic>\r\n"
2353 "<ep:activities>\r\n"
2354 " <ep:activity>%s</ep:activity>\r\n"
2358 "<ci:display-name>%s</ci:display-name>\r\n"
2366 static void send_clear_notes(struct sipe_account_data
*sip
)
2371 process_send_presence_info_v0_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2373 if (msg
->response
== 488) {
2374 sip
->presence_method_version
= 1;
2375 send_presence_info(sip
);
2380 static void send_presence_info_v0(struct sipe_account_data
*sip
, char * note
)
2383 if (!strcmp(sip
->status
, "away")) {
2385 } else if (!strcmp(sip
->status
, "busy")) {
2392 gchar
*name
= g_strdup_printf("sip: sip:%s", sip
->username
);
2393 gchar
* body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
, name
, 200, code
, note
);
2394 send_soap_request_with_cb(sip
, body
, process_send_presence_info_v0_response
, NULL
);
2400 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2402 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
2403 if (msg
->response
== 200) {
2404 sip
->status_version
= 0;
2405 send_presence_info(sip
);
2411 process_send_presence_info_v1_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2413 if (msg
->response
== 409) {
2414 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
2415 // TODO need to parse the version #'s?
2416 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
2417 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
2419 gchar
*tmp
= get_contact(sip
);
2420 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
2421 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
2423 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
2433 static void send_presence_info_v1(struct sipe_account_data
*sip
, char * note
)
2436 if (!strcmp(sip
->status
, "away")) {
2438 } else if (!strcmp(sip
->status
, "busy")) {
2445 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
2446 gchar
*doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
2447 sip
->status_version
, code
,
2448 sip
->status_version
, code
,
2449 sip
->status_version
, note
? note
: "",
2450 sip
->status_version
, note
? note
: "",
2451 sip
->status_version
, note
? note
: ""
2453 sip
->status_version
++;
2455 gchar
*tmp
= get_contact(sip
);
2456 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
2457 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
2459 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_info_v1_response
);
2467 static void send_presence_info(struct sipe_account_data
*sip
)
2469 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
2470 if (!status
) return;
2472 gchar
*note
= g_strdup(purple_status_get_attr_string(status
, "message"));
2474 purple_debug_info("sipe", "sending presence info, version = %d\n", sip
->presence_method_version
);
2475 if (sip
->presence_method_version
!= 1) {
2476 send_presence_info_v0(sip
, note
);
2478 send_presence_info_v1(sip
, note
);
2482 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2484 gboolean found
= FALSE
;
2485 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
2486 if (msg
->response
== 0) { /* request */
2487 if (!strcmp(msg
->method
, "MESSAGE")) {
2488 process_incoming_message(sip
, msg
);
2490 } else if (!strcmp(msg
->method
, "NOTIFY")) {
2491 purple_debug_info("sipe","send->process_incoming_notify\n");
2492 process_incoming_notify(sip
, msg
);
2494 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
2495 purple_debug_info("sipe","send->process_incoming_benotify\n");
2496 process_incoming_benotify(sip
, msg
);
2498 } else if (!strcmp(msg
->method
, "INVITE")) {
2499 process_incoming_invite(sip
, msg
);
2501 } else if (!strcmp(msg
->method
, "INFO")) {
2503 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
2505 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
2507 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2509 } else if (!strcmp(msg
->method
, "ACK")) {
2510 // ACK's don't need any response
2512 } else if (!strcmp(msg
->method
, "BYE")) {
2513 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2515 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
2516 struct sip_im_session
* session
= find_im_session (sip
, from
);
2520 // TODO Let the user know the other user left the conversation?
2521 im_session_destroy(sip
, session
);
2526 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2528 } else { /* response */
2529 struct transaction
*trans
= transactions_find(sip
, msg
);
2531 if (msg
->response
== 407) {
2532 gchar
*resend
, *auth
, *ptmp
;
2534 if (sip
->proxy
.retries
> 30) return;
2535 sip
->proxy
.retries
++;
2536 /* do proxy authentication */
2538 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
2540 fill_auth(sip
, ptmp
, &sip
->proxy
);
2541 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
);
2542 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
2543 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
2545 resend
= sipmsg_to_string(trans
->msg
);
2546 /* resend request */
2547 sendout_pkt(sip
->gc
, resend
);
2550 if (msg
->response
== 100 || msg
->response
== 180) {
2551 /* ignore provisional response */
2552 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
2554 sip
->proxy
.retries
= 0;
2555 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
2556 if (msg
->response
== 401) sip
->registrar
.retries
++;
2557 else sip
->registrar
.retries
= 0;
2558 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
2560 if (msg
->response
== 401) {
2561 gchar
*resend
, *auth
, *ptmp
;
2563 if (sip
->registrar
.retries
> 4) return;
2564 sip
->registrar
.retries
++;
2566 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2567 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
2569 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2572 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
2574 fill_auth(sip
, ptmp
, &sip
->registrar
);
2575 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
);
2576 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
2577 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
2579 //sipmsg_remove_header(trans->msg, "Authorization");
2580 //sipmsg_add_header(trans->msg, "Authorization", auth);
2582 resend
= sipmsg_to_string(trans
->msg
);
2583 /* resend request */
2584 sendout_pkt(sip
->gc
, resend
);
2589 if (trans
->callback
) {
2590 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
2591 /* call the callback to process response*/
2592 (trans
->callback
)(sip
, msg
, trans
);
2594 /* Not sure if this is needed or what needs to be done
2595 but transactions seem to be removed prematurely so
2596 this only removes them if the response is 200 OK */
2597 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
2598 /*Has a bug and it's unneccesary*/
2599 /*transactions_remove(sip, trans);*/
2605 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
2609 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
2613 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
2621 /* according to the RFC remove CRLF at the beginning */
2622 while (*cur
== '\r' || *cur
== '\n') {
2625 if (cur
!= conn
->inbuf
) {
2626 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
2627 conn
->inbufused
= strlen(conn
->inbuf
);
2630 /* Received a full Header? */
2631 while ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
) {
2632 time_t currtime
= time(NULL
);
2635 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
2636 msg
= sipmsg_parse_header(conn
->inbuf
);
2639 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
2640 if (restlen
>= msg
->bodylen
) {
2641 dummy
= g_malloc(msg
->bodylen
+ 1);
2642 memcpy(dummy
, cur
, msg
->bodylen
);
2643 dummy
[msg
->bodylen
] = '\0';
2645 cur
+= msg
->bodylen
;
2646 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
2647 conn
->inbufused
= strlen(conn
->inbuf
);
2649 purple_debug_info("sipe", "process_input: body too short (%d < %d, strlen %d) - ignoring message\n",
2650 restlen
, msg
->bodylen
, strlen(conn
->inbuf
));
2656 purple_debug_info("sipe", "body:\n%s", msg
->body
);
2659 // Verify the signature before processing it
2660 if (sip
->registrar
.ntlm_key
) {
2661 struct sipmsg_breakdown msgbd
;
2663 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
2664 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
2666 if (signature_input_str
!= NULL
) {
2667 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
2670 gchar
* rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
2672 if (signature
!= NULL
) {
2673 if (rspauth
!= NULL
) {
2674 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
2675 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
2676 process_input_message(sip
, msg
);
2678 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
2679 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
2680 sip
->gc
->wants_to_die
= TRUE
;
2682 } else if (msg
->response
== 401) {
2683 purple_connection_error(sip
->gc
, _("Wrong Password"));
2684 sip
->gc
->wants_to_die
= TRUE
;
2688 sipmsg_breakdown_free(&msgbd
);
2690 process_input_message(sip
, msg
);
2695 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
2697 PurpleConnection
*gc
= data
;
2698 struct sipe_account_data
*sip
= gc
->proto_data
;
2703 static char buffer
[65536];
2704 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
2706 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
2707 msg
= sipmsg_parse_msg(buffer
);
2708 if (msg
) process_input_message(sip
, msg
);
2712 static void sipe_invalidate_ssl_connection(PurpleConnection
*gc
, const char *msg
, const char *debug
)
2714 struct sipe_account_data
*sip
= gc
->proto_data
;
2715 PurpleSslConnection
*gsc
= sip
->gsc
;
2717 purple_debug_error("sipe", debug
);
2718 purple_connection_error(gc
, msg
);
2720 /* Invalidate this connection. Next send will open a new one */
2721 connection_remove(sip
, gsc
->fd
);
2722 purple_ssl_close(gsc
);
2727 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2729 PurpleConnection
*gc
= data
;
2730 struct sipe_account_data
*sip
;
2731 struct sip_connection
*conn
;
2733 gboolean firstread
= TRUE
;
2735 /* NOTE: This check *IS* necessary */
2736 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
2737 purple_ssl_close(gsc
);
2741 sip
= gc
->proto_data
;
2742 conn
= connection_find(sip
, gsc
->fd
);
2744 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
2745 gc
->wants_to_die
= TRUE
;
2746 purple_connection_error(gc
, _("Connection not found; Please try to connect again.\n"));
2750 /* Read all available data from the SSL connection */
2752 /* Increase input buffer size as needed */
2753 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2754 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2755 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2756 purple_debug_info("sipe", "sipe_input_cb_ssl: new input buffer length %d\n", conn
->inbuflen
);
2759 /* Try to read as much as there is space left in the buffer */
2760 readlen
= conn
->inbuflen
- conn
->inbufused
- 1;
2761 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, readlen
);
2763 if (len
< 0 && errno
== EAGAIN
) {
2764 /* Try again later */
2766 } else if (len
< 0) {
2767 sipe_invalidate_ssl_connection(gc
, _("SSL read error"), "SSL read error\n");
2769 } else if (firstread
&& (len
== 0)) {
2770 sipe_invalidate_ssl_connection(gc
, _("Server has disconnected"), "Server has disconnected\n");
2774 conn
->inbufused
+= len
;
2777 /* Equivalence indicates that there is possibly more data to read */
2778 } while (len
== readlen
);
2780 conn
->inbuf
[conn
->inbufused
] = '\0';
2781 process_input(sip
, conn
);
2785 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2787 PurpleConnection
*gc
= data
;
2788 struct sipe_account_data
*sip
= gc
->proto_data
;
2790 struct sip_connection
*conn
= connection_find(sip
, source
);
2792 purple_debug_error("sipe", "Connection not found!\n");
2796 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2797 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2798 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2801 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2803 if (len
< 0 && errno
== EAGAIN
)
2805 else if (len
<= 0) {
2806 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2807 connection_remove(sip
, source
);
2808 if (sip
->fd
== source
) sip
->fd
= -1;
2812 conn
->inbufused
+= len
;
2813 conn
->inbuf
[conn
->inbufused
] = '\0';
2815 process_input(sip
, conn
);
2818 /* Callback for new connections on incoming TCP port */
2819 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2821 PurpleConnection
*gc
= data
;
2822 struct sipe_account_data
*sip
= gc
->proto_data
;
2823 struct sip_connection
*conn
;
2825 int newfd
= accept(source
, NULL
, NULL
);
2827 conn
= connection_create(sip
, newfd
);
2829 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2832 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
2834 PurpleConnection
*gc
= data
;
2835 struct sipe_account_data
*sip
;
2836 struct sip_connection
*conn
;
2838 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2846 purple_connection_error(gc
, _("Could not connect"));
2850 sip
= gc
->proto_data
;
2852 sip
->last_keepalive
= time(NULL
);
2854 conn
= connection_create(sip
, source
);
2856 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2860 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2863 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2865 struct sipe_account_data
*sip
= sipe_setup_ssl(data
, gsc
);
2866 if (sip
== NULL
) return;
2868 sip
->registertimeout
= purple_timeout_add((rand()%100) + 1000, (GSourceFunc
)subscribe_timeout
, sip
);
2872 static guint
sipe_ht_hash_nick(const char *nick
)
2874 char *lc
= g_utf8_strdown(nick
, -1);
2875 guint bucket
= g_str_hash(lc
);
2881 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
2883 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
2886 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
2888 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2890 sip
->listen_data
= NULL
;
2892 if (listenfd
== -1) {
2893 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2899 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2900 sip
->listenfd
= sip
->fd
;
2902 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
2904 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
2905 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2909 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
2911 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2914 sip
->query_data
= NULL
;
2916 if (!hosts
|| !hosts
->data
) {
2917 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
2921 addr_size
= GPOINTER_TO_INT(hosts
->data
);
2922 hosts
= g_slist_remove(hosts
, hosts
->data
);
2923 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
2924 g_free(hosts
->data
);
2925 hosts
= g_slist_remove(hosts
, hosts
->data
);
2927 hosts
= g_slist_remove(hosts
, hosts
->data
);
2928 g_free(hosts
->data
);
2929 hosts
= g_slist_remove(hosts
, hosts
->data
);
2932 /* create socket for incoming connections */
2933 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
2934 sipe_udp_host_resolved_listen_cb
, sip
);
2935 if (sip
->listen_data
== NULL
) {
2936 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2941 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
2944 PurpleConnection
*gc
= data
;
2945 struct sipe_account_data
*sip
;
2947 /* If the connection is already disconnected, we don't need to do anything else */
2948 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2951 sip
= gc
->proto_data
;
2956 case PURPLE_SSL_CONNECT_FAILED
:
2957 purple_connection_error(gc
, _("Connection Failed"));
2959 case PURPLE_SSL_HANDSHAKE_FAILED
:
2960 purple_connection_error(gc
, _("SSL Handshake Failed"));
2966 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
2968 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2969 PurpleProxyConnectData
*connect_data
;
2971 sip
->listen_data
= NULL
;
2973 sip
->listenfd
= listenfd
;
2974 if (sip
->listenfd
== -1) {
2975 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2979 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
2980 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2981 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
2982 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
2983 sipe_newconn_cb
, sip
->gc
);
2984 purple_debug_info("sipe", "connecting to %s port %d\n",
2985 sip
->realhostname
, sip
->realport
);
2986 /* open tcp connection to the server */
2987 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
2988 sip
->realport
, login_cb
, sip
->gc
);
2990 if (connect_data
== NULL
) {
2991 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
2996 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
2998 PurpleAccount
*account
= sip
->account
;
2999 PurpleConnection
*gc
= sip
->gc
;
3001 if (purple_account_get_bool(account
, "useport", FALSE
)) {
3002 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
3003 port
= purple_account_get_int(account
, "port", 0);
3005 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
3008 sip
->realhostname
= hostname
;
3009 sip
->realport
= port
;
3011 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
3014 /* TODO: is there a good default grow size? */
3015 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
3016 sip
->txbuf
= purple_circ_buffer_new(0);
3018 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
3020 if (!purple_ssl_is_supported()) {
3021 gc
->wants_to_die
= TRUE
;
3022 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
3026 purple_debug_info("sipe", "using SSL\n");
3028 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
3029 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
3030 if (sip
->gsc
== NULL
) {
3031 purple_connection_error(gc
, _("Could not create SSL context"));
3034 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
3036 purple_debug_info("sipe", "using UDP\n");
3038 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
3039 if (sip
->query_data
== NULL
) {
3040 purple_connection_error(gc
, _("Could not resolve hostname"));
3044 purple_debug_info("sipe", "using TCP\n");
3045 /* create socket for incoming connections */
3046 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
3047 sipe_tcp_connect_listen_cb
, sip
);
3048 if (sip
->listen_data
== NULL
) {
3049 purple_connection_error(gc
, _("Could not create listen socket"));
3055 /* Service list for autodection */
3056 static const struct sipe_service_data service_autodetect
[] = {
3057 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
3058 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
3059 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
3060 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
3064 /* Service list for SSL/TLS */
3065 static const struct sipe_service_data service_tls
[] = {
3066 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
3067 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
3071 /* Service list for TCP */
3072 static const struct sipe_service_data service_tcp
[] = {
3073 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
3074 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
3078 /* Service list for UDP */
3079 static const struct sipe_service_data service_udp
[] = {
3080 { "sip", "udp", SIPE_TRANSPORT_UDP
},
3084 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
3085 static void resolve_next_service(struct sipe_account_data
*sip
,
3086 const struct sipe_service_data
*start
)
3089 sip
->service_data
= start
;
3091 sip
->service_data
++;
3092 if (sip
->service_data
->service
== NULL
) {
3093 /* Try connecting to the SIP hostname directly */
3094 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
3095 // If SSL is supported, default to using it; OCS servers aren't configured
3096 // by default to accept TCP
3097 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
3098 gchar
* hostname
= g_strdup(sip
->sipdomain
);
3099 create_connection(sip
, hostname
, 0);
3104 /* Try to resolve next service */
3105 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
3106 sip
->service_data
->transport
,
3111 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
3113 struct sipe_account_data
*sip
= data
;
3115 sip
->srv_query_data
= NULL
;
3117 /* find the host to connect to */
3119 gchar
*hostname
= g_strdup(resp
->hostname
);
3120 int port
= resp
->port
;
3121 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
3125 sip
->transport
= sip
->service_data
->type
;
3127 create_connection(sip
, hostname
, port
);
3129 resolve_next_service(sip
, NULL
);
3133 static void sipe_login(PurpleAccount
*account
)
3135 PurpleConnection
*gc
;
3136 struct sipe_account_data
*sip
;
3138 const char *transport
;
3140 const char *username
= purple_account_get_username(account
);
3141 gc
= purple_account_get_connection(account
);
3143 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
3144 gc
->wants_to_die
= TRUE
;
3145 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
3149 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
3151 sip
->account
= account
;
3152 sip
->registerexpire
= 900;
3154 userserver
= g_strsplit(username
, "@", 2);
3155 purple_connection_set_display_name(gc
, userserver
[0]);
3156 sip
->username
= g_strdup(g_strjoin("@", userserver
[0], userserver
[1], NULL
));
3157 sip
->sipdomain
= g_strdup(userserver
[1]);
3158 sip
->password
= g_strdup(purple_connection_get_password(gc
));
3159 g_strfreev(userserver
);
3161 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
3163 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
3165 /* TODO: Set the status correctly. */
3166 sip
->status
= g_strdup("available");
3168 transport
= purple_account_get_string(account
, "transport", "auto");
3170 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
3171 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
3172 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
3173 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
3175 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
3176 } else if (strcmp(transport
, "auto") == 0) {
3177 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
3178 } else if (strcmp(transport
, "tls") == 0) {
3179 resolve_next_service(sip
, service_tls
);
3180 } else if (strcmp(transport
, "tcp") == 0) {
3181 resolve_next_service(sip
, service_tcp
);
3183 resolve_next_service(sip
, service_udp
);
3187 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
3189 connection_free_all(sip
);
3191 if (sip
->query_data
!= NULL
)
3192 purple_dnsquery_destroy(sip
->query_data
);
3193 sip
->query_data
== NULL
;
3195 if (sip
->srv_query_data
!= NULL
)
3196 purple_srv_cancel(sip
->srv_query_data
);
3197 sip
->srv_query_data
= NULL
;
3199 if (sip
->listen_data
!= NULL
)
3200 purple_network_listen_cancel(sip
->listen_data
);
3201 sip
->listen_data
= NULL
;
3203 if (sip
->gsc
!= NULL
)
3204 purple_ssl_close(sip
->gsc
);
3207 g_free(sip
->registrar
.nonce
);
3208 sip
->registrar
.nonce
= NULL
;
3209 g_free(sip
->registrar
.opaque
);
3210 sip
->registrar
.opaque
= NULL
;
3211 g_free(sip
->registrar
.realm
);
3212 sip
->registrar
.realm
= NULL
;
3213 g_free(sip
->registrar
.target
);
3214 sip
->registrar
.target
= NULL
;
3215 g_free(sip
->registrar
.digest_session_key
);
3216 sip
->registrar
.digest_session_key
= NULL
;
3217 g_free(sip
->registrar
.ntlm_key
);
3218 sip
->registrar
.ntlm_key
= NULL
;
3219 sip
->registrar
.type
= 0;
3220 sip
->registrar
.retries
= 0;
3222 g_free(sip
->proxy
.nonce
);
3223 sip
->proxy
.nonce
= NULL
;
3224 g_free(sip
->proxy
.opaque
);
3225 sip
->proxy
.opaque
= NULL
;
3226 g_free(sip
->proxy
.realm
);
3227 sip
->proxy
.realm
= NULL
;
3228 g_free(sip
->proxy
.target
);
3229 sip
->proxy
.target
= NULL
;
3230 g_free(sip
->proxy
.digest_session_key
);
3231 sip
->proxy
.digest_session_key
= NULL
;
3232 g_free(sip
->proxy
.ntlm_key
);
3233 sip
->proxy
.ntlm_key
= NULL
;
3234 sip
->proxy
.type
= 0;
3235 sip
->proxy
.retries
= 0;
3238 purple_circ_buffer_destroy(sip
->txbuf
);
3241 g_free(sip
->realhostname
);
3242 sip
->realhostname
= NULL
;
3245 purple_input_remove(sip
->listenpa
);
3247 if (sip
->tx_handler
)
3248 purple_input_remove(sip
->tx_handler
);
3249 sip
->tx_handler
= 0;
3250 if (sip
->resendtimeout
)
3251 purple_timeout_remove(sip
->resendtimeout
);
3252 sip
->resendtimeout
= 0;
3253 if (sip
->registertimeout
)
3254 purple_timeout_remove(sip
->registertimeout
);
3255 sip
->registertimeout
= 0;
3260 static void sipe_close(PurpleConnection
*gc
)
3262 struct sipe_account_data
*sip
= gc
->proto_data
;
3265 /* leave all conversations */
3266 im_session_close_all(sip
);
3269 do_register_exp(sip
, 0);
3271 sipe_connection_cleanup(sip
);
3272 g_free(sip
->sipdomain
);
3273 g_free(sip
->username
);
3274 g_free(sip
->password
);
3276 g_free(gc
->proto_data
);
3277 gc
->proto_data
= NULL
;
3280 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
3282 PurpleAccount
*acct
= purple_connection_get_account(gc
);
3283 char *id
= g_strdup_printf("sip:%s", g_list_nth_data(row
, 0));
3284 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
3286 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
3287 purple_conversation_present(conv
);
3291 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
, void *user_data
)
3294 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
3295 g_list_nth_data(row
, 0), NULL
, g_list_nth_data(row
, 1));
3298 static gboolean
process_search_contact_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
,struct transaction
*tc
)
3300 PurpleNotifySearchResults
*results
;
3301 PurpleNotifySearchColumn
*column
;
3302 xmlnode
*searchResults
;
3305 searchResults
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
3306 if (!searchResults
) {
3307 purple_debug_info("sipe", "process_search_contact_response: no parseable searchResults\n");
3311 results
= purple_notify_searchresults_new();
3313 if (results
== NULL
) {
3314 purple_debug_error("sipe", "purple_parse_searchreply: Unable to display the search results.\n");
3315 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results."), NULL
);
3317 xmlnode_free(searchResults
);
3321 column
= purple_notify_searchresults_column_new(_("User Name"));
3322 purple_notify_searchresults_column_add(results
, column
);
3324 column
= purple_notify_searchresults_column_new(_("Name"));
3325 purple_notify_searchresults_column_add(results
, column
);
3327 column
= purple_notify_searchresults_column_new(_("Company"));
3328 purple_notify_searchresults_column_add(results
, column
);
3330 column
= purple_notify_searchresults_column_new(_("Country"));
3331 purple_notify_searchresults_column_add(results
, column
);
3333 column
= purple_notify_searchresults_column_new(_("Email"));
3334 purple_notify_searchresults_column_add(results
, column
);
3336 int match_count
= 0;
3337 for (mrow
= xmlnode_get_descendant(searchResults
, "Body", "Array", "row", NULL
); mrow
; mrow
= xmlnode_get_next_twin(mrow
)) {
3340 gchar
**uri_parts
= g_strsplit(xmlnode_get_attrib(mrow
, "uri"), ":", 2);
3341 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
3342 g_strfreev(uri_parts
);
3344 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "displayName")));
3345 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "company")));
3346 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "country")));
3347 row
= g_list_append(row
, g_strdup(xmlnode_get_attrib(mrow
, "email")));
3349 purple_notify_searchresults_row_add(results
, row
);
3353 gboolean more
= FALSE
;
3354 if ((mrow
= xmlnode_get_descendant(searchResults
, "Body", "directorySearch", "moreAvailable", NULL
)) != NULL
) {
3355 char *data
= xmlnode_get_data_unescaped(mrow
);
3356 more
= (g_strcasecmp(data
, "true") == 0);
3360 gchar
*secondary
= g_strdup_printf(
3361 dngettext(GETTEXT_PACKAGE
,
3362 "Found %d contact%s:",
3363 "Found %d contacts%s:", match_count
),
3364 match_count
, more
? _(" (more matched your query)") : "");
3366 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
3367 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
3368 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
3371 xmlnode_free(searchResults
);
3375 static void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
3377 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
3378 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
3382 PurpleRequestField
*field
= entries
->data
;
3383 const char *id
= purple_request_field_get_id(field
);
3384 const char *value
= purple_request_field_string_get_value(field
);
3386 purple_debug_info("sipe", "sipe_search_contact_with_cb: %s = '%s'\n", id
, value
);
3388 if (value
!= NULL
) attrs
[i
++] = g_strdup_printf(SIPE_SOAP_SEARCH_ROW
, id
, value
);
3389 } while ((entries
= g_list_next(entries
)) != NULL
);
3393 gchar
*query
= g_strjoinv(NULL
, attrs
);
3394 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
3395 send_soap_request_with_cb(gc
->proto_data
, body
,
3396 (TransCallback
) process_search_contact_response
, NULL
);
3404 static void sipe_show_find_contact(PurplePluginAction
*action
)
3406 PurpleConnection
*gc
= (PurpleConnection
*) action
->context
;
3407 PurpleRequestFields
*fields
;
3408 PurpleRequestFieldGroup
*group
;
3409 PurpleRequestField
*field
;
3411 fields
= purple_request_fields_new();
3412 group
= purple_request_field_group_new(NULL
);
3413 purple_request_fields_add_group(fields
, group
);
3415 field
= purple_request_field_string_new("givenName", _("First Name"), NULL
, FALSE
);
3416 purple_request_field_group_add_field(group
, field
);
3417 field
= purple_request_field_string_new("sn", _("Last Name"), NULL
, FALSE
);
3418 purple_request_field_group_add_field(group
, field
);
3419 field
= purple_request_field_string_new("company", _("Company"), NULL
, FALSE
);
3420 purple_request_field_group_add_field(group
, field
);
3421 field
= purple_request_field_string_new("c", _("Country"), NULL
, FALSE
);
3422 purple_request_field_group_add_field(group
, field
);
3424 purple_request_fields(gc
,
3426 _("Search for a Contact"),
3427 _("Enter the information of the person you wish to find. Empty fields will be ignored."),
3429 _("_Search"), G_CALLBACK(sipe_search_contact_with_cb
),
3431 purple_connection_get_account(gc
), NULL
, NULL
, gc
);
3434 GList
*sipe_actions(PurplePlugin
*plugin
, gpointer context
)
3436 PurpleConnection
*gc
= (PurpleConnection
*) context
;
3437 struct sipe_account_data
*sip
= gc
->proto_data
;
3439 PurplePluginAction
*act
;
3441 act
= purple_plugin_action_new(_("Contact Search..."), sipe_show_find_contact
);
3442 menu
= g_list_prepend(menu
, act
);
3444 menu
= g_list_reverse(menu
);
3449 /* not needed since privacy is checked for every subscribe */
3450 static void dummy_add_deny(PurpleConnection
*gc
, const char *name
) {
3453 static void dummy_permit_deny(PurpleConnection
*gc
)
3457 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
3463 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
3469 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
3473 static PurplePlugin
*my_protocol
= NULL
;
3475 static PurplePluginProtocolInfo prpl_info
=
3478 NULL
, /* user_splits */
3479 NULL
, /* protocol_options */
3480 NO_BUDDY_ICONS
, /* icon_spec */
3481 sipe_list_icon
, /* list_icon */
3482 NULL
, /* list_emblems */
3483 NULL
, /* status_text */ // TODO
3484 NULL
, /* tooltip_text */ // add custom info to contact tooltip
3485 sipe_status_types
, /* away_states */
3486 NULL
, /* blist_node_menu */
3487 NULL
, /* chat_info */
3488 NULL
, /* chat_info_defaults */
3489 sipe_login
, /* login */
3490 sipe_close
, /* close */
3491 sipe_im_send
, /* send_im */
3492 NULL
, /* set_info */ // TODO maybe
3493 sipe_send_typing
, /* send_typing */
3494 NULL
, /* get_info */ // TODO maybe
3495 sipe_set_status
, /* set_status */
3496 NULL
, /* set_idle */
3497 NULL
, /* change_passwd */
3498 sipe_add_buddy
, /* add_buddy */
3499 NULL
, /* add_buddies */
3500 sipe_remove_buddy
, /* remove_buddy */
3501 NULL
, /* remove_buddies */
3502 dummy_add_deny
, /* add_permit */
3503 dummy_add_deny
, /* add_deny */
3504 dummy_add_deny
, /* rem_permit */
3505 dummy_add_deny
, /* rem_deny */
3506 dummy_permit_deny
, /* set_permit_deny */
3507 NULL
, /* join_chat */
3508 NULL
, /* reject_chat */
3509 NULL
, /* get_chat_name */
3510 NULL
, /* chat_invite */
3511 NULL
, /* chat_leave */
3512 NULL
, /* chat_whisper */
3513 NULL
, /* chat_send */
3514 sipe_keep_alive
, /* keepalive */
3515 NULL
, /* register_user */
3516 NULL
, /* get_cb_info */ // deprecated
3517 NULL
, /* get_cb_away */ // deprecated
3518 sipe_alias_buddy
, /* alias_buddy */
3519 sipe_group_buddy
, /* group_buddy */
3520 sipe_rename_group
, /* rename_group */
3521 NULL
, /* buddy_free */
3522 sipe_convo_closed
, /* convo_closed */
3523 purple_normalize_nocase
, /* normalize */
3524 NULL
, /* set_buddy_icon */
3525 sipe_remove_group
, /* remove_group */
3526 NULL
, /* get_cb_real_name */ // TODO?
3527 NULL
, /* set_chat_topic */
3528 NULL
, /* find_blist_chat */
3529 NULL
, /* roomlist_get_list */
3530 NULL
, /* roomlist_cancel */
3531 NULL
, /* roomlist_expand_category */
3532 NULL
, /* can_receive_file */
3533 NULL
, /* send_file */
3534 NULL
, /* new_xfer */
3535 NULL
, /* offline_message */
3536 NULL
, /* whiteboard_prpl_ops */
3537 sipe_send_raw
, /* send_raw */
3541 static PurplePluginInfo info
= {
3542 PURPLE_PLUGIN_MAGIC
,
3543 PURPLE_MAJOR_VERSION
,
3544 PURPLE_MINOR_VERSION
,
3545 PURPLE_PLUGIN_PROTOCOL
, /**< type */
3546 NULL
, /**< ui_requirement */
3548 NULL
, /**< dependencies */
3549 PURPLE_PRIORITY_DEFAULT
, /**< priority */
3550 "prpl-sipe", /**< id */
3551 "Microsoft LCS/OCS", /**< name */
3552 VERSION
, /**< version */
3553 "SIP/SIMPLE OCS/LCS Protocol Plugin", /** summary */
3554 "The SIP/SIMPLE LCS/OCS Protocol Plugin", /** description */
3555 "Anibal Avelar <avelar@gmail.com>, " /**< author */
3556 "Gabriel Burt <gburt@novell.com>", /**< author */
3557 PURPLE_WEBSITE
, /**< homepage */
3558 sipe_plugin_load
, /**< load */
3559 sipe_plugin_unload
, /**< unload */
3560 sipe_plugin_destroy
, /**< destroy */
3561 NULL
, /**< ui_info */
3562 &prpl_info
, /**< extra_info */
3571 static void init_plugin(PurplePlugin
*plugin
)
3573 PurpleAccountUserSplit
*split
;
3574 PurpleAccountOption
*option
;
3575 PurpleKeyValuePair
*kvp
;
3578 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
3579 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
3580 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
3583 purple_plugin_register(plugin
);
3585 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
3586 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3587 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
3588 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3590 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
3591 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3592 // Translators: noun (networking port)
3593 option
= purple_account_option_int_new(_("Port"), "port", 5061);
3594 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3596 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
3597 purple_account_option_add_list_item(option
, _("Auto"), "auto");
3598 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
3599 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
3600 purple_account_option_add_list_item(option
, _("UDP"), "udp");
3601 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3603 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
3604 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
3606 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
3607 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3609 // TODO commented out so won't show in the preferences until we fix krb message signing
3610 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
3611 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3613 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
3614 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
3615 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3618 option
= purple_account_option_string_new(_("Auth User"), "authuser", "");
3619 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3620 option
= purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
3621 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3622 my_protocol
= plugin
;
3625 /* I had to redefined the function for it load, but works */
3626 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
3627 plugin
->info
= &(info
);
3628 init_plugin((plugin
));
3629 sipe_plugin_load((plugin
));
3630 return purple_plugin_register(plugin
);