6 * Copyright (C) 2008 Novell, Inc.
7 * Copyright (C) 2007 Anibal Avelar "Fixxxer"<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 "sip-internal.h"
38 #define _LIBC_INTERNAL_
47 # include <glib/gi18n-lib.h>
49 # define _(String) ((const char *) (String))
50 #endif /* ENABLE_NLS */
52 #include "accountopt.h"
54 #include "conversation.h"
70 #endif /*USE_KERBEROS*/
73 #include "sipe-sign.h"
76 /* Keep in sync with sipe_transport_type! */
77 static const char *transport_descriptor
[] = { "tls", "tcp", "udp" };
78 #define TRANSPORT_DESCRIPTOR (transport_descriptor[sip->transport])
82 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
85 static gchar
*get_epid()
87 return sipe_uuid_get_macaddr();
90 static char *genbranch()
92 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
93 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
94 rand() & 0xFFFF, rand() & 0xFFFF);
97 static char *gencallid()
99 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
100 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
101 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
102 rand() & 0xFFFF, rand() & 0xFFFF);
105 static gchar
*find_tag(const gchar
*hdr
)
107 gchar
* tag
= sipmsg_find_part_of_header (hdr
, "tag=", ";", NULL
);
109 // In case it's at the end and there's no trailing ;
110 tag
= sipmsg_find_part_of_header (hdr
, "tag=", NULL
, NULL
);
116 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
121 static void sipe_keep_alive(PurpleConnection
*gc
)
123 struct sipe_account_data
*sip
= gc
->proto_data
;
124 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
125 /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */
126 gchar buf
[2] = {0, 0};
127 purple_debug_info("sipe", "sending keep alive\n");
128 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
132 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
134 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
135 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
138 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*);
139 static void sipe_close(PurpleConnection
*gc
);
141 static void send_service(struct sipe_account_data
*sip
);
142 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
);
143 static void send_presence_info(struct sipe_account_data
*sip
);
145 static void do_notifies(struct sipe_account_data
*sip
)
147 GSList
*tmp
= sip
->watcher
;
148 purple_debug_info("sipe", "do_notifies()\n");
151 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher
*)tmp
->data
)->name
);
152 send_notify(sip
, tmp
->data
);
157 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
159 struct sip_connection
*ret
= NULL
;
160 GSList
*entry
= sip
->openconns
;
163 if (ret
->fd
== fd
) return ret
;
169 static struct sipe_watcher
*watcher_find(struct sipe_account_data
*sip
,
172 struct sipe_watcher
*watcher
;
173 GSList
*entry
= sip
->watcher
;
175 watcher
= entry
->data
;
176 if (!strcmp(name
, watcher
->name
)) return watcher
;
182 static struct sipe_watcher
*watcher_create(struct sipe_account_data
*sip
,
183 const gchar
*name
, const gchar
*callid
, const gchar
*ourtag
,
184 const gchar
*theirtag
, gboolean needsxpidf
)
186 struct sipe_watcher
*watcher
= g_new0(struct sipe_watcher
, 1);
187 watcher
->name
= g_strdup(name
);
188 watcher
->dialog
.callid
= g_strdup(callid
);
189 watcher
->dialog
.ourtag
= g_strdup(ourtag
);
190 watcher
->dialog
.theirtag
= g_strdup(theirtag
);
191 watcher
->needsxpidf
= needsxpidf
;
192 sip
->watcher
= g_slist_append(sip
->watcher
, watcher
);
196 static void watcher_remove(struct sipe_account_data
*sip
, const gchar
*name
)
198 struct sipe_watcher
*watcher
= watcher_find(sip
, name
);
199 sip
->watcher
= g_slist_remove(sip
->watcher
, watcher
);
200 g_free(watcher
->name
);
201 g_free(watcher
->dialog
.callid
);
202 g_free(watcher
->dialog
.ourtag
);
203 g_free(watcher
->dialog
.theirtag
);
207 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
209 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
211 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
215 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
217 struct sip_connection
*conn
= connection_find(sip
, fd
);
218 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
219 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
224 static void connection_free_all(struct sipe_account_data
*sip
)
226 struct sip_connection
*ret
= NULL
;
227 GSList
*entry
= sip
->openconns
;
230 connection_remove(sip
, ret
->fd
);
231 entry
= sip
->openconns
;
235 static gchar
*auth_header_without_newline(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
237 const gchar
*method
= msg
->method
;
238 const gchar
*target
= msg
->target
;
243 const char *authdomain
;
244 const char *authuser
;
245 const char *krb5_realm
;
247 gchar
*krb5_token
= NULL
;
249 authdomain
= purple_account_get_string(sip
->account
, "authdomain", "");
250 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
252 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
253 // and do error checking
255 // KRB realm should always be uppercase
256 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
258 if (sip
->realhostname
) {
259 host
= sip
->realhostname
;
260 } else if (purple_account_get_bool(sip
->account
, "useproxy", TRUE
)) {
261 host
= purple_account_get_string(sip
->account
, "proxy", "");
263 host
= sip
->sipdomain
;
266 /*gboolean new_auth = krb5_auth.gss_context == NULL;
268 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
271 if (new_auth || force_reauth) {
272 krb5_token = krb5_auth.base64_token;
275 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
276 krb5_token = krb5_auth.base64_token;*/
278 if (!authuser
|| strlen(authuser
) < 1) {
279 authuser
= sip
->username
;
282 if (auth
->type
== 1) { /* Digest */
283 sprintf(noncecount
, "%08d", auth
->nc
++);
284 response
= purple_cipher_http_digest_calculate_response(
285 "md5", method
, target
, NULL
, NULL
,
286 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
287 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
289 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
);
292 } else if (auth
->type
== 2) { /* NTLM */
293 // If we have a signature for the message, include that
294 if (msg
->signature
) {
295 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
);
299 if (auth
->nc
== 3 && auth
->nonce
&& auth
->ntlm_key
== NULL
) {
300 /* TODO: Don't hardcode "purple" as the hostname */
301 const gchar
* ntlm_key
;
302 gchar
* gssapi_data
= purple_ntlm_gen_authenticate(&ntlm_key
, authuser
, sip
->password
, "purple", authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
303 auth
->ntlm_key
= (gchar
*)ntlm_key
;
304 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
309 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
311 } else if (auth
->type
== 3) {
314 /*if (new_auth || force_reauth) {
315 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
317 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);
319 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
322 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
323 gchar * mic = "MICTODO";
324 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
325 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
326 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
327 //auth->opaque ? auth->opaque : "", auth->target);
328 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
333 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
336 sprintf(noncecount
, "%08d", auth
->nc
++);
337 response
= purple_cipher_http_digest_calculate_response(
338 "md5", method
, target
, NULL
, NULL
,
339 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
340 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
342 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
);
347 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
349 gchar
*with
, *without
;
351 without
= auth_header_without_newline(sip
, auth
, msg
, force_reauth
);
352 with
= g_strdup_printf("%s\r\n", without
);
358 static char *parse_attribute(const char *attrname
, const char *source
)
360 const char *tmp
, *tmp2
;
362 int len
= strlen(attrname
);
364 if (!strncmp(source
, attrname
, len
)) {
366 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
368 retval
= g_strndup(tmp
, tmp2
- tmp
);
370 retval
= g_strdup(tmp
);
376 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
379 const char *authuser
;
382 const char *krb5_realm
;
385 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
386 // and do error checking
388 // KRB realm should always be uppercase
389 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
391 if (sip->realhostname) {
392 host = sip->realhostname;
393 } else if (purple_account_get_bool(sip->account, "useproxy", TRUE)) {
394 host = purple_account_get_string(sip->account, "proxy", "");
396 host = sip->sipdomain;
399 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
401 if (!authuser
|| strlen(authuser
) < 1) {
402 authuser
= sip
->username
;
406 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
410 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
412 parts
= g_strsplit(hdr
+5, "\", ", 0);
415 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
416 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
417 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
420 if ((tmp
= parse_attribute("targetname=\"",
424 else if ((tmp
= parse_attribute("realm=\"",
428 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
435 if (!strstr(hdr
, "gssapi-data")) {
443 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
444 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
446 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
447 parts
= g_strsplit(hdr
+9, "\", ", 0);
450 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
451 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
452 /*if (krb5_auth.gss_context == NULL) {
453 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
455 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
458 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
460 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
462 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
469 //if (!strstr(hdr, "gssapi-data")) {
478 parts
= g_strsplit(hdr
, " ", 0);
480 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
483 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
490 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
492 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
493 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
499 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
501 PurpleConnection
*gc
= data
;
502 struct sipe_account_data
*sip
= gc
->proto_data
;
506 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
508 if (max_write
== 0) {
509 if (sip
->tx_handler
!= 0){
510 purple_input_remove(sip
->tx_handler
);
516 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
518 if (written
< 0 && errno
== EAGAIN
)
520 else if (written
<= 0) {
521 /*TODO: do we really want to disconnect on a failure to write?*/
522 purple_connection_error(gc
, _("Could not write"));
526 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
529 static void sipe_canwrite_cb_ssl(gpointer data
, gint src
, PurpleInputCondition cond
)
531 PurpleConnection
*gc
= data
;
532 struct sipe_account_data
*sip
= gc
->proto_data
;
536 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
538 if (max_write
== 0) {
539 if (sip
->tx_handler
!= 0) {
540 purple_input_remove(sip
->tx_handler
);
546 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
548 if (written
< 0 && errno
== EAGAIN
)
550 else if (written
<= 0) {
551 /*TODO: do we really want to disconnect on a failure to write?*/
552 purple_connection_error(gc
, _("Could not write"));
556 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
559 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
561 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
563 PurpleConnection
*gc
= data
;
564 struct sipe_account_data
*sip
;
565 struct sip_connection
*conn
;
567 if (!PURPLE_CONNECTION_IS_VALID(gc
))
575 purple_connection_error(gc
, _("Could not connect"));
579 sip
= gc
->proto_data
;
581 sip
->connecting
= FALSE
;
583 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
585 /* If there is more to write now, we need to register a handler */
586 if (sip
->txbuf
->bufused
> 0)
587 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb
, gc
);
589 conn
= connection_create(sip
, source
);
590 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
593 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
595 PurpleConnection
*gc
= data
;
596 struct sipe_account_data
*sip
;
597 struct sip_connection
*conn
;
599 if (!PURPLE_CONNECTION_IS_VALID(gc
))
601 if(gsc
) purple_ssl_close(gsc
);
605 sip
= gc
->proto_data
;
608 sip
->connecting
= FALSE
;
610 sipe_canwrite_cb_ssl(gc
, sip
->gsc
->fd
, PURPLE_INPUT_WRITE
);
612 /* If there is more to write now */
613 if (sip
->txbuf
->bufused
> 0)
615 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
618 conn
= connection_create(sip
, sip
->gsc
->fd
);
619 purple_ssl_input_add(sip
->gsc
, sipe_input_cb_ssl
, gc
);
623 static void sendlater(PurpleConnection
*gc
, const char *buf
)
625 struct sipe_account_data
*sip
= gc
->proto_data
;
627 if (!sip
->connecting
) {
628 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
629 if (sip
->transport
== SIPE_TRANSPORT_TLS
){
630 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
632 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
633 purple_connection_error(gc
, _("Couldn't create socket"));
636 sip
->connecting
= TRUE
;
639 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
640 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
642 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
645 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
647 struct sipe_account_data
*sip
= gc
->proto_data
;
648 time_t currtime
= time(NULL
);
649 int writelen
= strlen(buf
);
651 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
652 if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
653 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
654 purple_debug_info("sipe", "could not send packet\n");
663 if (sip
->tx_handler
) {
668 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
670 ret
= write(sip
->fd
, buf
, writelen
);
674 if (ret
< 0 && errno
== EAGAIN
)
676 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
681 if (ret
< writelen
) {
682 if (!sip
->tx_handler
){
684 sip
->tx_handler
= purple_input_add(sip
->gsc
->fd
, PURPLE_INPUT_WRITE
, sipe_canwrite_cb_ssl
, gc
);
687 sip
->tx_handler
= purple_input_add(sip
->fd
,
688 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
693 /* XXX: is it OK to do this? You might get part of a request sent
694 with part of another. */
695 if (sip
->txbuf
->bufused
> 0)
696 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
698 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
704 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
706 sendout_pkt(gc
, buf
);
710 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
712 GSList
*tmp
= msg
->headers
;
715 GString
*outstr
= g_string_new("");
716 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
718 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
719 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
720 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
721 tmp
= g_slist_next(tmp
);
723 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
724 sendout_pkt(sip
->gc
, outstr
->str
);
725 g_string_free(outstr
, TRUE
);
728 static void sign_outgoing_message (struct sipmsg
* msg
, struct sipe_account_data
*sip
, const gchar
*method
)
731 if (sip
->registrar
.ntlm_key
) {
732 struct sipmsg_breakdown msgbd
;
734 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
735 // TODO generate this
736 msgbd
.rand
= g_strdup("0878F41B");
737 sip
->registrar
.ntlm_num
++;
738 msgbd
.num
= g_strdup_printf("%d", sip
->registrar
.ntlm_num
);
739 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
740 if (signature_input_str
!= NULL
) {
741 msg
->signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
742 msg
->rand
= g_strdup(msgbd
.rand
);
743 msg
->num
= g_strdup(msgbd
.num
);
745 sipmsg_breakdown_free(&msgbd
);
748 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
749 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
750 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
751 sipmsg_add_header(msg
, "Authorization", buf
);
753 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
754 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
757 } 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")) {
759 sip
->registrar
.type
=2;
761 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
762 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
763 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
764 //sipmsg_add_header(msg, "Authorization", buf);
767 purple_debug_info("sipe", "not adding auth header to msg w/ method %s\n", method
);
771 static char *get_contact(struct sipe_account_data
*sip
)
773 return g_strdup(sip
->contact
);
777 static char *get_contact_service(struct sipe_account_data
*sip
)
779 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()));
780 //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);
783 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
784 const char *text
, const char *body
)
788 GString
*outstr
= g_string_new("");
789 struct sipe_account_data
*sip
= gc
->proto_data
;
792 contact
= get_contact(sip
);
793 sipmsg_remove_header(msg
, "Contact");
794 sipmsg_add_header(msg
, "Contact", contact
);
797 /* When sending the acknowlegements and errors, the content length from the original
798 message is still here, but there is no body; we need to make sure we're sending the
799 correct content length */
800 sipmsg_remove_header(msg
, "Content-Length");
803 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
804 sipmsg_add_header(msg
, "Content-Length", len
);
806 sipmsg_add_header(msg
, "Content-Length", "0");
809 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
810 //gchar * mic = "MICTODO";
811 msg
->response
= code
;
813 sipmsg_remove_header(msg
, "Authentication-Info");
814 sign_outgoing_message(msg
, sip
, msg
->method
);
816 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
817 GSList
*tmp
= msg
->headers
;
819 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
820 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
822 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
823 tmp
= g_slist_next(tmp
);
825 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
826 sendout_pkt(gc
, outstr
->str
);
827 g_string_free(outstr
, TRUE
);
830 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
832 if (trans
->msg
) sipmsg_free(trans
->msg
);
833 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
837 static struct transaction
*
838 transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
840 struct transaction
*trans
= g_new0(struct transaction
, 1);
841 trans
->time
= time(NULL
);
842 trans
->msg
= (struct sipmsg
*)msg
;
843 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
844 trans
->callback
= callback
;
845 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
849 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
851 struct transaction
*trans
;
852 GSList
*transactions
= sip
->transactions
;
853 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
855 while (transactions
) {
856 trans
= transactions
->data
;
857 if (!strcmp(trans
->cseq
, cseq
)) {
860 transactions
= transactions
->next
;
866 static struct transaction
*
867 send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
868 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
869 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
871 struct sipe_account_data
*sip
= gc
->proto_data
;
872 const char *addh
= "";
876 gchar
*ourtag
= dialog
&& dialog
->ourtag
? g_strdup(dialog
->ourtag
) : NULL
;
877 gchar
*theirtag
= dialog
&& dialog
->theirtag
? g_strdup(dialog
->theirtag
) : NULL
;
878 gchar
*theirepid
= dialog
&& dialog
->theirepid
? g_strdup(dialog
->theirepid
) : NULL
;
879 gchar
*callid
= dialog
&& dialog
->callid
? g_strdup(dialog
->callid
) : gencallid();
880 gchar
*branch
= dialog
&& dialog
->callid
? NULL
: genbranch();
881 gchar
*useragent
= (gchar
*)purple_account_get_string(sip
->account
, "useragent", "Purple/" VERSION
);
883 if (!ourtag
&& !dialog
) {
887 if (!strcmp(method
, "REGISTER")) {
888 if (sip
->regcallid
) {
890 callid
= g_strdup(sip
->regcallid
);
892 sip
->regcallid
= g_strdup(callid
);
896 if (addheaders
) addh
= addheaders
;
898 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
899 "Via: SIP/2.0/%s %s:%d%s%s\r\n"
900 "From: <sip:%s>%s%s;epid=%s\r\n"
901 "To: <%s>%s%s%s%s\r\n"
902 "Max-Forwards: 70\r\n"
907 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
909 dialog
&& dialog
->request
? dialog
->request
: url
,
910 TRANSPORT_DESCRIPTOR
,
911 purple_network_get_my_ip(-1),
913 branch
? ";branch=" : "",
914 branch
? branch
: "",
916 ourtag
? ";tag=" : "",
917 ourtag
? ourtag
: "",
918 get_epid(), // TODO generate one per account/login
920 theirtag
? ";tag=" : "",
921 theirtag
? theirtag
: "",
922 theirepid
? ";epid=" : "",
923 theirepid
? theirepid
: "",
924 dialog
? ++dialog
->cseq
: ++sip
->cseq
,
928 dialog
&& dialog
->route
? dialog
->route
: "",
930 body
? strlen(body
) : 0,
934 //printf ("parsing msg buf:\n%s\n\n", buf);
935 msg
= sipmsg_parse_msg(buf
);
944 sign_outgoing_message (msg
, sip
, method
);
946 buf
= sipmsg_to_string (msg
);
948 /* add to ongoing transactions */
949 struct transaction
* trans
= transactions_add_buf(sip
, msg
, tc
);
950 sendout_pkt(gc
, buf
);
955 static void send_soap_request_with_cb(struct sipe_account_data
*sip
, gchar
*body
, TransCallback callback
, void * payload
)
957 gchar
*from
= g_strdup_printf("sip:%s", sip
->username
);
958 gchar
*hdr
= g_strdup("Content-Type: application/SOAP+xml\r\n");
960 struct transaction
* tr
= send_sip_request(sip
->gc
, "SERVICE", from
, from
, hdr
, body
, NULL
, callback
);
961 tr
->payload
= payload
;
967 static void send_soap_request(struct sipe_account_data
*sip
, gchar
*body
)
969 send_soap_request_with_cb(sip
, body
, NULL
, NULL
);
972 static char *get_contact_register(struct sipe_account_data
*sip
)
974 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()));
977 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
979 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
980 char *to
= g_strdup_printf("sip:%s", sip
->username
);
981 char *contact
= get_contact_register(sip
);
982 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
983 // char *hdr = g_strdup_printf("Contact: %s\r\nEvent: registration\r\nAllow-Events: presence\r\nms-keep-alive: UAC;hop-hop=yes\r\nExpires: %d\r\n", contact,expire);
984 //char *hdr = g_strdup_printf("Contact: %s\r\nSupported: com.microsoft.msrtc.presence, adhoclist\r\nms-keep-alive: UAC;hop-hop=yes\r\nEvent: registration\r\nAllow-Events: presence\r\n", contact);
985 //char *hdr = g_strdup_printf("Contact: %s\r\nSupported: com.microsoft.msrtc.presence, gruu-10, adhoclist\r\nEvent: registration\r\nAllow-Events: presence\r\nms-keep-alive: UAC;hop-hop=yes\r\nExpires: %d\r\n", contact,expire);
986 char *hdr
= g_strdup_printf("Contact: %s\r\n"
987 "Supported: gruu-10, adhoclist\r\n"
988 "Event: registration\r\n"
989 "Allow-Events: presence\r\n"
990 "ms-keep-alive: UAC;hop-hop=yes\r\n"
991 "Expires: %d\r\n", contact
,expire
);
994 sip
->registerstatus
= 1;
997 sip
->reregister
= time(NULL
) + expire
- 50;
999 sip
->reregister
= time(NULL
) + 600;
1002 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
1003 process_register_response
);
1010 static void do_register(struct sipe_account_data
*sip
)
1012 do_register_exp(sip
, sip
->registerexpire
);
1015 static gchar
*parse_from(const gchar
*hdr
)
1018 const gchar
*tmp
, *tmp2
= hdr
;
1020 if (!hdr
) return NULL
;
1021 purple_debug_info("sipe", "parsing address out of %s\n", hdr
);
1022 tmp
= strchr(hdr
, '<');
1024 /* i hate the different SIP UA behaviours... */
1025 if (tmp
) { /* sip address in <...> */
1027 tmp
= strchr(tmp2
, '>');
1029 from
= g_strndup(tmp2
, tmp
- tmp2
);
1031 purple_debug_info("sipe", "found < without > in From\n");
1035 tmp
= strchr(tmp2
, ';');
1037 from
= g_strndup(tmp2
, tmp
- tmp2
);
1039 from
= g_strdup(tmp2
);
1042 purple_debug_info("sipe", "got %s\n", from
);
1046 static xmlnode
* xmlnode_get_descendant(xmlnode
* parent
, ...)
1052 va_start(args
, parent
);
1053 while ((name
= va_arg(args
, const char *)) != NULL
) {
1054 node
= xmlnode_get_child(parent
, name
);
1055 if (node
== NULL
) return NULL
;
1064 sipe_group_add (struct sipe_account_data
*sip
, struct sipe_group
* group
)
1066 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
1067 if (!purple_group
) {
1068 purple_group
= purple_group_new(group
->name
);
1069 purple_blist_add_group(purple_group
, NULL
);
1073 group
->purple_group
= purple_group
;
1074 sip
->groups
= g_slist_append(sip
->groups
, group
);
1075 purple_debug_info("sipe", "added group %s (id %d)\n", group
->name
, group
->id
);
1077 purple_debug_info("sipe", "did not add group %s\n", group
->name
);
1081 static struct sipe_group
* sipe_group_find_by_id (struct sipe_account_data
*sip
, int id
)
1087 struct sipe_group
*group
;
1088 GSList
*entry
= sip
->groups
;
1090 group
= entry
->data
;
1091 if (group
->id
== id
) {
1094 entry
= entry
->next
;
1099 static struct sipe_group
* sipe_group_find_by_name (struct sipe_account_data
*sip
, gchar
* name
)
1105 struct sipe_group
*group
;
1106 GSList
*entry
= sip
->groups
;
1108 group
= entry
->data
;
1109 if (!strcmp(group
->name
, name
)) {
1112 entry
= entry
->next
;
1118 sipe_group_rename (struct sipe_account_data
*sip
, struct sipe_group
* group
, gchar
* name
)
1120 purple_debug_info("sipe", "Renaming group %s to %s\n", group
->name
, name
);
1121 gchar
* body
= g_strdup_printf(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->delta_num
++);
1122 send_soap_request(sip
, body
);
1124 g_free(group
->name
);
1125 group
->name
= g_strdup(name
);
1129 sipe_group_set_user (struct sipe_account_data
*sip
, struct sipe_group
* group
, const gchar
* who
)
1131 struct sipe_buddy
*buddy
= g_hash_table_lookup(sip
->buddies
, who
);
1132 PurpleBuddy
* purple_buddy
= purple_find_buddy (sip
->account
, who
);
1135 group
= sipe_group_find_by_id (sip
, buddy
->group_id
);
1137 buddy
->group_id
= group
? group
->id
: 1;
1139 if (buddy
&& purple_buddy
) {
1140 gchar
* alias
= purple_buddy_get_alias(purple_buddy
);
1141 purple_debug_info("sipe", "Saving buddy %s with alias %s and group_id %d\n", who
, alias
, buddy
->group_id
);
1142 gchar
* body
= g_strdup_printf(SIPE_SOAP_SET_CONTACT
,
1143 alias
, buddy
->group_id
, "true", buddy
->name
, sip
->delta_num
++
1145 send_soap_request(sip
, body
);
1150 static gboolean
process_add_group_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1152 if (msg
->response
== 200) {
1153 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1155 struct group_user_context
* ctx
= (struct group_user_context
*)tc
->payload
;
1156 group
->name
= ctx
->group_name
;
1158 xmlnode
* xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1159 if (!xml
) return FALSE
;
1161 xmlnode
* node
= xmlnode_get_descendant(xml
, "Body", "addGroup", "groupID", NULL
);
1162 if (!node
) return FALSE
;
1164 char * group_id
= xmlnode_get_data(node
);
1165 if (!group_id
) return FALSE
;
1167 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
1169 sipe_group_add(sip
, group
);
1170 sipe_group_set_user(sip
, group
, ctx
->user_name
);
1179 static void sipe_group_create (struct sipe_account_data
*sip
, gchar
*name
, gchar
* who
)
1181 struct group_user_context
* ctx
= g_new0(struct group_user_context
, 1);
1182 ctx
->group_name
= g_strdup(name
);
1183 ctx
->user_name
= g_strdup(who
);
1185 gchar
* body
= g_strdup_printf(SIPE_SOAP_ADD_GROUP
, name
, sip
->delta_num
++);
1186 send_soap_request_with_cb(sip
, body
, process_add_group_response
, ctx
);
1190 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1194 if (msg
->response
== 200 || msg
->response
== 202) {
1198 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* cant be NULL since it is our own msg */
1200 /* we can not subscribe -> user is offline (TODO unknown status?) */
1202 purple_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
1207 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
)
1209 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1210 gchar
*tmp
= get_contact(sip
);
1211 gchar
*contact
= g_strdup_printf(
1212 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1213 "Event: presence\r\n"
1214 "Contact: %s\r\n", tmp
);
1217 /* subscribe to buddy presence */
1218 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
, process_subscribe_response
);
1224 static void sipe_subscribe(struct sipe_account_data
*sip
, struct sipe_buddy
*buddy
)
1226 sipe_subscribe_to_name(sip
, buddy
->name
);
1228 /* resubscribe before subscription expires */
1229 /* add some jitter */
1230 buddy
->resubscribe
= time(NULL
)+1140+(rand()%50);
1233 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
1235 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(purple_status_get_type(status
));
1236 struct sipe_account_data
*sip
= NULL
;
1238 if (!purple_status_is_active(status
))
1242 sip
= account
->gc
->proto_data
;
1245 g_free(sip
->status
);
1247 if (primitive
== PURPLE_STATUS_AWAY
) {
1248 sip
->status
= g_strdup("away");
1249 } else if (primitive
== PURPLE_STATUS_AVAILABLE
) {
1250 sip
->status
= g_strdup("available");
1251 } else if (primitive
== PURPLE_STATUS_UNAVAILABLE
) {
1252 sip
->status
= g_strdup("busy");
1255 send_presence_info(sip
);
1260 sipe_alias_buddy(PurpleConnection
*gc
, const char *name
, const char *alias
)
1262 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1263 sipe_group_set_user(sip
, NULL
, name
);
1267 sipe_group_buddy(PurpleConnection
*gc
,
1269 const char *old_group_name
,
1270 const char *new_group_name
)
1272 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1273 struct sipe_group
* group
= sipe_group_find_by_name(sip
, new_group_name
);
1275 sipe_group_create(sip
, new_group_name
, who
);
1277 sipe_group_set_user(sip
, group
, who
);
1281 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1283 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1284 struct sipe_buddy
*b
;
1286 // Prepend sip: if needed
1287 if (strncmp("sip:", buddy
->name
, 4)) {
1288 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
1289 purple_blist_rename_buddy(buddy
, buf
);
1293 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
1294 b
= g_new0(struct sipe_buddy
, 1);
1295 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
1296 b
->name
= g_strdup(buddy
->name
);
1297 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
1299 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1301 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
1305 // Not Used; Remove?
1306 /*static void sipe_get_buddies(PurpleConnection *gc)
1308 PurpleBlistNode *gnode, *cnode, *bnode;
1310 purple_debug_info("sipe", "sipe_get_buddies\n");
1312 for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
1313 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue;
1314 for (cnode = gnode->child; cnode; cnode = cnode->next) {
1315 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue;
1316 for (bnode = cnode->child; bnode; bnode = bnode->next) {
1317 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue;
1318 if (((PurpleBuddy*)bnode)->account == gc->account)
1319 sipe_add_buddy(gc, (PurpleBuddy*)bnode, (PurpleGroup *)gnode);
1325 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1327 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1328 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
1331 g_hash_table_remove(sip
->buddies
, buddy
->name
);
1334 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->delta_num
++);
1335 send_soap_request(sip
, body
);
1344 sipe_rename_group(PurpleConnection
*gc
,
1345 const char *old_name
,
1347 GList
*moved_buddies
)
1349 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1350 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, old_name
);
1352 sipe_group_rename(sip
, s_group
, group
->name
);
1354 purple_debug_info("sipe", "Cannot find group %s to rename\n", old_name
);
1359 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1361 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1362 struct sipe_group
* s_group
= sipe_group_find_by_name(sip
, group
->name
);
1364 purple_debug_info("sipe", "Deleting group %s\n", group
->name
);
1365 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->delta_num
++);
1366 send_soap_request(sip
, body
);
1369 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1370 g_free(s_group
->name
);
1372 purple_debug_info("sipe", "Cannot find group %s to delete\n", group
->name
);
1376 static GList
*sipe_status_types(PurpleAccount
*acc
)
1378 PurpleStatusType
*type
;
1379 GList
*types
= NULL
;
1382 type
= purple_status_type_new_with_attrs(
1383 PURPLE_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1384 // Translators: noun
1385 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1387 types
= g_list_append(types
, type
);
1390 type
= purple_status_type_new_with_attrs(
1391 PURPLE_STATUS_AWAY
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
1392 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1394 types
= g_list_append(types
, type
);
1397 type
= purple_status_type_new_with_attrs(
1398 PURPLE_STATUS_UNAVAILABLE
, "busy", _("Busy"), TRUE
, TRUE
, FALSE
,
1399 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
1401 types
= g_list_append(types
, type
);
1404 type
= purple_status_type_new_full(
1405 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
1406 types
= g_list_append(types
, type
);
1411 static gboolean
sipe_add_lcs_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1413 int len
= msg
->bodylen
;
1415 gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1416 if (!tmp
|| strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1420 /* Convert the contact from XML to Purple Buddies */
1421 xmlnode
* isc
= xmlnode_from_str(msg
->body
, len
);
1426 char * delta_num
= xmlnode_get_attrib(isc
, "deltaNum");
1428 sip
->delta_num
= (int)g_ascii_strtod(delta_num
, NULL
);
1432 xmlnode
*group_node
;
1433 for (group_node
= xmlnode_get_child(isc
, "group"); group_node
; group_node
= xmlnode_get_next_twin(group_node
)) {
1434 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1436 group
->name
= xmlnode_get_attrib(group_node
, "name");
1437 if (!strncmp(group
->name
, "~", 1)){
1439 group
->name
= "General";
1441 group
->name
= g_strdup(group
->name
);
1442 group
->id
= (int)g_ascii_strtod(xmlnode_get_attrib(group_node
, "id"), NULL
);
1444 sipe_group_add(sip
, group
);
1447 // Make sure we have at least one group
1448 if (g_slist_length(sip
->groups
) == 0) {
1449 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1451 group
->name
= g_strdup("General");
1453 PurpleGroup
* purple_group
= purple_group_new(group
->name
);
1454 purple_blist_add_group(purple_group
, NULL
);
1455 sip
->groups
= g_slist_append(sip
->groups
, group
);
1458 /* Parse contacts */
1460 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1461 char * uri
= xmlnode_get_attrib(item
, "uri");
1462 char * name
= xmlnode_get_attrib(item
, "name");
1463 gchar
**item_groups
= g_strsplit(xmlnode_get_attrib(item
, "groups"), " ", 0);
1465 struct sipe_group
* group
= NULL
;
1467 // Find the first group this contact belongs to; that's where we'll place it in the buddy list
1468 if (item_groups
[0]) {
1469 group
= sipe_group_find_by_id(sip
, g_ascii_strtod(item_groups
[0], NULL
));
1472 // If couldn't find the right group for this contact, just put them in the first group we have
1473 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1474 group
= sip
->groups
->data
;
1477 if (group
!= NULL
) {
1478 char * buddy_name
= g_strdup_printf("sip:%s", uri
);
1480 //b = purple_find_buddy(sip->account, buddy_name);
1481 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1483 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1487 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1489 if (name
!= NULL
&& strlen(name
) != 0) {
1490 purple_blist_alias_buddy(b
, name
);
1492 purple_blist_alias_buddy(b
, uri
);
1495 struct sipe_buddy
* buddy
= g_new0(struct sipe_buddy
, 1);
1496 buddy
->name
= g_strdup(b
->name
);
1497 buddy
->group_id
= group
->id
;
1498 g_hash_table_insert(sip
->buddies
, buddy
->name
, buddy
);
1500 purple_debug_info("sipe", "Added buddy %s to group %s\n", buddy
->name
, group
->name
);
1502 purple_debug_info("sipe", "No group found for contact %s! Unable to add to buddy list\n",
1512 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1514 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1515 gchar
*tmp
= get_contact(sip
);
1516 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-roaming-contacts\r\n"
1517 "Accept: application/vnd-microsoft-roaming-contacts+xml\r\n"
1518 "Supported: com.microsoft.autoextend\r\n"
1519 "Supported: ms-benotify\r\n"
1520 "Proxy-Require: ms-benotify\r\n"
1521 "Supported: ms-piggyback-first-notify\r\n"
1522 "Contact: %s\r\n", tmp
);
1525 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, "", NULL
, sipe_add_lcs_contacts
);
1530 static void sipe_subscribe_roaming_self(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1532 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1533 gchar
*tmp
= get_contact(sip
);
1534 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-roaming-self\r\n"
1535 "Accept: application/vnd-microsoft-roaming-self+xml\r\n"
1536 "Supported: com.microsoft.autoextend\r\n"
1537 "Supported: ms-benotify\r\n"
1538 "Proxy-Require: ms-benotify\r\n"
1539 "Supported: ms-piggyback-first-notify\r\n"
1541 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n"
1546 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>");
1548 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
1554 static void sipe_subscribe_roaming_provisioning(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
1556 gchar
*to
= g_strdup_printf("sip:%s", sip
->username
);
1557 gchar
*tmp
= get_contact(sip
);
1558 gchar
*hdr
= g_strdup_printf("Event: vnd-microsoft-provisioning-v2\r\n"
1559 "Accept: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
1560 "Supported: com.microsoft.autoextend\r\n"
1561 "Supported: ms-benotify\r\n"
1562 "Proxy-Require: ms-benotify\r\n"
1563 "Supported: ms-piggyback-first-notify\r\n"
1566 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n"
1571 gchar
*body
=g_strdup("<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\"><provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/><provisioningGroup name=\"ucPolicy\"/></provisioningGroupList>");
1572 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, hdr
, body
, NULL
, NULL
);
1578 /* IM Session (INVITE and MESSAGE methods) */
1580 static struct sip_im_session
* find_im_session (struct sipe_account_data
*sip
, const char *who
)
1582 if (sip
== NULL
|| who
== NULL
) {
1586 struct sip_im_session
*session
;
1587 GSList
*entry
= sip
->im_sessions
;
1589 session
= entry
->data
;
1590 if ((who
!= NULL
&& !strcmp(who
, session
->with
))) {
1593 entry
= entry
->next
;
1598 static struct sip_im_session
* find_or_create_im_session (struct sipe_account_data
*sip
, const char *who
)
1600 struct sip_im_session
*session
= find_im_session(sip
, who
);
1602 session
= g_new0(struct sip_im_session
, 1);
1603 session
->with
= g_strdup(who
);
1604 sip
->im_sessions
= g_slist_append(sip
->im_sessions
, session
);
1609 static void im_session_destroy(struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1611 sip
->im_sessions
= g_slist_remove(sip
->im_sessions
, session
);
1612 // TODO free session resources
1615 static void sipe_send_message(struct sipe_account_data
*sip
, struct sip_im_session
* session
, const char *msg
)
1621 if (strncmp("sip:", session
->with
, 4)) {
1622 fullto
= g_strdup_printf("sip:%s", session
->with
);
1624 fullto
= g_strdup(session
->with
);
1627 hdr
= g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1628 //hdr = g_strdup("Content-Type: text/rtf\r\n");
1629 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA\r\nSupported: timer\r\n");
1631 tmp
= get_contact(sip
);
1632 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1635 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msg
, session
->dialog
, NULL
);
1643 sipe_im_process_queue (struct sipe_account_data
* sip
, struct sip_im_session
* session
)
1645 GSList
*entry
= session
->outgoing_message_queue
;
1647 char *queued_msg
= entry
->data
;
1648 sipe_send_message(sip
, session
, queued_msg
);
1650 // Remove from the queue and free the string
1651 entry
= session
->outgoing_message_queue
= g_slist_remove(session
->outgoing_message_queue
, queued_msg
);
1657 sipe_parse_dialog(struct sipmsg
* msg
, struct sip_dialog
* dialog
, gboolean outgoing
)
1659 gchar
*us
= outgoing
? "From" : "To";
1660 gchar
*them
= outgoing
? "To" : "From";
1662 dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
1663 dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, us
));
1664 dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, them
));
1665 if (!dialog
->theirepid
) {
1666 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", ";", NULL
);
1668 if (!dialog
->theirepid
) {
1669 dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, them
), "epid=", NULL
, NULL
);
1672 dialog
->request
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Record-Route"), "<", ">", NULL
);
1673 dialog
->route
= g_strdup_printf("Route: %s\r\n", sipmsg_find_header(msg
, "Contact"));
1678 process_invite_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*trans
)
1680 gchar
* with
= parse_from(sipmsg_find_header(msg
, "To"));
1681 struct sip_im_session
* session
= find_im_session(sip
, with
);
1685 purple_debug_info("sipe", "process_invite_response: unable to find IM session\n");
1689 if (msg
->response
!= 200) {
1690 purple_debug_info("sipe", "process_invite_response: INVITE response not 200, ignoring\n");
1691 im_session_destroy(sip
, session
);
1695 struct sip_dialog
* dialog
= session
->dialog
;
1697 purple_debug_info("sipe", "process_invite_response: session outgoign dialog is NULL\n");
1701 sipe_parse_dialog(msg
, dialog
, TRUE
);
1704 send_sip_request(sip
->gc
, "ACK", session
->with
, session
->with
, NULL
, NULL
, dialog
, NULL
);
1705 session
->outgoing_invite
= NULL
;
1706 sipe_im_process_queue(sip
, session
);
1712 static void sipe_invite(struct sipe_account_data
*sip
, struct sip_im_session
* session
, gchar
* msg_body
)
1719 if (session
->dialog
) {
1720 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->with
);
1724 session
->dialog
= g_new0(struct sip_dialog
, 1);
1726 if (strstr(session
->with
, "sip:")) {
1727 to
= g_strdup(session
->with
);
1729 to
= g_strdup_printf("sip:%s", session
->with
);
1732 char * base64_msg
= purple_base64_encode((guchar
*) msg_body
, strlen(msg_body
));
1733 char * ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
, base64_msg
);
1736 contact
= get_contact(sip
);
1737 hdr
= g_strdup_printf(
1739 "Content-Type: application/sdp\r\n",
1740 contact
, ms_text_format
, sip
->username
, sip
->username
, to
);
1741 g_free(ms_text_format
);
1743 body
= g_strdup_printf(
1745 "o=- 0 0 IN IP4 %s\r\n"
1749 "m=message %d sip null\r\n"
1750 "a=accept-types:text/plain text/html image/gif "
1751 "multipart/alternative application/im-iscomposing+xml\r\n",
1752 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), sip
->realport
);
1754 session
->outgoing_invite
= send_sip_request(sip
->gc
, "INVITE",
1755 to
, to
, hdr
, body
, session
->dialog
, process_invite_response
);
1764 im_session_close (struct sipe_account_data
*sip
, struct sip_im_session
* session
)
1767 send_sip_request(sip
->gc
, "BYE", session
->with
, session
->with
, NULL
, NULL
, session
->dialog
, NULL
);
1768 im_session_destroy(sip
, session
);
1773 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
1775 struct sipe_account_data
*sip
= gc
->proto_data
;
1777 purple_debug_info("sipe", "conversation with %s closed\n", who
);
1778 im_session_close(sip
, find_im_session(sip
, who
));
1782 im_session_close_all (struct sipe_account_data
*sip
)
1784 GSList
*entry
= sip
->im_sessions
;
1786 im_session_close (sip
, entry
->data
);
1787 entry
= sip
->im_sessions
;
1791 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
1793 struct sipe_account_data
*sip
= gc
->proto_data
;
1794 char *to
= g_strdup(who
);
1795 char *text
= purple_unescape_html(what
);
1797 struct sip_im_session
* session
= find_or_create_im_session(sip
, who
);
1799 // Queue the message
1800 session
->outgoing_message_queue
= g_slist_append(session
->outgoing_message_queue
, text
);
1802 if (session
->dialog
&& session
->dialog
->callid
) {
1803 sipe_im_process_queue(sip
, session
);
1804 } else if (!session
->outgoing_invite
) {
1805 // Need to send the INVITE to get the outgoing dialog setup
1806 sipe_invite(sip
, session
, text
);
1814 /* End IM Session (INVITE and MESSAGE methods) */
1817 sipe_send_typing(PurpleConnection
*gc
, const char *who
, PurpleTypingState state
)
1819 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
1821 if (state
== PURPLE_NOT_TYPING
)
1824 struct sip_im_session
* session
= find_im_session(sip
, who
);
1826 if (session
&& session
->dialog
) {
1827 send_sip_request(gc
, "INFO", who
, who
,
1828 "Content-Type: application/xml\r\n",
1829 SIPE_SEND_TYPING
, session
->dialog
, NULL
);
1832 return SIPE_TYPING_SEND_TIMEOUT
;
1836 static void sipe_buddy_resub(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1838 time_t curtime
= time(NULL
);
1839 if (buddy
->resubscribe
< curtime
) {
1840 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_buddy_resub %s\n", name
);
1841 sipe_subscribe(sip
, buddy
);
1845 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
1847 GSList
*tmp
= sip
->transactions
;
1848 time_t currtime
= time(NULL
);
1850 struct transaction
*trans
= tmp
->data
;
1852 purple_debug_info("sipe", "have open transaction age: %d\n", currtime
- trans
->time
);
1853 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
1856 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
1858 sendout_sipmsg(sip
, trans
->msg
);
1865 static gboolean
subscribe_timeout(struct sipe_account_data
*sip
)
1868 time_t curtime
= time(NULL
);
1869 /* register again if first registration expires */
1870 if (sip
->reregister
< curtime
) {
1873 /* check for every subscription if we need to resubscribe */
1874 //Fixxxer we need resub?
1875 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_resub
, (gpointer
)sip
);
1877 /* remove a timed out suscriber */
1881 struct sipe_watcher
*watcher
= tmp
->data
;
1882 if (watcher
->expire
< curtime
) {
1883 watcher_remove(sip
, watcher
->name
);
1886 if (tmp
) tmp
= tmp
->next
;
1892 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1896 gboolean found
= FALSE
;
1898 from
= parse_from(sipmsg_find_header(msg
, "From"));
1902 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
1904 contenttype
= sipmsg_find_header(msg
, "Content-Type");
1905 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
1906 serv_got_im(sip
->gc
, from
, msg
->body
, 0, time(NULL
));
1907 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1910 if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
1911 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1916 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1920 state
= xmlnode_get_child(isc
, "state");
1923 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1928 statedata
= xmlnode_get_data(state
);
1930 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
1931 else serv_got_typing_stopped(sip
->gc
, from
);
1936 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1940 purple_debug_info("sipe", "got unknown mime-type");
1941 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
1946 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1948 // Only accept text invitations
1949 if (msg
->body
&& !strstr(msg
->body
, "m=message")) {
1950 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
1954 gchar
* from
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "<", ">", NULL
);
1955 struct sip_im_session
* session
= find_or_create_im_session (sip
, from
);
1957 if (session
->dialog
) {
1958 purple_debug_info("sipe", "process_incoming_invite, session already has dialog!\n");
1960 session
->dialog
= g_new0(struct sip_dialog
, 1);
1962 sipe_parse_dialog(msg
, session
->dialog
, FALSE
);
1964 session
->dialog
->callid
= sipmsg_find_header(msg
, "Call-ID");
1965 session
->dialog
->ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
1966 session
->dialog
->theirtag
= find_tag(sipmsg_find_header(msg
, "From"));
1967 session
->dialog
->theirepid
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "From"), "epid=", NULL
, NULL
);
1970 purple_debug_info("sipe", "process_incoming_invite, failed to find or create IM session\n");
1974 send_sip_response(sip
->gc
, msg
, 200, "OK", g_strdup_printf(
1976 "o=- 0 0 IN IP4 %s\r\n"
1980 "m=message %d sip sip:%s\r\n"
1981 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml\r\n",
1982 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
1983 sip
->realport
, sip
->username
));
1986 static void sipe_connection_cleanup(struct sipe_account_data
*);
1987 static void create_connection(struct sipe_account_data
*, gchar
*, int);
1989 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1991 gchar
*tmp
, krb5_token
;
1992 const gchar
*expires_header
;
1995 expires_header
= sipmsg_find_header(msg
, "Expires");
1996 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
1997 purple_debug_info("sipe", "process_register_response: got response to REGISTER; expires = %d\n", expires
);
1999 switch (msg
->response
) {
2002 sip
->registerstatus
= 0;
2004 sip
->registerstatus
= 3;
2005 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
2007 gchar
*gruu
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Contact"), "gruu=\"", "\"", NULL
);
2009 sip
->contact
= g_strdup_printf("<%s>", gruu
);
2011 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
);
2014 /* get buddies from blist; Has a bug */
2015 subscribe_timeout(sip
);
2017 tmp
= sipmsg_find_header(msg
, "Allow-Events");
2018 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
2019 sipe_subscribe_buddylist(sip
, msg
);
2022 sipe_subscribe_roaming_self(sip
, msg
);
2023 sipe_subscribe_roaming_provisioning(sip
, msg
);
2024 sipe_set_status(sip
->account
, purple_account_get_active_status(sip
->account
));
2026 // Should we remove the transaction here?
2027 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
2028 transactions_remove(sip
, tc
);
2033 gchar
*redirect
= parse_from(sipmsg_find_header(msg
, "Contact"));
2035 if (redirect
&& (g_strncasecmp("sip:", redirect
, 4) == 0)) {
2036 gchar
**parts
= g_strsplit(redirect
+ 4, ";", 0);
2040 sipe_transport_type transport
= SIPE_TRANSPORT_TLS
;
2043 tmp
= g_strsplit(parts
[0], ":", 0);
2044 hostname
= g_strdup(tmp
[0]);
2045 if (tmp
[1]) port
= strtoul(tmp
[1], NULL
, 10);
2049 tmp
= g_strsplit(parts
[i
], "=", 0);
2051 if (g_strcasecmp("transport", tmp
[0]) == 0) {
2052 if (g_strcasecmp("tcp", tmp
[1]) == 0) {
2053 transport
= SIPE_TRANSPORT_TCP
;
2054 } else if (g_strcasecmp("udp", tmp
[1]) == 0) {
2055 transport
= SIPE_TRANSPORT_UDP
;
2064 /* Close old connection */
2065 sipe_connection_cleanup(sip
);
2067 /* Create new connection */
2068 sip
->transport
= transport
;
2069 purple_debug_info("sipe", "process_register_response: redirected to host %s port %d transport %s\n",
2070 hostname
, port
, TRANSPORT_DESCRIPTOR
);
2071 create_connection(sip
, hostname
, port
);
2076 if (sip
->registerstatus
!= 2) {
2077 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
2078 if (sip
->registrar
.retries
> 3) {
2079 sip
->gc
->wants_to_die
= TRUE
;
2080 purple_connection_error(sip
->gc
, _("Wrong Password"));
2083 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2084 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
2086 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2088 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
2089 fill_auth(sip
, tmp
, &sip
->registrar
);
2090 sip
->registerstatus
= 2;
2091 if (sip
->account
->disconnecting
) {
2092 do_register_exp(sip
, 0);
2100 const gchar
*warning
= sipmsg_find_header(msg
, "Warning");
2101 if (warning
!= NULL
) {
2103 Warning: 310 lcs.microsoft.com "You are currently not using the recommended version of the client"
2105 gchar
**tmp
= g_strsplit(warning
, "\"", 0);
2106 warning
= g_strdup_printf("You have been rejected by the server: %s", tmp
[1] ? tmp
[1] : "no reason given");
2109 warning
= _("You have been rejected by the server");
2112 sip
->gc
->wants_to_die
= TRUE
;
2113 purple_connection_error(sip
->gc
, warning
);
2121 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2126 gchar
*activity
= g_strdup("available");
2128 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
2129 gboolean isonline
= FALSE
;
2131 fromhdr
= sipmsg_find_header(msg
, "From");
2132 from
= parse_from(fromhdr
);
2138 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2140 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
2144 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
2145 if ((status
= xmlnode_get_child(tuple
, "status"))) {
2146 basicstatus
= xmlnode_get_child(status
, "basic");
2151 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
2156 tmp2
= xmlnode_get_data(basicstatus
);
2158 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
2163 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n", tmp2
);
2164 if (strstr(tmp2
, "open")) {
2168 xmlnode
*display_name_node
= xmlnode_get_child(pidf
, "display-name");
2169 if (display_name_node
) {
2170 PurpleBuddy
* buddy
= purple_find_buddy (sip
->account
, from
);
2171 char * display_name
= xmlnode_get_data(display_name_node
);
2172 if (buddy
&& display_name
) {
2173 purple_blist_server_alias_buddy (buddy
, g_strdup(display_name
));
2177 if ((tuple
= xmlnode_get_child(pidf
, "tuple"))) {
2178 if ((status
= xmlnode_get_child(tuple
, "status"))) {
2179 if (basicstatus
= xmlnode_get_child(status
, "activities")) {
2180 if (basicstatus
= xmlnode_get_child(basicstatus
, "activity")) {
2181 activity
= xmlnode_get_data(basicstatus
);
2186 purple_debug_info("sipe", "process_incoming_notify: activity(%s)\n", activity
);
2189 gchar
* status_id
= NULL
;
2191 if (strstr(activity
, "busy")) {
2193 } else if (strstr(activity
, "away")) {
2199 status_id
= "available";
2202 purple_debug_info("sipe", "process_incoming_notify: status_id(%s)\n", status_id
);
2203 purple_prpl_got_user_status(sip
->account
, from
, status_id
, NULL
);
2205 purple_prpl_got_user_status(sip
->account
, from
, "offline", NULL
);
2213 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2216 static void process_incoming_benotify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2218 xmlnode
*xml
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
2221 char * delta_num
= xmlnode_get_attrib(xml
, "deltaNum");
2223 sip
->delta_num
= (int)g_ascii_strtod(delta_num
, NULL
);
2230 static gchar
* gen_xpidf(struct sipe_account_data
*sip
)
2232 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
2234 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
2235 "<display name=\"sip:%s\"/>\r\n"
2236 "<atom id=\"1234\">\r\n"
2237 "<address uri=\"sip:%s\">\r\n"
2238 "<status status=\"%s\"/>\r\n"
2251 static gchar
* gen_pidf(struct sipe_account_data
*sip
)
2253 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
2254 "<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"
2255 "<tuple id=\"0\">\r\n"
2257 "<basic>open</basic>\r\n"
2258 "<ep:activities>\r\n"
2259 " <ep:activity>%s</ep:activity>\r\n"
2263 "<ci:display-name>%s</ci:display-name>\r\n"
2271 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*watcher
)
2273 gchar
*doc
= watcher
->needsxpidf
? gen_xpidf(sip
) : gen_pidf(sip
);
2274 gchar
*hdr
= watcher
->needsxpidf
? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
2275 send_sip_request(sip
->gc
, "NOTIFY", watcher
->name
, watcher
->name
, hdr
, doc
, &watcher
->dialog
, NULL
);
2279 static gboolean
process_service_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2282 if (msg
->response
!= 200 && msg
->response
!= 408) {
2283 /* never send again */
2284 sip
->republish
= -1;
2289 static void send_clear_notes(struct sipe_account_data
*sip
)
2294 process_send_presence_info_v0_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2296 if (msg
->response
== 488) {
2297 sip
->presence_method_version
= 1;
2298 send_presence_info(sip
);
2303 static void send_presence_info_v0(struct sipe_account_data
*sip
, char * note
)
2306 if (!strcmp(sip
->status
, "away")) {
2308 } else if (!strcmp(sip
->status
, "busy")) {
2315 gchar
*name
= g_strdup_printf("sip: sip:%s", sip
->username
);
2316 gchar
* body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
, name
, 200, code
, note
);
2317 send_soap_request_with_cb(sip
, body
, process_send_presence_info_v0_response
, NULL
);
2323 process_clear_presence_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2325 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
2326 if (msg
->response
== 200) {
2327 sip
->status_version
= 0;
2328 send_presence_info(sip
);
2334 process_send_presence_info_v1_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
2336 if (msg
->response
== 409) {
2337 // Version(s) of presence info were out of date; tell the server to clear them, then we'll try again
2338 // TODO need to parse the version #'s?
2339 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
2340 gchar
*doc
= g_strdup_printf(SIPE_SEND_CLEAR_PRESENCE
, uri
);
2342 gchar
*tmp
= get_contact(sip
);
2343 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
2344 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
2346 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_clear_presence_response
);
2356 static void send_presence_info_v1(struct sipe_account_data
*sip
, char * note
)
2359 if (!strcmp(sip
->status
, "away")) {
2361 } else if (!strcmp(sip
->status
, "busy")) {
2368 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
2369 gchar
*doc
= g_strdup_printf(SIPE_SEND_PRESENCE
, uri
,
2370 sip
->status_version
, code
,
2371 sip
->status_version
, code
,
2372 sip
->status_version
, note
? note
: "",
2373 sip
->status_version
, note
? note
: "",
2374 sip
->status_version
, note
? note
: ""
2376 sip
->status_version
++;
2378 gchar
*tmp
= get_contact(sip
);
2379 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
2380 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
2382 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_send_presence_info_v1_response
);
2390 static void send_presence_info(struct sipe_account_data
*sip
)
2392 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
2393 if (!status
) return;
2395 char *note
= purple_status_get_attr_string(status
, "message");
2397 purple_debug_info("sipe", "sending presence info, version = %d\n", sip
->presence_method_version
);
2398 if (sip
->presence_method_version
!= 1) {
2399 send_presence_info_v0(sip
, note
);
2401 send_presence_info_v1(sip
, note
);
2405 static void send_service(struct sipe_account_data
*sip
)
2407 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
2408 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
2409 //gchar *doc = gen_pidf(sip);
2411 gchar
*doc
= gen_pidf(sip
);
2412 gchar
*hdr
= g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
2414 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
2415 gchar
*tmp
= get_contact(sip
);
2416 hdr
= g_strdup_printf("Contact: %s\r\n%s; +sip.instance=\"<urn:uuid:%s>\"", tmp
, hdr
,generateUUIDfromEPID(get_epid()));
2418 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
,
2420 doc
, NULL
, process_service_response
);
2421 sip
->republish
= time(NULL
) + 500;
2427 static void process_incoming_subscribe(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
2429 const char *from_hdr
= sipmsg_find_header(msg
, "From");
2430 gchar
*from
= parse_from(from_hdr
);
2431 gchar
*theirtag
= find_tag(from_hdr
);
2432 gchar
*ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
2433 gboolean tagadded
= FALSE
;
2434 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
2435 gchar
*expire
= sipmsg_find_header(msg
, "Expire");
2437 struct sipe_watcher
*watcher
= watcher_find(sip
, from
);
2442 if (!watcher
) { /* new subscription */
2443 gchar
*acceptheader
= sipmsg_find_header(msg
, "Accept");
2444 gboolean needsxpidf
= FALSE
;
2445 if (!purple_privacy_check(sip
->account
, from
)) {
2446 send_sip_response(sip
->gc
, msg
, 202, "Ok", NULL
);
2450 gchar
*tmp
= acceptheader
;
2451 gboolean foundpidf
= FALSE
;
2452 gboolean foundxpidf
= FALSE
;
2453 while (tmp
&& tmp
< acceptheader
+ strlen(acceptheader
)) {
2454 gchar
*tmp2
= strchr(tmp
, ',');
2455 if (tmp2
) *tmp2
= '\0';
2456 if (!strcmp("application/pidf+xml", tmp
))
2458 if (!strcmp("application/xpidf+xml", tmp
))
2463 while (*tmp
== ' ') tmp
++;
2467 if (!foundpidf
&& foundxpidf
) needsxpidf
= TRUE
;
2468 g_free(acceptheader
);
2470 watcher
= watcher_create(sip
, from
, callid
, ourtag
, theirtag
, needsxpidf
);
2473 gchar
*to
= g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg
, "To"), ourtag
);
2474 sipmsg_remove_header(msg
, "To");
2475 sipmsg_add_header(msg
, "To", to
);
2479 watcher
->expire
= time(NULL
) + strtol(expire
, NULL
, 10);
2481 watcher
->expire
= time(NULL
) + 600;
2483 sipmsg_remove_header(msg
, "Contact");
2484 tmp
= get_contact(sip
);
2485 sipmsg_add_header(msg
, "Contact", tmp
);
2487 purple_debug_info("sipe", "got subscribe: name %s ourtag %s theirtag %s callid %s\n", watcher
->name
, watcher
->dialog
.ourtag
, watcher
->dialog
.theirtag
, watcher
->dialog
.callid
);
2488 send_sip_response(sip
->gc
, msg
, 200, "Ok", NULL
);
2489 send_notify(sip
, watcher
);
2498 static void process_input_message(struct sipe_account_data
*sip
,struct sipmsg
*msg
)
2500 gboolean found
= FALSE
;
2501 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
2502 if (msg
->response
== 0) { /* request */
2503 if (!strcmp(msg
->method
, "MESSAGE")) {
2504 process_incoming_message(sip
, msg
);
2506 } else if (!strcmp(msg
->method
, "NOTIFY")) {
2507 purple_debug_info("sipe","send->process_incoming_notify\n");
2508 process_incoming_notify(sip
, msg
);
2510 } else if (!strcmp(msg
->method
, "BENOTIFY")) {
2511 purple_debug_info("sipe","send->process_incoming_benotify\n");
2512 process_incoming_benotify(sip
, msg
);
2514 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
2515 purple_debug_info("sipe","send->process_incoming_subscribe\n");
2516 process_incoming_subscribe(sip
, msg
);
2518 } else if (!strcmp(msg
->method
, "INVITE")) {
2519 process_incoming_invite(sip
, msg
);
2521 } else if (!strcmp(msg
->method
, "INFO")) {
2523 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
2525 serv_got_typing(sip
->gc
, from
, SIPE_TYPING_RECV_TIMEOUT
, PURPLE_TYPING
);
2527 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2529 } else if (!strcmp(msg
->method
, "ACK")) {
2530 // ACK's don't need any response
2532 } else if (!strcmp(msg
->method
, "BYE")) {
2533 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
2535 gchar
* from
= parse_from(sipmsg_find_header(msg
, "From"));
2536 struct sip_im_session
* session
= find_im_session (sip
, from
);
2540 // TODO Let the user know the other user left the conversation?
2541 im_session_destroy(sip
, session
);
2546 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
2548 } else { /* response */
2549 struct transaction
*trans
= transactions_find(sip
, msg
);
2551 if (msg
->response
== 407) {
2552 gchar
*resend
, *auth
, *ptmp
;
2554 if (sip
->proxy
.retries
> 30) return;
2555 sip
->proxy
.retries
++;
2556 /* do proxy authentication */
2558 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
2560 fill_auth(sip
, ptmp
, &sip
->proxy
);
2561 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
, TRUE
);
2562 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
2563 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
2565 resend
= sipmsg_to_string(trans
->msg
);
2566 /* resend request */
2567 sendout_pkt(sip
->gc
, resend
);
2570 if (msg
->response
== 100 || msg
->response
== 180) {
2571 /* ignore provisional response */
2572 purple_debug_info("sipe", "got trying (%d) response\n", msg
->response
);
2574 sip
->proxy
.retries
= 0;
2575 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
2576 if (msg
->response
== 401) sip
->registrar
.retries
++;
2577 else sip
->registrar
.retries
= 0;
2578 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
2580 if (msg
->response
== 401) {
2581 gchar
*resend
, *auth
, *ptmp
;
2583 if (sip
->registrar
.retries
> 4) return;
2584 sip
->registrar
.retries
++;
2586 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
2587 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
2589 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
2592 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
2594 fill_auth(sip
, ptmp
, &sip
->registrar
);
2595 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
, TRUE
);
2596 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
2597 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
2599 //sipmsg_remove_header(trans->msg, "Authorization");
2600 //sipmsg_add_header(trans->msg, "Authorization", auth);
2602 resend
= sipmsg_to_string(trans
->msg
);
2603 /* resend request */
2604 sendout_pkt(sip
->gc
, resend
);
2609 if (trans
->callback
) {
2610 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
2611 /* call the callback to process response*/
2612 (trans
->callback
)(sip
, msg
, trans
);
2614 /* Not sure if this is needed or what needs to be done
2615 but transactions seem to be removed prematurely so
2616 this only removes them if the response is 200 OK */
2617 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
2618 /*Has a bug and it's unneccesary*/
2619 /*transactions_remove(sip, trans);*/
2625 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
2629 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
2633 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
2641 /* according to the RFC remove CRLF at the beginning */
2642 while (*cur
== '\r' || *cur
== '\n') {
2645 if (cur
!= conn
->inbuf
) {
2646 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
2647 conn
->inbufused
= strlen(conn
->inbuf
);
2650 /* Received a full Header? */
2651 while ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
) {
2652 time_t currtime
= time(NULL
);
2655 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
2656 msg
= sipmsg_parse_header(conn
->inbuf
);
2659 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
2660 if (restlen
>= msg
->bodylen
) {
2661 dummy
= g_malloc(msg
->bodylen
+ 1);
2662 memcpy(dummy
, cur
, msg
->bodylen
);
2663 dummy
[msg
->bodylen
] = '\0';
2665 cur
+= msg
->bodylen
;
2666 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
2667 conn
->inbufused
= strlen(conn
->inbuf
);
2674 purple_debug_info("sipe", "body:\n%s", msg
->body
);
2677 // Verify the signature before processing it
2678 if (sip
->registrar
.ntlm_key
) {
2679 struct sipmsg_breakdown msgbd
;
2681 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
2682 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
2684 if (signature_input_str
!= NULL
) {
2685 signature
= purple_ntlm_sipe_signature_make (signature_input_str
, sip
->registrar
.ntlm_key
);
2688 gchar
* rspauth
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
);
2689 if (signature
!= NULL
&& rspauth
!= NULL
) {
2690 if (purple_ntlm_verify_signature (signature
, rspauth
)) {
2691 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature validated\n");
2692 process_input_message(sip
, msg
);
2694 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "incoming message's signature is invalid. Received %s but generated %s; Ignoring message\n", rspauth
, signature
);
2695 purple_connection_error(sip
->gc
, _("Invalid message signature received"));
2696 sip
->gc
->wants_to_die
= TRUE
;
2700 sipmsg_breakdown_free(&msgbd
);
2702 process_input_message(sip
, msg
);
2707 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
2709 PurpleConnection
*gc
= data
;
2710 struct sipe_account_data
*sip
= gc
->proto_data
;
2715 static char buffer
[65536];
2716 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
2718 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
2719 msg
= sipmsg_parse_msg(buffer
);
2720 if (msg
) process_input_message(sip
, msg
);
2724 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2726 PurpleConnection
*gc
= data
;
2727 struct sipe_account_data
*sip
= gc
->proto_data
;
2728 struct sip_connection
*conn
= NULL
;
2730 static char buf
[4096];
2732 /* TODO: It should be possible to make this check unnecessary */
2733 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
2734 purple_ssl_close(gsc
);
2739 conn
= connection_find(sip
, gsc
->fd
);
2741 purple_debug_error("sipe", "Connection not found; Please try to connect again.\n");
2742 if (sip
->fd
!= -1) purple_ssl_close(gsc
);
2743 purple_connection_error(sip
->gc
, _("Connection not found; Please try to connect again.\n"));
2744 sip
->gc
->wants_to_die
= TRUE
;
2748 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2749 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2750 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2753 len
= purple_ssl_read(sip
->gsc
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2755 if (len
< 0 && errno
== EAGAIN
) {
2756 /* Try again later */
2758 } else if (len
< 0) {
2759 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
2761 connection_remove(sip
, sip
->gsc
->fd
);
2762 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
2763 purple_debug_info("sipe", "sipe_input_cb_ssl: connection_remove\n");
2766 } else if (len
== 0) {
2767 purple_connection_error(gc
, _("Server has disconnected"));
2769 connection_remove(sip
, sip
->gsc
->fd
);
2770 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
2775 conn
->inbufused
+= len
;
2776 conn
->inbuf
[conn
->inbufused
] = '\0';
2778 process_input(sip
, conn
);
2782 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2784 PurpleConnection
*gc
= data
;
2785 struct sipe_account_data
*sip
= gc
->proto_data
;
2787 struct sip_connection
*conn
= connection_find(sip
, source
);
2789 purple_debug_error("sipe", "Connection not found!\n");
2793 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
2794 conn
->inbuflen
+= SIMPLE_BUF_INC
;
2795 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
2798 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2800 if (len
< 0 && errno
== EAGAIN
)
2802 else if (len
<= 0) {
2803 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2804 connection_remove(sip
, source
);
2805 if (sip
->fd
== source
) sip
->fd
= -1;
2809 conn
->inbufused
+= len
;
2810 conn
->inbuf
[conn
->inbufused
] = '\0';
2812 process_input(sip
, conn
);
2815 /* Callback for new connections on incoming TCP port */
2816 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2818 PurpleConnection
*gc
= data
;
2819 struct sipe_account_data
*sip
= gc
->proto_data
;
2820 struct sip_connection
*conn
;
2822 int newfd
= accept(source
, NULL
, NULL
);
2824 conn
= connection_create(sip
, newfd
);
2826 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2829 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
2831 PurpleConnection
*gc
= data
;
2832 struct sipe_account_data
*sip
;
2833 struct sip_connection
*conn
;
2835 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2843 purple_connection_error(gc
, _("Could not connect"));
2847 sip
= gc
->proto_data
;
2850 conn
= connection_create(sip
, source
);
2852 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2856 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2859 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2861 PurpleConnection
*gc
= data
;
2862 struct sipe_account_data
*sip
;
2863 struct sip_connection
*conn
;
2865 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2867 if (gsc
) purple_ssl_close(gsc
);
2871 sip
= gc
->proto_data
;
2874 conn
= connection_create(sip
, sip
->fd
);
2875 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2876 sip
->listenfd
= sip
->fd
;
2877 sip
->registertimeout
= purple_timeout_add((rand()%100) + 1000, (GSourceFunc
)subscribe_timeout
, sip
);
2881 purple_ssl_input_add(sip
->gsc
, sipe_input_cb_ssl
, gc
);
2884 static guint
sipe_ht_hash_nick(const char *nick
)
2886 char *lc
= g_utf8_strdown(nick
, -1);
2887 guint bucket
= g_str_hash(lc
);
2893 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
2895 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
2898 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
2900 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2902 sip
->listen_data
= NULL
;
2904 if (listenfd
== -1) {
2905 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2911 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2912 sip
->listenfd
= sip
->fd
;
2914 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
2916 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
2917 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2921 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
2923 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2926 sip
->query_data
= NULL
;
2928 if (!hosts
|| !hosts
->data
) {
2929 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
2933 addr_size
= GPOINTER_TO_INT(hosts
->data
);
2934 hosts
= g_slist_remove(hosts
, hosts
->data
);
2935 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
2936 g_free(hosts
->data
);
2937 hosts
= g_slist_remove(hosts
, hosts
->data
);
2939 hosts
= g_slist_remove(hosts
, hosts
->data
);
2940 g_free(hosts
->data
);
2941 hosts
= g_slist_remove(hosts
, hosts
->data
);
2944 /* create socket for incoming connections */
2945 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
2946 sipe_udp_host_resolved_listen_cb
, sip
);
2947 if (sip
->listen_data
== NULL
) {
2948 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2953 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
2956 PurpleConnection
*gc
= data
;
2957 struct sipe_account_data
*sip
;
2959 /* If the connection is already disconnected, we don't need to do anything else */
2960 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2963 sip
= gc
->proto_data
;
2967 case PURPLE_SSL_CONNECT_FAILED
:
2968 purple_connection_error(gc
, _("Connection Failed"));
2970 case PURPLE_SSL_HANDSHAKE_FAILED
:
2971 purple_connection_error(gc
, _("SSL Handshake Failed"));
2977 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
2979 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2980 PurpleProxyConnectData
*connect_data
;
2982 sip
->listen_data
= NULL
;
2984 sip
->listenfd
= listenfd
;
2985 if (sip
->listenfd
== -1) {
2986 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2990 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
2991 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2992 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
2993 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
2994 sipe_newconn_cb
, sip
->gc
);
2995 purple_debug_info("sipe", "connecting to %s port %d\n",
2996 sip
->realhostname
, sip
->realport
);
2997 /* open tcp connection to the server */
2998 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
2999 sip
->realport
, login_cb
, sip
->gc
);
3001 if (connect_data
== NULL
) {
3002 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
3007 static void create_connection(struct sipe_account_data
*sip
, gchar
*hostname
, int port
)
3009 PurpleAccount
*account
= sip
->account
;
3010 PurpleConnection
*gc
= sip
->gc
;
3012 if (purple_account_get_bool(account
, "useport", FALSE
)) {
3013 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - using specified SIP port\n");
3014 port
= purple_account_get_int(account
, "port", 0);
3016 port
= port
? port
: (sip
->transport
== SIPE_TRANSPORT_TLS
) ? 5061 : 5060;
3019 sip
->realhostname
= hostname
;
3020 sip
->realport
= port
;
3022 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "create_connection - hostname: %s port: %d\n",
3025 /* TODO: is there a good default grow size? */
3026 if (sip
->transport
!= SIPE_TRANSPORT_UDP
)
3027 sip
->txbuf
= purple_circ_buffer_new(0);
3029 if (sip
->transport
== SIPE_TRANSPORT_TLS
) {
3031 if (!purple_ssl_is_supported()) {
3032 gc
->wants_to_die
= TRUE
;
3033 purple_connection_error(gc
, _("SSL support is not installed. Either install SSL support or configure a different connection type in the account editor."));
3037 purple_debug_info("sipe", "using SSL\n");
3038 sip
->gsc
= purple_ssl_connect(account
, hostname
, port
,
3039 login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
3040 if (sip
->gsc
== NULL
) {
3041 purple_connection_error(gc
, _("Could not create SSL context"));
3044 } else if (sip
->transport
== SIPE_TRANSPORT_UDP
) {
3046 purple_debug_info("sipe", "using UDP\n");
3048 sip
->query_data
= purple_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
3049 if (sip
->query_data
== NULL
) {
3050 purple_connection_error(gc
, _("Could not resolve hostname"));
3054 purple_debug_info("sipe", "using TCP\n");
3055 /* create socket for incoming connections */
3056 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_STREAM
,
3057 sipe_tcp_connect_listen_cb
, sip
);
3058 if (sip
->listen_data
== NULL
) {
3059 purple_connection_error(gc
, _("Could not create listen socket"));
3065 /* Service list for autodection */
3066 static const struct sipe_service_data service_autodetect
[] = {
3067 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
3068 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
3069 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
3070 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
3074 /* Service list for SSL/TLS */
3075 static const struct sipe_service_data service_tls
[] = {
3076 { "sipinternaltls", "tcp", SIPE_TRANSPORT_TLS
}, /* for internal TLS connections */
3077 { "sip", "tls", SIPE_TRANSPORT_TLS
}, /* for external TLS connections */
3081 /* Service list for TCP */
3082 static const struct sipe_service_data service_tcp
[] = {
3083 { "sipinternal", "tcp", SIPE_TRANSPORT_TCP
}, /* for internal TCP connections */
3084 { "sip", "tcp", SIPE_TRANSPORT_TCP
}, /*.for external TCP connections */
3088 /* Service list for UDP */
3089 static const struct sipe_service_data service_udp
[] = {
3090 { "sip", "udp", SIPE_TRANSPORT_UDP
},
3094 static void srvresolved(PurpleSrvResponse
*, int, gpointer
);
3095 static void resolve_next_service(struct sipe_account_data
*sip
,
3096 const struct sipe_service_data
*start
)
3099 sip
->service_data
= start
;
3101 sip
->service_data
++;
3102 if (sip
->service_data
->service
== NULL
) {
3103 /* Try connecting to the SIP hostname directly */
3104 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "no SRV records found; using SIP domain as fallback\n");
3105 // If SSL is supported, default to using it; OCS servers aren't configured
3106 // by default to accept TCP
3107 sip
->transport
= purple_ssl_is_supported() ? SIPE_TRANSPORT_TLS
: SIPE_TRANSPORT_TCP
;
3108 gchar
* hostname
= g_strdup(sip
->sipdomain
);
3109 create_connection(sip
, hostname
, 0);
3114 /* Try to resolve next service */
3115 sip
->srv_query_data
= purple_srv_resolve(sip
->service_data
->service
,
3116 sip
->service_data
->transport
,
3121 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
3123 struct sipe_account_data
*sip
= data
;
3125 sip
->srv_query_data
= NULL
;
3127 /* find the host to connect to */
3129 gchar
*hostname
= g_strdup(resp
->hostname
);
3130 int port
= resp
->port
;
3131 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s port: %d\n",
3135 sip
->transport
= sip
->service_data
->type
;
3137 create_connection(sip
, hostname
, port
);
3139 resolve_next_service(sip
, NULL
);
3143 static void sipe_login(PurpleAccount
*account
)
3145 PurpleConnection
*gc
;
3146 struct sipe_account_data
*sip
;
3148 const char *transport
;
3150 const char *username
= purple_account_get_username(account
);
3151 gc
= purple_account_get_connection(account
);
3153 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
3154 gc
->wants_to_die
= TRUE
;
3155 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
3159 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
3161 sip
->account
= account
;
3162 sip
->registerexpire
= 900;
3164 userserver
= g_strsplit(username
, "@", 2);
3165 purple_connection_set_display_name(gc
, userserver
[0]);
3166 sip
->username
= g_strdup(g_strjoin("@", userserver
[0], userserver
[1], NULL
));
3167 sip
->sipdomain
= g_strdup(userserver
[1]);
3168 sip
->password
= g_strdup(purple_connection_get_password(gc
));
3169 g_strfreev(userserver
);
3171 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
3173 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
3175 /* TODO: Set the status correctly. */
3176 sip
->status
= g_strdup("available");
3178 transport
= purple_account_get_string(account
, "transport", "auto");
3180 if (purple_account_get_bool(account
, "useproxy", FALSE
)) {
3181 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - using specified SIP proxy\n");
3182 sip
->transport
= (strcmp(transport
, "tls") == 0) ? SIPE_TRANSPORT_TLS
:
3183 (strcmp(transport
, "tcp") == 0) ? SIPE_TRANSPORT_TCP
:
3185 create_connection(sip
, g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
)), 0);
3186 } else if (strcmp(transport
, "auto") == 0) {
3187 resolve_next_service(sip
, purple_ssl_is_supported() ? service_autodetect
: service_tcp
);
3188 } else if (strcmp(transport
, "tls") == 0) {
3189 resolve_next_service(sip
, service_tls
);
3190 } else if (strcmp(transport
, "tcp") == 0) {
3191 resolve_next_service(sip
, service_tcp
);
3193 resolve_next_service(sip
, service_udp
);
3197 static void sipe_connection_cleanup(struct sipe_account_data
*sip
)
3199 connection_free_all(sip
);
3201 if (sip
->query_data
!= NULL
)
3202 purple_dnsquery_destroy(sip
->query_data
);
3203 sip
->query_data
== NULL
;
3205 if (sip
->srv_query_data
!= NULL
)
3206 purple_srv_cancel(sip
->srv_query_data
);
3207 sip
->srv_query_data
= NULL
;
3209 if (sip
->listen_data
!= NULL
)
3210 purple_network_listen_cancel(sip
->listen_data
);
3211 sip
->listen_data
= NULL
;
3213 g_free(sip
->registrar
.nonce
);
3214 sip
->registrar
.nonce
= NULL
;
3215 g_free(sip
->registrar
.opaque
);
3216 sip
->registrar
.opaque
= NULL
;
3217 g_free(sip
->registrar
.realm
);
3218 sip
->registrar
.realm
= NULL
;
3219 g_free(sip
->registrar
.target
);
3220 sip
->registrar
.target
= NULL
;
3221 g_free(sip
->registrar
.digest_session_key
);
3222 sip
->registrar
.digest_session_key
= NULL
;
3223 g_free(sip
->registrar
.ntlm_key
);
3224 sip
->registrar
.ntlm_key
= NULL
;
3225 sip
->registrar
.type
= 0;
3226 sip
->registrar
.retries
= 0;
3228 g_free(sip
->proxy
.nonce
);
3229 sip
->proxy
.nonce
= NULL
;
3230 g_free(sip
->proxy
.opaque
);
3231 sip
->proxy
.opaque
= NULL
;
3232 g_free(sip
->proxy
.realm
);
3233 sip
->proxy
.realm
= NULL
;
3234 g_free(sip
->proxy
.target
);
3235 sip
->proxy
.target
= NULL
;
3236 g_free(sip
->proxy
.digest_session_key
);
3237 sip
->proxy
.digest_session_key
= NULL
;
3238 g_free(sip
->proxy
.ntlm_key
);
3239 sip
->proxy
.ntlm_key
= NULL
;
3240 sip
->proxy
.type
= 0;
3241 sip
->proxy
.retries
= 0;
3244 purple_circ_buffer_destroy(sip
->txbuf
);
3247 g_free(sip
->realhostname
);
3248 sip
->realhostname
= NULL
;
3251 purple_input_remove(sip
->listenpa
);
3253 if (sip
->tx_handler
)
3254 purple_input_remove(sip
->tx_handler
);
3255 sip
->tx_handler
= 0;
3256 if (sip
->resendtimeout
)
3257 purple_timeout_remove(sip
->resendtimeout
);
3258 sip
->resendtimeout
= 0;
3259 if (sip
->registertimeout
)
3260 purple_timeout_remove(sip
->registertimeout
);
3261 sip
->registertimeout
= 0;
3264 static void sipe_close(PurpleConnection
*gc
)
3266 struct sipe_account_data
*sip
= gc
->proto_data
;
3269 /* leave all conversations */
3270 im_session_close_all(sip
);
3273 do_register_exp(sip
, 0);
3275 sipe_connection_cleanup(sip
);
3276 g_free(sip
->sipdomain
);
3277 g_free(sip
->username
);
3278 g_free(sip
->password
);
3280 g_free(gc
->proto_data
);
3281 gc
->proto_data
= NULL
;
3284 /* not needed since privacy is checked for every subscribe */
3285 static void dummy_add_deny(PurpleConnection
*gc
, const char *name
) {
3288 static void dummy_permit_deny(PurpleConnection
*gc
)
3292 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
3298 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
3304 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
3308 static PurplePlugin
*my_protocol
= NULL
;
3310 static PurplePluginProtocolInfo prpl_info
=
3313 NULL
, /* user_splits */
3314 NULL
, /* protocol_options */
3315 NO_BUDDY_ICONS
, /* icon_spec */
3316 sipe_list_icon
, /* list_icon */
3317 NULL
, /* list_emblems */
3318 NULL
, /* status_text */ // TODO
3319 NULL
, /* tooltip_text */ // add custom info to contact tooltip
3320 sipe_status_types
, /* away_states */
3321 NULL
, /* blist_node_menu */
3322 NULL
, /* chat_info */
3323 NULL
, /* chat_info_defaults */
3324 sipe_login
, /* login */
3325 sipe_close
, /* close */
3326 sipe_im_send
, /* send_im */
3327 NULL
, /* set_info */ // TODO maybe
3328 sipe_send_typing
, /* send_typing */
3329 NULL
, /* get_info */ // TODO maybe
3330 sipe_set_status
, /* set_status */
3331 NULL
, /* set_idle */
3332 NULL
, /* change_passwd */
3333 sipe_add_buddy
, /* add_buddy */
3334 NULL
, /* add_buddies */
3335 sipe_remove_buddy
, /* remove_buddy */
3336 NULL
, /* remove_buddies */
3337 dummy_add_deny
, /* add_permit */
3338 dummy_add_deny
, /* add_deny */
3339 dummy_add_deny
, /* rem_permit */
3340 dummy_add_deny
, /* rem_deny */
3341 dummy_permit_deny
, /* set_permit_deny */
3342 NULL
, /* join_chat */
3343 NULL
, /* reject_chat */
3344 NULL
, /* get_chat_name */
3345 NULL
, /* chat_invite */
3346 NULL
, /* chat_leave */
3347 NULL
, /* chat_whisper */
3348 NULL
, /* chat_send */
3349 sipe_keep_alive
, /* keepalive */
3350 NULL
, /* register_user */
3351 NULL
, /* get_cb_info */ // deprecated
3352 NULL
, /* get_cb_away */ // deprecated
3353 sipe_alias_buddy
, /* alias_buddy */
3354 sipe_group_buddy
, /* group_buddy */
3355 sipe_rename_group
, /* rename_group */
3356 NULL
, /* buddy_free */
3357 sipe_convo_closed
, /* convo_closed */
3358 purple_normalize_nocase
, /* normalize */
3359 NULL
, /* set_buddy_icon */
3360 sipe_remove_group
, /* remove_group */
3361 NULL
, /* get_cb_real_name */ // TODO?
3362 NULL
, /* set_chat_topic */
3363 NULL
, /* find_blist_chat */
3364 NULL
, /* roomlist_get_list */
3365 NULL
, /* roomlist_cancel */
3366 NULL
, /* roomlist_expand_category */
3367 NULL
, /* can_receive_file */
3368 NULL
, /* send_file */
3369 NULL
, /* new_xfer */
3370 NULL
, /* offline_message */
3371 NULL
, /* whiteboard_prpl_ops */
3372 sipe_send_raw
, /* send_raw */
3376 static PurplePluginInfo info
= {
3377 PURPLE_PLUGIN_MAGIC
,
3378 PURPLE_MAJOR_VERSION
,
3379 PURPLE_MINOR_VERSION
,
3380 PURPLE_PLUGIN_PROTOCOL
, /**< type */
3381 NULL
, /**< ui_requirement */
3383 NULL
, /**< dependencies */
3384 PURPLE_PRIORITY_DEFAULT
, /**< priority */
3385 "prpl-sipe", /**< id */
3386 "Microsoft LCS/OCS", /**< name */
3387 VERSION
, /**< version */
3388 N_("SIP/SIMPLE OCS/LCS Protocol Plugin"), /** summary */
3389 N_("The SIP/SIMPLE LCS/OCS Protocol Plugin"), /** description */
3390 "Anibal Avelar <avelar@gmail.com>, " /**< author */
3391 "Gabriel Burt <gburt@novell.com>", /**< author */
3392 PURPLE_WEBSITE
, /**< homepage */
3393 sipe_plugin_load
, /**< load */
3394 sipe_plugin_unload
, /**< unload */
3395 sipe_plugin_destroy
, /**< destroy */
3396 NULL
, /**< ui_info */
3397 &prpl_info
, /**< extra_info */
3406 static void init_plugin(PurplePlugin
*plugin
)
3408 PurpleAccountUserSplit
*split
;
3409 PurpleAccountOption
*option
;
3410 PurpleKeyValuePair
*kvp
;
3413 purple_debug_info(PACKAGE
, "bindtextdomain = %s", bindtextdomain(GETTEXT_PACKAGE
, LOCALEDIR
));
3414 purple_debug_info(PACKAGE
, "bind_textdomain_codeset = %s",
3415 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8"));
3418 purple_plugin_register(plugin
);
3420 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
3421 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3422 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
3423 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3425 option
= purple_account_option_bool_new(_("Use non-standard port"), "useport", FALSE
);
3426 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3427 // Translators: noun (networking port)
3428 option
= purple_account_option_int_new(_("Port"), "port", 5061);
3429 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3431 option
= purple_account_option_list_new(_("Connection Type"), "transport", NULL
);
3432 purple_account_option_add_list_item(option
, _("Auto"), "auto");
3433 purple_account_option_add_list_item(option
, _("SSL/TLS"), "tls");
3434 purple_account_option_add_list_item(option
, _("TCP"), "tcp");
3435 purple_account_option_add_list_item(option
, _("UDP"), "udp");
3436 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3438 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
3439 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
3441 option
= purple_account_option_string_new(_("User Agent"), "useragent", "Purple/" VERSION
);
3442 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3444 // TODO commented out so won't show in the preferences until we fix krb message signing
3445 /*option = purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE);
3446 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3448 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
3449 option = purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
3450 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
3453 option
= purple_account_option_string_new(_("Auth User"), "authuser", "");
3454 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3455 option
= purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
3456 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
3457 my_protocol
= plugin
;
3460 /* I had to redefined the function for it load, but works */
3461 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
3462 plugin
->info
= &(info
);
3463 init_plugin((plugin
));
3464 sipe_plugin_load((plugin
));
3465 return purple_plugin_register(plugin
);