6 * Copyright (C) 2007 Anibal Avelar "Fixxxer"<avelar@gmail.com>
7 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
10 * Thanks to Google's Summer of Code Program and the helpful mentors
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "sip-internal.h"
34 #define _LIBC_INTERNAL_
40 #include "accountopt.h"
42 #include "conversation.h"
59 #include "sipe-sign.h"
64 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
67 static char *genbranch()
69 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
70 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
71 rand() & 0xFFFF, rand() & 0xFFFF);
74 static char *gencallid()
76 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
77 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
78 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
79 rand() & 0xFFFF, rand() & 0xFFFF);
82 static const char *sipe_list_icon(PurpleAccount
*a
, PurpleBuddy
*b
)
87 static void sipe_keep_alive(PurpleConnection
*gc
)
89 struct sipe_account_data
*sip
= gc
->proto_data
;
90 if (sip
->udp
) { /* in case of UDP send a packet only with a 0 byte to
91 remain in the NAT table */
92 gchar buf
[2] = {0, 0};
93 purple_debug_info("sipe", "sending keep alive\n");
94 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
99 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
101 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
102 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
105 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*);
107 static void send_service(struct sipe_account_data
*sip
);
108 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
);
109 static void send_publish(struct sipe_account_data
*sip
);
111 static void do_notifies(struct sipe_account_data
*sip
)
113 GSList
*tmp
= sip
->watcher
;
114 purple_debug_info("sipe", "do_notifies()\n");
115 //if ((sip->republish != -1) || sip->republish < time(NULL)) {
116 // if (purple_account_get_bool(sip->account, "doservice", TRUE)) {
117 // send_service(sip);
122 purple_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher
*)tmp
->data
)->name
);
123 send_notify(sip
, tmp
->data
);
128 static void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
130 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(purple_status_get_type(status
));
131 struct sipe_account_data
*sip
= NULL
;
133 if (!purple_status_is_active(status
))
137 sip
= account
->gc
->proto_data
;
142 if (primitive
== PURPLE_STATUS_AVAILABLE
)
143 sip
->status
= g_strdup("available");
145 sip
->status
= g_strdup("busy");
151 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
)
153 struct sip_connection
*ret
= NULL
;
154 GSList
*entry
= sip
->openconns
;
157 if (ret
->fd
== fd
) return ret
;
163 static struct sipe_watcher
*watcher_find(struct sipe_account_data
*sip
,
166 struct sipe_watcher
*watcher
;
167 GSList
*entry
= sip
->watcher
;
169 watcher
= entry
->data
;
170 if (!strcmp(name
, watcher
->name
)) return watcher
;
176 static struct sipe_watcher
*watcher_create(struct sipe_account_data
*sip
,
177 const gchar
*name
, const gchar
*callid
, const gchar
*ourtag
,
178 const gchar
*theirtag
, gboolean needsxpidf
)
180 struct sipe_watcher
*watcher
= g_new0(struct sipe_watcher
, 1);
181 watcher
->name
= g_strdup(name
);
182 watcher
->dialog
.callid
= g_strdup(callid
);
183 watcher
->dialog
.ourtag
= g_strdup(ourtag
);
184 watcher
->dialog
.theirtag
= g_strdup(theirtag
);
185 watcher
->needsxpidf
= needsxpidf
;
186 sip
->watcher
= g_slist_append(sip
->watcher
, watcher
);
190 static void watcher_remove(struct sipe_account_data
*sip
, const gchar
*name
)
192 struct sipe_watcher
*watcher
= watcher_find(sip
, name
);
193 sip
->watcher
= g_slist_remove(sip
->watcher
, watcher
);
194 g_free(watcher
->name
);
195 g_free(watcher
->dialog
.callid
);
196 g_free(watcher
->dialog
.ourtag
);
197 g_free(watcher
->dialog
.theirtag
);
201 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
)
203 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
205 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
209 static void connection_remove(struct sipe_account_data
*sip
, int fd
)
211 struct sip_connection
*conn
= connection_find(sip
, fd
);
212 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
213 if (conn
->inputhandler
) purple_input_remove(conn
->inputhandler
);
218 static void connection_free_all(struct sipe_account_data
*sip
)
220 struct sip_connection
*ret
= NULL
;
221 GSList
*entry
= sip
->openconns
;
224 connection_remove(sip
, ret
->fd
);
225 entry
= sip
->openconns
;
229 static void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
231 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
232 struct sipe_buddy
*b
;
233 if (strncmp("sip:", buddy
->name
, 4)) {
234 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
235 purple_blist_rename_buddy(buddy
, buf
);
238 if (!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
239 b
= g_new0(struct sipe_buddy
, 1);
240 purple_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
241 b
->name
= g_strdup(buddy
->name
);
242 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
244 purple_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
248 static void sipe_get_buddies(PurpleConnection
*gc
)
250 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
252 purple_debug_info("sipe", "sipe_get_buddies\n");
254 for (gnode
= purple_get_blist()->root
; gnode
; gnode
= gnode
->next
) {
255 if (!PURPLE_BLIST_NODE_IS_GROUP(gnode
)) continue;
256 for (cnode
= gnode
->child
; cnode
; cnode
= cnode
->next
) {
257 if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode
)) continue;
258 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
259 if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode
)) continue;
260 if (((PurpleBuddy
*)bnode
)->account
== gc
->account
)
261 sipe_add_buddy(gc
, (PurpleBuddy
*)bnode
, (PurpleGroup
*)gnode
);
267 static void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
269 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
270 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
271 g_hash_table_remove(sip
->buddies
, buddy
->name
);
276 static GList
*sipe_status_types(PurpleAccount
*acc
)
278 PurpleStatusType
*type
;
281 type
= purple_status_type_new_with_attrs(
282 PURPLE_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
283 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING
),
285 types
= g_list_append(types
, type
);
287 type
= purple_status_type_new_full(
288 PURPLE_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
289 types
= g_list_append(types
, type
);
294 //static struct sipe_krb5_auth krb5_auth;
295 static gchar
*auth_header_without_newline(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
297 const gchar
*method
= msg
->method
;
298 const gchar
*target
= msg
->target
;
303 const char *authdomain
;
304 const char *authuser
;
305 const char *krb5_realm
;
307 gchar
*krb5_token
= NULL
;
309 authdomain
= purple_account_get_string(sip
->account
, "authdomain", "");
310 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
312 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
313 // and do error checking
315 // KRB realm should always be uppercase
316 //krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
318 if (sip
->realhostname
) {
319 host
= sip
->realhostname
;
320 } else if (purple_account_get_bool(sip
->account
, "use_proxy", TRUE
)) {
321 host
= purple_account_get_string(sip
->account
, "proxy", "");
323 host
= sip
->sipdomain
;
326 /*gboolean new_auth = krb5_auth.gss_context == NULL;
328 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
331 if (new_auth || force_reauth) {
332 krb5_token = krb5_auth.base64_token;
335 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
336 krb5_token = krb5_auth.base64_token;*/
338 if (!authuser
|| strlen(authuser
) < 1) {
339 authuser
= sip
->username
;
342 if (auth
->type
== 1) { /* Digest */
343 sprintf(noncecount
, "%08d", auth
->nc
++);
344 response
= purple_cipher_http_digest_calculate_response(
345 "md5", method
, target
, NULL
, NULL
,
346 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
347 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
349 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
);
352 } else if (auth
->type
== 2) { /* NTLM */
353 // If we have a signature for the message, include that
354 if (msg
->signature
) {
355 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", response=\"%s\"", auth
->realm
, auth
->target
, msg
->signature
);
359 if (auth
->nc
== 3 && auth
->nonce
) {
360 /* TODO: Don't hardcode "purple" as the hostname */
361 gchar
* gssapi_data
= purple_ntlm_gen_authenticate(authuser
, sip
->password
, "purple", authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
362 //auth->ntlm_key = purple_ntlm_get_key();
363 //printf ("ntlmkey == now NULL? %i\n", auth->ntlm_key == NULL);
364 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth
->opaque
, auth
->realm
, auth
->target
, gssapi_data
);
369 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth
->realm
, auth
->target
);
371 } else if (auth
->type
== 3) {
374 /*if (new_auth || force_reauth) {
375 printf ("krb5 token not NULL, so adding gssapi-data attribute; op = %s\n", auth->opaque);
377 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);
379 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", "SIP Communications Service", auth->target, krb5_token);
382 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
383 gchar * mic = "MICTODO";
384 printf ("krb5 token is NULL, so adding response attribute with mic = %s, op=%s\n", mic, auth->opaque);
385 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\", response=\"%s\"", "SIP Communications Service", auth->opaque, auth->target, mic);
386 //tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", opaque=\"%s\", targetname=\"%s\"", "SIP Communications Service",
387 //auth->opaque ? auth->opaque : "", auth->target);
388 tmp = g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\"", "SIP Communications Service", auth->target);
393 tmp
= g_strdup_printf("Kerberos qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", "SIP Communication Service", auth
->target
);
396 sprintf(noncecount
, "%08d", auth
->nc
++);
397 response
= purple_cipher_http_digest_calculate_response(
398 "md5", method
, target
, NULL
, NULL
,
399 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
400 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "response %s\n", response
);
402 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
);
407 static gchar
*auth_header(struct sipe_account_data
*sip
, struct sip_auth
*auth
, struct sipmsg
* msg
, gboolean force_reauth
)
409 gchar
*with
, *without
;
411 without
= auth_header_without_newline(sip
, auth
, msg
, force_reauth
);
412 with
= g_strdup_printf("%s\r\n", without
);
418 static char *parse_attribute(const char *attrname
, const char *source
)
420 const char *tmp
, *tmp2
;
422 int len
= strlen(attrname
);
424 if (!strncmp(source
, attrname
, len
)) {
426 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
428 retval
= g_strndup(tmp
, tmp2
- tmp
);
430 retval
= g_strdup(tmp
);
436 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
)
439 const char *authuser
;
442 const char *krb5_realm
;
445 // XXX FIXME: Get this info from the account dialogs and/or /etc/krb5.conf
446 // and do error checking
448 // KRB realm should always be uppercase
449 /*krb5_realm = g_strup(purple_account_get_string(sip->account, "krb5_realm", ""));
451 if (sip->realhostname) {
452 host = sip->realhostname;
453 } else if (purple_account_get_bool(sip->account, "use_proxy", TRUE)) {
454 host = purple_account_get_string(sip->account, "proxy", "");
456 host = sip->sipdomain;
459 authuser
= purple_account_get_string(sip
->account
, "authuser", sip
->username
);
461 if (!authuser
|| strlen(authuser
) < 1) {
462 authuser
= sip
->username
;
466 purple_debug_error("sipe", "fill_auth: hdr==NULL\n");
470 if (!g_strncasecmp(hdr
, "NTLM", 4)) {
472 parts
= g_strsplit(hdr
+5, "\", ", 0);
475 //purple_debug_info("sipe", "parts[i] %s\n", parts[i]);
476 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
477 auth
->nonce
= g_memdup(purple_ntlm_parse_challenge(tmp
, &auth
->flags
), 8);
480 if ((tmp
= parse_attribute("targetname=\"",
484 else if ((tmp
= parse_attribute("realm=\"",
488 else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
495 if (!strstr(hdr
, "gssapi-data")) {
503 if (!g_strncasecmp(hdr
, "Kerberos", 8)) {
504 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "setting auth type to Kerberos (3)\r\n");
506 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "fill_auth - header: %s\r\n", hdr
);
507 parts
= g_strsplit(hdr
+9, "\", ", 0);
510 purple_debug_info("sipe", "krb - parts[i] %s\n", parts
[i
]);
511 if ((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
512 /*if (krb5_auth.gss_context == NULL) {
513 purple_krb5_init_auth(&krb5_auth, authuser, krb5_realm, sip->password, host, "sip");
515 auth->nonce = g_memdup(krb5_auth.base64_token, 8);*/
518 if ((tmp
= parse_attribute("targetname=\"", parts
[i
]))) {
520 } else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
522 } else if ((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
529 //if (!strstr(hdr, "gssapi-data")) {
538 parts
= g_strsplit(hdr
, " ", 0);
540 if ((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
543 else if ((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
550 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
552 auth
->digest_session_key
= purple_cipher_http_digest_calculate_session_key(
553 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
559 static void sipe_canwrite_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
561 PurpleConnection
*gc
= data
;
562 struct sipe_account_data
*sip
= gc
->proto_data
;
566 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
568 if (max_write
== 0) {
569 purple_input_remove(sip
->tx_handler
);
574 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
576 if (written
< 0 && errno
== EAGAIN
)
578 else if (written
<= 0) {
579 /*TODO: do we really want to disconnect on a failure to write?*/
580 purple_connection_error(gc
, _("Could not write"));
584 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
587 static void sipe_canwrite_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
589 PurpleConnection
*gc
= data
;
590 struct sipe_account_data
*sip
= gc
->proto_data
;
594 max_write
= purple_circ_buffer_get_max_read(sip
->txbuf
);
596 if (max_write
== 0) {
597 purple_input_remove(sip
->tx_handler
);
602 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
604 if (written
< 0 && errno
== EAGAIN
)
606 else if (written
<= 0) {
607 /*TODO: do we really want to disconnect on a failure to write?*/
608 purple_connection_error(gc
, _("Could not write"));
612 purple_circ_buffer_mark_read(sip
->txbuf
, written
);
615 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
);
617 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
)
619 PurpleConnection
*gc
= data
;
620 struct sipe_account_data
*sip
;
621 struct sip_connection
*conn
;
623 if (!PURPLE_CONNECTION_IS_VALID(gc
))
631 purple_connection_error(gc
, _("Could not connect"));
635 sip
= gc
->proto_data
;
637 sip
->connecting
= FALSE
;
639 sipe_canwrite_cb(gc
, sip
->fd
, PURPLE_INPUT_WRITE
);
641 /* If there is more to write now, we need to register a handler */
642 if (sip
->txbuf
->bufused
> 0)
643 sip
->tx_handler
= purple_input_add(sip
->fd
, PURPLE_INPUT_WRITE
,
644 sipe_canwrite_cb
, gc
);
646 conn
= connection_create(sip
, source
);
647 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
650 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
652 PurpleConnection
*gc
= data
;
653 struct sipe_account_data
*sip
;
654 struct sip_connection
*conn
;
656 if (!PURPLE_CONNECTION_IS_VALID(gc
))
658 purple_ssl_close(gsc
);
662 sip
= gc
->proto_data
;
664 sip
->connecting
= FALSE
;
666 sipe_canwrite_cb_ssl(gc
, gsc
, PURPLE_INPUT_WRITE
);
668 /* If there is more to write now, we need to register a handler */
669 if (sip
->txbuf
->bufused
> 0)
670 purple_ssl_input_add(gsc
, sipe_canwrite_cb_ssl
, gc
);
672 conn
= connection_create(sip
, gsc
->fd
);
673 purple_ssl_input_add(sip
->gsc
, sipe_input_cb_ssl
, gc
);
677 static void sendlater(PurpleConnection
*gc
, const char *buf
)
679 struct sipe_account_data
*sip
= gc
->proto_data
;
681 if (!sip
->connecting
) {
682 purple_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
684 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
687 if (purple_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
688 purple_connection_error(gc
, _("Couldn't create socket"));
691 sip
->connecting
= TRUE
;
694 if (purple_circ_buffer_get_max_read(sip
->txbuf
) > 0)
695 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
697 purple_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
700 static void sendout_pkt(PurpleConnection
*gc
, const char *buf
)
702 struct sipe_account_data
*sip
= gc
->proto_data
;
703 time_t currtime
= time(NULL
);
704 int writelen
= strlen(buf
);
706 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
708 if (sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
709 purple_debug_info("sipe", "could not send packet\n");
718 if (sip
->tx_handler
) {
723 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
725 ret
= write(sip
->fd
, buf
, writelen
);
729 if (ret
< 0 && errno
== EAGAIN
)
731 else if (ret
<= 0) { /* XXX: When does this happen legitimately? */
736 if (ret
< writelen
) {
737 if (!sip
->tx_handler
){
739 purple_ssl_input_add(sip
->gsc
, sipe_canwrite_cb_ssl
, gc
);
742 sip
->tx_handler
= purple_input_add(sip
->fd
,
743 PURPLE_INPUT_WRITE
, sipe_canwrite_cb
,
748 /* XXX: is it OK to do this? You might get part of a request sent
749 with part of another. */
750 if (sip
->txbuf
->bufused
> 0)
751 purple_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
753 purple_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
759 static int sipe_send_raw(PurpleConnection
*gc
, const char *buf
, int len
)
761 sendout_pkt(gc
, buf
);
765 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
767 GSList
*tmp
= msg
->headers
;
770 GString
*outstr
= g_string_new("");
771 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
773 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
774 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
775 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
776 tmp
= g_slist_next(tmp
);
778 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
779 sendout_pkt(sip
->gc
, outstr
->str
);
780 g_string_free(outstr
, TRUE
);
783 static void send_sip_response(PurpleConnection
*gc
, struct sipmsg
*msg
, int code
,
784 const char *text
, const char *body
)
786 GSList
*tmp
= msg
->headers
;
789 GString
*outstr
= g_string_new("");
791 /* When sending the acknowlegements and errors, the content length from the original
792 message is still here, but there is no body; we need to make sure we're sending the
793 correct content length */
794 sipmsg_remove_header(msg
, "Content-Length");
797 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
798 sipmsg_add_header(msg
, "Content-Length", len
);
800 sipmsg_add_header(msg
, "Content-Length", "0");
803 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
804 //gchar * mic = "MICTODO";
806 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
808 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
809 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
811 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
812 tmp
= g_slist_next(tmp
);
814 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
815 sendout_pkt(gc
, outstr
->str
);
816 g_string_free(outstr
, TRUE
);
819 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
)
821 if (trans
->msg
) sipmsg_free(trans
->msg
);
822 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
826 static void transactions_add_buf(struct sipe_account_data
*sip
, const struct sipmsg
*msg
, void *callback
)
828 struct transaction
*trans
= g_new0(struct transaction
, 1);
829 trans
->time
= time(NULL
);
831 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
832 trans
->callback
= callback
;
833 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
836 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
838 struct transaction
*trans
;
839 GSList
*transactions
= sip
->transactions
;
840 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
842 while (transactions
) {
843 trans
= transactions
->data
;
844 if (!strcmp(trans
->cseq
, cseq
)) {
847 transactions
= transactions
->next
;
853 static void send_sip_request(PurpleConnection
*gc
, const gchar
*method
,
854 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
855 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
)
857 struct sipe_account_data
*sip
= gc
->proto_data
;
858 char *callid
= dialog
? g_strdup(dialog
->callid
) : gencallid();
859 const char *addh
= "";
860 gchar
*branch
= genbranch();
866 if (!strcmp(method
, "REGISTER")) {
867 if (sip
->regcallid
) {
869 callid
= g_strdup(sip
->regcallid
);
871 else sip
->regcallid
= g_strdup(callid
);
874 if (addheaders
) addh
= addheaders
;
880 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
881 "Via: SIP/2.0/%s %s:%d;branch=%s\r\n"
882 /* epid Identifies a unique endpoint for the user. Used by
883 * the server to determine the correct SA to use for
884 * signing an outgoing response.
885 * TODO: generate a random epid
887 "From: <sip:%s>;tag=%s;epid=1234567890\r\n"
889 "Max-Forwards: 70\r\n"
891 "User-Agent: Purple/" VERSION
"\r\n"
894 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
897 sip
->use_ssl
? "TLS" : sip
->udp
? "UDP" : "TCP",
898 purple_network_get_my_ip(-1),
902 dialog
? dialog
->ourtag
: tag
,
904 dialog
? ";tag=" : "",
905 dialog
? dialog
->theirtag
: "",
914 //printf ("parsing msg buf:\n%s\n\n", buf);
915 msg
= sipmsg_parse_msg(buf
);
922 if (purple_ntlm_authorized()) {
923 struct sipmsg_breakdown msgbd
;
925 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
926 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
928 printf ("Have signature_input_str: %s\n", signature_input_str
);
929 if (signature_input_str
!= NULL
) {
930 msg
->signature
= purple_ntlm_signature_make (signature_input_str
, 0, NULL
);
932 msg
->signature
= NULL
;
934 g_free(signature_input_str
);
935 sipmsg_breakdown_free(&msgbd
);
937 printf ("registrar ntlm_key is null. proxies? %i\n", sip
->proxy
.ntlm_key
== NULL
);
940 if (sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
941 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
942 printf("1.for sig %s got auth buf %s\n", msg
->signature
, buf
);
943 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
944 sipmsg_add_header(msg
, "Authorization", buf
);
946 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
947 //sipmsg_add_header_pos(msg, "Authorization", buf, 5);
950 } else if (!strcmp(method
,"SUBSCRIBE") || !strcmp(method
,"SERVICE") || !strcmp(method
,"MESSAGE") || !strcmp(method
,"INVITE") || !strcmp(method
,"NOTIFY")) {
952 sip
->registrar
.type
=2;
954 buf
= auth_header_without_newline(sip
, &sip
->registrar
, msg
, FALSE
);
955 printf("2.for sig %s got auth buf %s\n", msg
->signature
, buf
);
956 //buf = auth_header(sip, &sip->proxy, msg, FALSE);
957 sipmsg_add_header_pos(msg
, "Proxy-Authorization", buf
, 5);
958 //sipmsg_add_header(msg, "Authorization", buf);
963 buf
= sipmsg_to_string (msg
);
965 /* add to ongoing transactions */
967 transactions_add_buf(sip
, msg
, tc
);
969 sendout_pkt(gc
, buf
);
972 static char *get_contact_register(struct sipe_account_data
*sip
)
974 return g_strdup_printf("<sip:%s:%d;transport=%s>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace", purple_network_get_my_ip(-1), sip
->listenport
, sip
->use_ssl
? "tls" : sip
->udp
? "udp" : "tcp");
977 static char *get_contact(struct sipe_account_data
*sip
)
979 //return g_strdup_printf("<sip:%s@%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->sipdomain, sip->listenport, sipe_network_get_local_system_ip() , sip->udp ? "udp" : "tcp");
980 return g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip
->username
, sip
->listenport
, purple_network_get_my_ip(-1), sip
->use_ssl
? "tls" : sip
->udp
? "udp" : "tcp");
983 static void do_register_exp(struct sipe_account_data
*sip
, int expire
)
985 char *uri
= g_strdup_printf("sip:%s", sip
->sipdomain
);
986 char *to
= g_strdup_printf("sip:%s", sip
->username
);
987 char *contact
= get_contact_register(sip
);
988 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
989 // 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);
990 //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);
991 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
);
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 gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1050 if (msg
->response
== 200 || msg
->response
== 202) {
1054 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* cant be NULL since it is our own msg */
1056 /* we can not subscribe -> user is offline (TODO unknown status?) */
1058 purple_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
1063 static void sipe_subscribe_to_name(struct sipe_account_data
*sip
, const char * buddy_name
)
1065 gchar
*to
= strstr(buddy_name
, "sip:") ? g_strdup(buddy_name
) : g_strdup_printf("sip:%s", buddy_name
);
1066 gchar
*tmp
= get_contact(sip
);
1067 gchar
*contact
= g_strdup_printf(
1068 "Accept: application/pidf+xml, application/xpidf+xml\r\n"
1069 "Event: presence\r\n"
1070 "Contact: %s\r\n", tmp
);
1073 /* subscribe to buddy presence
1074 * we dont need to know the status so we do not need a callback */
1076 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
,
1077 process_subscribe_response
);
1083 static void sipe_subscribe(struct sipe_account_data
*sip
, struct sipe_buddy
*buddy
)
1085 sipe_subscribe_to_name(sip
, buddy
->name
);
1087 /* resubscribe before subscription expires */
1088 /* add some jitter */
1089 buddy
->resubscribe
= time(NULL
)+1140+(rand()%50);
1092 static gboolean
sipe_add_lcs_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1095 xmlnode
*item
, *group
, *isc
;
1096 const char *name_group
, *group_id
;
1098 PurpleGroup
*g
= NULL
;
1102 struct sipe_buddy
*bs
;
1103 struct sipe_group
*gr
;
1104 int len
= msg
->bodylen
;
1106 // Reserved to max 10 groups. TODO be dynamic
1107 gr
= g_new0(struct sipe_group
, 10);
1109 tmp
= sipmsg_find_header(msg
, "Event");
1110 if (tmp
&& !strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)) {
1111 purple_debug_info("sipe", "sipe_add_lcs_contacts->%s-%d\n", msg
->body
, len
);
1112 /*Convert the contact from XML to Purple Buddies*/
1113 isc
= xmlnode_from_str(msg
->body
, len
);
1115 /* TODO Find for all groups */
1116 for (group
= xmlnode_get_child(isc
, "group"); group
; group
= xmlnode_get_next_twin(group
)) {
1117 name_group
= xmlnode_get_attrib(group
, "name");
1118 group_id
= xmlnode_get_attrib(group
, "id");
1120 if (!strncmp(name_group
, "~", 1)){
1121 name_group
=g_strdup("General");
1124 gr
[ng
].name_group
= g_strdup(name_group
);
1125 gr
[ng
].id
= g_strdup(group_id
);
1126 purple_debug_info("sipe", "name_group->%s\n", name_group
);
1127 g
= purple_find_group(name_group
);
1130 g
= purple_group_new(name_group
);
1131 purple_blist_add_group(g
, NULL
);
1135 g
= purple_find_group("General");
1137 g
= purple_group_new("General");
1138 purple_blist_add_group(g
, NULL
);
1146 for (i
= 0; i
< ng
;i
++) {
1147 purple_debug_info("sipe", "id->%s\n", gr
[i
].id
);
1148 purple_debug_info("sipe", "id->%s\n", gr
[i
].name_group
);
1151 for (item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
)) {
1152 const char *uri
, *name
, *groups
;
1155 uri
= xmlnode_get_attrib(item
, "uri");
1156 name
= xmlnode_get_attrib(item
, "name");
1157 groups
= xmlnode_get_attrib(item
, "groups");
1158 parts
= g_strsplit(groups
, " ", 0);
1159 purple_debug_info("sipe", "URI->%s,Groups->%s\n", uri
, groups
);
1160 if (parts
[i
]!=NULL
){
1162 purple_debug_info("sipe", "Groups->parts[i] %s\n", parts
[i
]);
1163 if (!strcmp(gr
[i
].id
,parts
[i
])){
1164 purple_debug_info("sipe", "Found Groups->gr[i].id(%s),gr[i].name_group (%s)\n",gr
[i
].id
,gr
[i
].name_group
);
1166 buddy_name
= g_strdup_printf("sip:%s", uri
);
1168 //b = purple_find_buddy(sip->account, buddy_name);
1169 b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, gr
[i
].g
);
1171 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1175 //sipe_add_buddy(sip->gc, b , gr[i].g);
1176 purple_blist_add_buddy(b
, NULL
, gr
[i
].g
, NULL
);
1177 purple_blist_alias_buddy(b
, uri
);
1178 bs
= g_new0(struct sipe_buddy
, 1);
1179 bs
->name
= g_strdup(b
->name
);
1180 g_hash_table_insert(sip
->buddies
, bs
->name
, bs
);
1192 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
)
1194 gchar
*contact
= "Event: vnd-microsoft-roaming-contacts\r\nAccept: application/vnd-microsoft-roaming-contacts+xml\r\nSupported: com.microsoft.autoextend\r\nSupported: ms-benotify\r\nProxy-Require: ms-benotify\r\nSupported: ms-piggyback-first-notify\r\n";
1197 //to = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
1198 to
= g_strdup_printf("sip:%s", sip
->username
);
1200 tmp
= get_contact(sip
);
1201 contact
= g_strdup_printf("%sContact: %s\r\n", contact
, tmp
);
1203 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
, sipe_add_lcs_contacts
);
1208 static void sipe_invite(struct sipe_account_data
*sip
, const char *name
)
1215 if (strstr(name
, "sip:")) {
1216 to
= g_strdup(name
);
1218 to
= g_strdup_printf("sip:%s", name
);
1221 contact
= get_contact(sip
);
1222 hdr
= g_strdup_printf(
1224 "Supported: com.microsoft.rtc-multiparty\r\n"
1225 "Roster-Manager:sip:%s\r\n"
1226 "Ms-Conversation-ID: AckwibOFOjjDdVR6S5e0xywMj6Kaww==\r\n" //temp
1227 "ms-text-format: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC0ASQBNAC0ARgBvAHIAbQBhAHQAOgAgAEYATgA9AE0AUwAlADIAMABTAGgAZQBsAGwAJQAyADAARABsAGcAJQAyADAAMgA7ACAARQBGAD0AOwAgAEMATwA9ADAAOwAgAEMAUwA9ADAAOwAgAFAARgA9ADAACgANAAoADQA; \r\n" //temp
1228 "Supported: ms-delayed-accept\r\n"
1229 "Supported: ms-renders-isf\r\n"
1230 "Supported: ms-renders-gif\r\n"
1231 "Supported: ms-renders-mime-alternative\r\n"
1232 "EndPoints: <sip:%s>, <%s>\r\n"
1233 "Content-Type: application/sdp\r\n",
1234 contact
, sip
->username
, sip
->username
, to
);
1236 body
= g_strdup_printf(
1238 "o=- 0 0 IN IP4 %s\r\n"
1242 "m=message %d sip null\r\n"
1243 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml",
1244 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1), 5061);
1246 send_sip_request(sip
->gc
, "INVITE", to
, to
, hdr
, body
, NULL
, NULL
);
1254 static void sipe_buddy_resub(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
)
1256 time_t curtime
= time(NULL
);
1257 if (buddy
->resubscribe
< curtime
) {
1258 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_buddy_resub %s\n", name
);
1259 sipe_subscribe(sip
, buddy
);
1263 static gboolean
resend_timeout(struct sipe_account_data
*sip
)
1265 GSList
*tmp
= sip
->transactions
;
1266 time_t currtime
= time(NULL
);
1268 struct transaction
*trans
= tmp
->data
;
1270 purple_debug_info("sipe", "have open transaction age: %d\n", currtime
- trans
->time
);
1271 if ((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
1274 if ((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
1276 sendout_sipmsg(sip
, trans
->msg
);
1283 static gboolean
subscribe_timeout(struct sipe_account_data
*sip
)
1286 time_t curtime
= time(NULL
);
1287 /* register again if first registration expires */
1288 if (sip
->reregister
< curtime
) {
1291 /* check for every subscription if we need to resubscribe */
1292 //Fixxxer we need resub?
1293 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_resub
, (gpointer
)sip
);
1295 /* remove a timed out suscriber */
1299 struct sipe_watcher
*watcher
= tmp
->data
;
1300 if (watcher
->expire
< curtime
) {
1301 watcher_remove(sip
, watcher
->name
);
1304 if (tmp
) tmp
= tmp
->next
;
1310 static void sipe_send_message(struct sipe_account_data
*sip
, const char *to
, const char *msg
, const char *type
)
1315 if (strncmp("sip:", to
, 4)) {
1316 fullto
= g_strdup_printf("sip:%s", to
);
1318 fullto
= g_strdup(to
);
1321 hdr
= g_strdup_printf("Content-Type: %s\r\n", type
);
1323 hdr
= g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1325 tmp
= get_contact(sip
);
1326 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1329 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msg
, NULL
, NULL
);
1334 static int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
, PurpleMessageFlags flags
)
1336 struct sipe_account_data
*sip
= gc
->proto_data
;
1337 char *to
= g_strdup(who
);
1338 char *text
= purple_unescape_html(what
);
1339 //sipe_send_message(sip, to, text, NULL);
1340 //sipe_invite(sip, to);
1341 purple_debug_info("sipe", "sending IMs not implemented\n");
1347 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1351 gboolean found
= FALSE
;
1353 from
= parse_from(sipmsg_find_header(msg
, "From"));
1357 purple_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
1359 contenttype
= sipmsg_find_header(msg
, "Content-Type");
1360 if (!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
1361 serv_got_im(sip
->gc
, from
, msg
->body
, 0, time(NULL
));
1362 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1365 if (!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
1366 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1371 purple_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1375 state
= xmlnode_get_child(isc
, "state");
1378 purple_debug_info("sipe", "process_incoming_message: no state found\n");
1383 statedata
= xmlnode_get_data(state
);
1385 if (strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, PURPLE_TYPING
);
1386 else serv_got_typing_stopped(sip
->gc
, from
);
1391 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1395 purple_debug_info("sipe", "got unknown mime-type");
1396 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
1401 static void process_incoming_invite(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1404 contact
= get_contact(sip
);
1405 sipmsg_remove_header(msg
, "Contact");
1406 sipmsg_add_header(msg
, "Contact", contact
);
1408 //sipmsg_remove_header(msg, "User-Agent");
1409 //sipmsg_add_header_pos(msg, "User-Agent", g_strdup_printf("Purple/" VERSION), 6);
1411 send_sip_response(sip
->gc
, msg
, 200, "OK", g_strdup_printf(
1413 "o=- 0 0 IN IP4 %s\r\n"
1417 "m=message %d sip sip:%s\r\n"
1418 "a=accept-types:text/plain text/html image/gif multipart/alternative application/im-iscomposing+xml",
1419 purple_network_get_my_ip(-1), purple_network_get_my_ip(-1),
1420 //sip->realport, sip->username
1421 5061, sip
->username
));
1426 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1428 gchar
*tmp
, krb5_token
;
1429 const gchar
*expires_header
;
1432 expires_header
= sipmsg_find_header(msg
, "Expires");
1433 expires
= expires_header
!= NULL
? strtol(expires_header
, NULL
, 10) : 0;
1434 purple_debug_info("sipe", "got response to REGISTER; expires = %d\n", expires
);
1436 switch (msg
->response
) {
1439 sip
->registerstatus
= 0;
1441 sip
->registerstatus
= 3;
1442 purple_connection_set_state(sip
->gc
, PURPLE_CONNECTED
);
1444 /* tell everybody we're online */
1447 /* get buddies from blist */
1448 sipe_get_buddies(sip
->gc
);
1449 subscribe_timeout(sip
);
1451 //sipe_subscribe_to_name(sip, sip->username);
1453 tmp
= sipmsg_find_header(msg
, "Allow-Events");
1454 if (tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
1455 sipe_subscribe_buddylist(sip
);
1458 // Should we remove the transaction here?
1459 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - got 200, removing CSeq: %d\r\n", sip
->cseq
);
1460 //transactions_remove(sip, tc);
1464 if (sip
->registerstatus
!= 2) {
1465 purple_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
1466 if (sip
->registrar
.retries
> 3) {
1467 sip
->gc
->wants_to_die
= TRUE
;
1468 purple_connection_error(sip
->gc
, _("Wrong Password"));
1471 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
1472 tmp
= sipmsg_find_auth_header(msg
, "NTLM");
1474 tmp
= sipmsg_find_auth_header(msg
, "Kerberos");
1476 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_register_response - Auth header: %s\r\n", tmp
);
1477 fill_auth(sip
, tmp
, &sip
->registrar
);
1478 sip
->registerstatus
= 2;
1479 if (sip
->account
->disconnecting
) {
1480 do_register_exp(sip
, 0);
1490 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1496 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
1497 gboolean isonline
= FALSE
;
1499 fromhdr
= sipmsg_find_header(msg
, "From");
1500 from
= parse_from(fromhdr
);
1503 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1506 purple_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
1510 purple_debug_info("sipe", "process_incoming_notify: body(%s)\n",msg
->body
);
1512 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
1513 if ((status
= xmlnode_get_child(tuple
, "status")))
1514 basicstatus
= xmlnode_get_child(status
, "basic");
1517 purple_debug_info("sipe", "process_incoming_notify: no basic found\n");
1522 tmp2
= xmlnode_get_data(basicstatus
);
1524 purple_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n",tmp2
);
1528 purple_debug_info("sipe", "process_incoming_notify: no basic data found\n");
1533 if (strstr(tmp2
, "open")) {
1539 if (isonline
) purple_prpl_got_user_status(sip
->account
, from
, "available", NULL
);
1540 else purple_prpl_got_user_status(sip
->account
, from
, "offline", NULL
);
1545 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1549 static gchar
*find_tag(const gchar
*hdr
)
1551 return sipmsg_find_part_of_header (hdr
, ";tag=", ";", NULL
);
1554 static gchar
* gen_xpidf(struct sipe_account_data
*sip
)
1556 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1558 "<presentity uri=\"sip:%s;method=SUBSCRIBE\"/>\r\n"
1559 "<display name=\"sip:%s\"/>\r\n"
1560 "<atom id=\"1234\">\r\n"
1561 "<address uri=\"sip:%s\">\r\n"
1562 "<status status=\"%s\"/>\r\n"
1575 static gchar
* gen_pidf(struct sipe_account_data
*sip
)
1577 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
1578 "<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"
1579 "<tuple id=\"0\">\r\n"
1581 "<basic>open</basic>\r\n"
1582 "<ep:activities>\r\n"
1583 " <ep:activity>%s</ep:activity>\r\n"
1587 "<ci:display-name>%s</ci:display-name>\r\n"
1595 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*watcher
)
1597 gchar
*doc
= watcher
->needsxpidf
? gen_xpidf(sip
) : gen_pidf(sip
);
1598 gchar
*hdr
= watcher
->needsxpidf
? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
1599 send_sip_request(sip
->gc
, "NOTIFY", watcher
->name
, watcher
->name
, hdr
, doc
, &watcher
->dialog
, NULL
);
1603 static gboolean
process_service_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
)
1605 if (msg
->response
!= 200 && msg
->response
!= 408) {
1606 /* never send again */
1607 sip
->republish
= -1;
1612 static void send_publish(struct sipe_account_data
*sip
)
1614 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
1615 gchar
*doc
= g_strdup_printf(
1616 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\"><publications uri=\"%s\"><publication categoryName=\"device\" instance=\"1617359818\" container=\"2\" version=\"0\" expireType=\"endpoint\"><device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"BB44F8D5-1540-547D-9ECE-6486D33DC804\"><capabilities preferred=\"false\" uri=\"%s\"><text capture=\"true\" render=\"true\" publish=\"false\"/><gifInk capture=\"false\" render=\"true\" publish=\"false\"/><isfInk capture=\"false\" render=\"true\" publish=\"false\"/></capabilities><timezone>%s</timezone><machineName>%s</machineName></device></publication><publication categoryName=\"state\" instance=\"906391356\" container=\"2\" version=\"0\" expireType=\"endpoint\"><state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\"><availability>3500</availability><endpointLocation></endpointLocation></state></publication><publication categoryName=\"state\" instance=\"906391356\" container=\"3\" version=\"0\" expireType=\"endpoint\"><state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\"><availability>3500</availability><endpointLocation></endpointLocation></state></publication></publications></publish>",
1618 "00:00:00-05:00", // TODO timezone
1619 "PC" // TODO machine name
1622 gchar
*tmp
= get_contact(sip
);
1623 gchar
*hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: application/msrtc-category-publish+xml\r\n", tmp
);
1626 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
, hdr
, doc
, NULL
, process_service_response
);
1627 //sip->republish = time(NULL) + 500;
1634 static void send_service(struct sipe_account_data
*sip
)
1636 //gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->sipdomain);
1637 gchar
*uri
= g_strdup_printf("sip:%s", sip
->username
);
1638 //gchar *doc = gen_pidf(sip);
1640 gchar
*doc
= gen_pidf(sip
);
1641 gchar
*hdr
= g_strdup("Event: presence\r\nContent-Type: application/pidf+xml\r\n");
1643 //gchar *hdr = g_strdup("Content-Type: application/SOAP+xml\r\n");
1644 gchar
*tmp
= get_contact(sip
);
1645 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1647 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
,
1649 doc
, NULL
, process_service_response
);
1650 sip
->republish
= time(NULL
) + 500;
1656 static void process_incoming_subscribe(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1658 const char *from_hdr
= sipmsg_find_header(msg
, "From");
1659 gchar
*from
= parse_from(from_hdr
);
1660 gchar
*theirtag
= find_tag(from_hdr
);
1661 gchar
*ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
1662 gboolean tagadded
= FALSE
;
1663 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1664 gchar
*expire
= sipmsg_find_header(msg
, "Expire");
1665 // gchar *ms-received-port =find_received_port(sipmsg_find_header(msg, "Contact"));
1667 struct sipe_watcher
*watcher
= watcher_find(sip
, from
);
1672 if (!watcher
) { /* new subscription */
1673 gchar
*acceptheader
= sipmsg_find_header(msg
, "Accept");
1674 gboolean needsxpidf
= FALSE
;
1675 if (!purple_privacy_check(sip
->account
, from
)) {
1676 send_sip_response(sip
->gc
, msg
, 202, "Ok", NULL
);
1680 gchar
*tmp
= acceptheader
;
1681 gboolean foundpidf
= FALSE
;
1682 gboolean foundxpidf
= FALSE
;
1683 while (tmp
&& tmp
< acceptheader
+ strlen(acceptheader
)) {
1684 gchar
*tmp2
= strchr(tmp
, ',');
1685 if (tmp2
) *tmp2
= '\0';
1686 if (!strcmp("application/pidf+xml", tmp
))
1688 if (!strcmp("application/xpidf+xml", tmp
))
1693 while (*tmp
== ' ') tmp
++;
1697 if (!foundpidf
&& foundxpidf
) needsxpidf
= TRUE
;
1698 g_free(acceptheader
);
1700 watcher
= watcher_create(sip
, from
, callid
, ourtag
, theirtag
, needsxpidf
);
1703 gchar
*to
= g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg
, "To"), ourtag
);
1704 sipmsg_remove_header(msg
, "To");
1705 sipmsg_add_header(msg
, "To", to
);
1709 watcher
->expire
= time(NULL
) + strtol(expire
, NULL
, 10);
1711 watcher
->expire
= time(NULL
) + 600;
1713 sipmsg_remove_header(msg
, "Contact");
1714 tmp
= get_contact(sip
);
1715 sipmsg_add_header(msg
, "Contact", tmp
);
1717 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
);
1718 send_sip_response(sip
->gc
, msg
, 200, "Ok", NULL
);
1719 send_notify(sip
, watcher
);
1728 static void process_input_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
)
1730 gboolean found
= FALSE
;
1731 purple_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
1732 if (msg
->response
== 0) { /* request */
1733 if (!strcmp(msg
->method
, "MESSAGE")) {
1734 process_incoming_message(sip
, msg
);
1736 } else if (!strcmp(msg
->method
, "NOTIFY")) {
1737 purple_debug_info("sipe","send->process_incoming_notify\n");
1738 process_incoming_notify(sip
, msg
);
1740 } else if (!strcmp(msg
->method
, "SUBSCRIBE")) {
1741 purple_debug_info("sipe","send->process_incoming_subscribe\n");
1742 process_incoming_subscribe(sip
, msg
);
1744 } else if (!strcmp(msg
->method
, "INVITE")) {
1745 purple_debug_info("sipe","not calling unfinished send->process_incoming_invite\n");
1746 //process_incoming_invite(sip, msg);
1748 } else if (!strcmp(msg
->method
, "INFO")) {
1749 // TODO implement this - keyboard activity
1751 } else if (!strcmp(msg
->method
, "ACK")) {
1754 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
1756 } else { /* response */
1757 struct transaction
*trans
= transactions_find(sip
, msg
);
1759 if (msg
->response
== 407) {
1760 gchar
*resend
, *auth
, *ptmp
;
1762 if (sip
->proxy
.retries
> 30) return;
1763 sip
->proxy
.retries
++;
1764 /* do proxy authentication */
1766 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
1768 fill_auth(sip
, ptmp
, &sip
->proxy
);
1769 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
, TRUE
);
1770 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
1771 sipmsg_add_header_pos(trans
->msg
, "Proxy-Authorization", auth
, 5);
1773 resend
= sipmsg_to_string(trans
->msg
);
1774 /* resend request */
1775 sendout_pkt(sip
->gc
, resend
);
1778 if (msg
->response
== 100) {
1779 /* ignore provisional response */
1780 purple_debug_info("sipe", "got trying response\n");
1782 sip
->proxy
.retries
= 0;
1783 if (!strcmp(trans
->msg
->method
, "REGISTER")) {
1784 if (msg
->response
== 401) sip
->registrar
.retries
++;
1785 else sip
->registrar
.retries
= 0;
1786 purple_debug_info("sipe", "RE-REGISTER CSeq: %d\r\n", sip
->cseq
);
1788 if (msg
->response
== 401) {
1789 gchar
*resend
, *auth
, *ptmp
;
1791 if (sip
->registrar
.retries
> 4) return;
1792 sip
->registrar
.retries
++;
1794 if (!purple_account_get_bool(sip
->account
, "krb5", FALSE
)) {
1795 ptmp
= sipmsg_find_auth_header(msg
, "NTLM");
1797 ptmp
= sipmsg_find_auth_header(msg
, "Kerberos");
1800 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - Auth header: %s\r\n", ptmp
);
1802 fill_auth(sip
, ptmp
, &sip
->registrar
);
1803 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
, TRUE
);
1804 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
1805 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
1807 //sipmsg_remove_header(trans->msg, "Authorization");
1808 //sipmsg_add_header(trans->msg, "Authorization", auth);
1810 resend
= sipmsg_to_string(trans
->msg
);
1811 /* resend request */
1812 sendout_pkt(sip
->gc
, resend
);
1816 if (trans
->callback
) {
1817 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - we have a transaction callback\r\n");
1818 /* call the callback to process response*/
1819 (trans
->callback
)(sip
, msg
, trans
);
1821 /* Not sure if this is needed or what needs to be done
1822 but transactions seem to be removed prematurely so
1823 this only removes them if the response is 200 OK */
1824 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "process_input_message - removing CSeq %d\r\n", sip
->cseq
);
1825 transactions_remove(sip
, trans
);
1830 /* This is done because in an OCS2007 server trace the MS
1831 * Communicator client seems to reset the CSeq after an OK */
1835 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received response to unknown transaction");
1839 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
1843 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
1851 /* according to the RFC remove CRLF at the beginning */
1852 while (*cur
== '\r' || *cur
== '\n') {
1855 if (cur
!= conn
->inbuf
) {
1856 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
1857 conn
->inbufused
= strlen(conn
->inbuf
);
1860 /* Received a full Header? */
1861 if ((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
) {
1862 time_t currtime
= time(NULL
);
1865 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
1866 msg
= sipmsg_parse_header(conn
->inbuf
);
1869 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
1870 if (restlen
>= msg
->bodylen
) {
1871 dummy
= g_malloc(msg
->bodylen
+ 1);
1872 memcpy(dummy
, cur
, msg
->bodylen
);
1873 dummy
[msg
->bodylen
] = '\0';
1875 cur
+= msg
->bodylen
;
1876 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
1877 conn
->inbufused
= strlen(conn
->inbuf
);
1883 //gchar * mic = purple_krb5_get_mic_for_sipmsg(&krb5_auth, msg);
1884 //printf ("\nWe think MIC for incoming should be: %s\n\n", mic);
1886 //if (purple_ntlm_authorized()) {
1888 struct sipmsg_breakdown msgbd
;
1890 sipmsg_breakdown_parse(&msgbd
, sip
->registrar
.realm
, sip
->registrar
.target
);
1891 gchar
* signature_input_str
= sipmsg_breakdown_get_string(&msgbd
);
1893 printf ("Have signature_input_str for incoming msg: %s\n", signature_input_str
);
1894 if (signature_input_str
!= NULL
) {
1895 guint64 srand
= g_ascii_strtoull (msgbd
.rand
, NULL
, 16);
1896 printf ("Have srand = %ld\n", srand
);
1897 msg
->signature
= purple_ntlm_signature_make (signature_input_str
, srand
, sipmsg_find_part_of_header(sipmsg_find_header(msg
, "Authentication-Info"), "rspauth=\"", "\"", NULL
));
1900 g_free(signature_input_str
);
1901 sipmsg_breakdown_free(&msgbd
);
1905 printf ("registrar ntlm_key is null. proxies? %i\n", sip
->proxy
.ntlm_key
== NULL
);
1908 process_input_message(sip
, msg
);
1911 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "received a incomplete sip msg: %s\n", conn
->inbuf
);
1915 static void sipe_udp_process(gpointer data
, gint source
, PurpleInputCondition con
)
1917 PurpleConnection
*gc
= data
;
1918 struct sipe_account_data
*sip
= gc
->proto_data
;
1923 static char buffer
[65536];
1924 if ((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
1926 purple_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
1927 msg
= sipmsg_parse_msg(buffer
);
1928 if (msg
) process_input_message(sip
, msg
);
1932 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
1934 PurpleConnection
*gc
= data
;
1935 struct sipe_account_data
*sip
= gc
->proto_data
;
1936 struct sip_connection
*conn
= NULL
;
1938 static char buf
[4096];
1940 /* TODO: It should be possible to make this check unnecessary */
1941 if (!PURPLE_CONNECTION_IS_VALID(gc
)) {
1942 purple_ssl_close(gsc
);
1946 conn
= connection_find(sip
, sip
->gsc
->fd
);
1948 purple_debug_error("sipe", "Connection not found!\n");
1949 purple_ssl_close(gsc
);
1954 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
1955 conn
->inbuflen
+= SIMPLE_BUF_INC
;
1956 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
1959 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
1961 if (len
< 0 && errno
== EAGAIN
) {
1962 /* Try again later */
1964 } else if (len
< 0) {
1965 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
1966 connection_remove(sip
, sip
->gsc
->fd
);
1967 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
1969 } else if (len
== 0) {
1970 purple_connection_error(gc
, _("Server has disconnected"));
1971 connection_remove(sip
, sip
->gsc
->fd
);
1972 if (sip
->fd
== gsc
->fd
) sip
->fd
= -1;
1976 conn
->inbufused
+= len
;
1977 conn
->inbuf
[conn
->inbufused
] = '\0';
1979 process_input(sip
, conn
);
1983 static void sipe_input_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
1985 PurpleConnection
*gc
= data
;
1986 struct sipe_account_data
*sip
= gc
->proto_data
;
1988 struct sip_connection
*conn
= connection_find(sip
, source
);
1990 purple_debug_error("sipe", "Connection not found!\n");
1994 if (conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
1995 conn
->inbuflen
+= SIMPLE_BUF_INC
;
1996 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
1999 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
2001 if (len
< 0 && errno
== EAGAIN
)
2003 else if (len
<= 0) {
2004 purple_debug_info("sipe", "sipe_input_cb: read error\n");
2005 connection_remove(sip
, source
);
2006 if (sip
->fd
== source
) sip
->fd
= -1;
2010 conn
->inbufused
+= len
;
2011 conn
->inbuf
[conn
->inbufused
] = '\0';
2013 process_input(sip
, conn
);
2016 /* Callback for new connections on incoming TCP port */
2017 static void sipe_newconn_cb(gpointer data
, gint source
, PurpleInputCondition cond
)
2019 PurpleConnection
*gc
= data
;
2020 struct sipe_account_data
*sip
= gc
->proto_data
;
2021 struct sip_connection
*conn
;
2023 int newfd
= accept(source
, NULL
, NULL
);
2025 conn
= connection_create(sip
, newfd
);
2027 conn
->inputhandler
= purple_input_add(newfd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2030 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
)
2032 PurpleConnection
*gc
= data
;
2033 struct sipe_account_data
*sip
;
2034 struct sip_connection
*conn
;
2036 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2044 purple_connection_error(gc
, _("Could not connect"));
2048 sip
= gc
->proto_data
;
2051 conn
= connection_create(sip
, source
);
2053 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2057 conn
->inputhandler
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_input_cb
, gc
);
2060 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
2062 PurpleConnection
*gc
= data
;
2063 struct sipe_account_data
*sip
;
2064 struct sip_connection
*conn
;
2066 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2068 purple_ssl_close(gsc
);
2072 sip
= gc
->proto_data
;
2074 conn
= connection_create(sip
, sip
->fd
);
2075 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2076 sip
->listenfd
= sip
->fd
;
2077 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2081 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
2084 static guint
sipe_ht_hash_nick(const char *nick
)
2086 char *lc
= g_utf8_strdown(nick
, -1);
2087 guint bucket
= g_str_hash(lc
);
2093 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
2095 return (purple_utf8_strcasecmp(nick1
, nick2
) == 0);
2098 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
)
2100 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2102 sip
->listen_data
= NULL
;
2104 if (listenfd
== -1) {
2105 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2111 sip
->listenport
= purple_network_get_port_from_fd(sip
->fd
);
2112 sip
->listenfd
= sip
->fd
;
2114 sip
->listenpa
= purple_input_add(sip
->fd
, PURPLE_INPUT_READ
, sipe_udp_process
, sip
->gc
);
2116 sip
->resendtimeout
= purple_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
2117 sip
->registertimeout
= purple_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
2121 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
)
2123 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2126 sip
->query_data
= NULL
;
2128 if (!hosts
|| !hosts
->data
) {
2129 purple_connection_error(sip
->gc
, _("Couldn't resolve host"));
2133 addr_size
= GPOINTER_TO_INT(hosts
->data
);
2134 hosts
= g_slist_remove(hosts
, hosts
->data
);
2135 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
2136 g_free(hosts
->data
);
2137 hosts
= g_slist_remove(hosts
, hosts
->data
);
2139 hosts
= g_slist_remove(hosts
, hosts
->data
);
2140 g_free(hosts
->data
);
2141 hosts
= g_slist_remove(hosts
, hosts
->data
);
2144 /* create socket for incoming connections */
2145 sip
->listen_data
= purple_network_listen_range(5060, 5160, SOCK_DGRAM
,
2146 sipe_udp_host_resolved_listen_cb
, sip
);
2147 if (sip
->listen_data
== NULL
) {
2148 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2153 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
2156 PurpleConnection
*gc
= data
;
2157 struct sipe_account_data
*sip
;
2159 /* If the connection is already disconnected, we don't need to do anything else */
2160 if (!PURPLE_CONNECTION_IS_VALID(gc
))
2163 sip
= gc
->proto_data
;
2167 case PURPLE_SSL_CONNECT_FAILED
:
2168 purple_connection_error(gc
, _("Connection Failed"));
2170 case PURPLE_SSL_HANDSHAKE_FAILED
:
2171 purple_connection_error(gc
, _("SSL Handshake Failed"));
2177 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
)
2179 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
2180 PurpleProxyConnectData
*connect_data
;
2182 sip
->listen_data
= NULL
;
2184 sip
->listenfd
= listenfd
;
2185 if (sip
->listenfd
== -1) {
2186 purple_connection_error(sip
->gc
, _("Could not create listen socket"));
2190 purple_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
2191 //sip->listenport = purple_network_get_port_from_fd(sip->listenfd);
2192 sip
->listenport
= purple_network_get_port_from_fd(sip
->listenfd
);
2193 sip
->listenpa
= purple_input_add(sip
->listenfd
, PURPLE_INPUT_READ
,
2194 sipe_newconn_cb
, sip
->gc
);
2195 purple_debug_info("sipe", "connecting to %s port %d\n",
2196 sip
->realhostname
, sip
->realport
);
2197 /* open tcp connection to the server */
2198 connect_data
= purple_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
2199 sip
->realport
, login_cb
, sip
->gc
);
2201 if (connect_data
== NULL
) {
2202 purple_connection_error(sip
->gc
, _("Couldn't create socket"));
2208 static void srvresolved(PurpleSrvResponse
*resp
, int results
, gpointer data
)
2210 struct sipe_account_data
*sip
;
2215 sip
->srv_query_data
= NULL
;
2217 port
= purple_account_get_int(sip
->account
, "port", 0);
2219 /* find the host to connect to */
2221 hostname
= g_strdup(resp
->hostname
);
2222 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - SRV hostname: %s\r\n", hostname
);
2227 if (!purple_account_get_bool(sip
->account
, "useproxy", FALSE
)) {
2228 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - using sipdomain\r\n");
2229 hostname
= g_strdup(sip
->sipdomain
);
2231 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "srvresolved - using specified SIP proxy\r\n");
2232 hostname
= g_strdup(purple_account_get_string(sip
->account
, "proxy", sip
->sipdomain
));
2236 sip
->realhostname
= hostname
;
2237 sip
->realport
= port
;
2238 if (!sip
->realport
) sip
->realport
= 5060;
2242 // /* create socket for incoming connections */
2243 // sip->listen_data = purple_network_listen_range(5060, 5160, SOCK_STREAM,
2244 // sipe_tcp_connect_listen_cb, sip);
2245 // if (sip->listen_data == NULL) {
2246 // purple_connection_error(sip->gc, _("Could not create listen socket"));
2249 //} else { /* UDP */
2250 // purple_debug_info("sipe", "using udp with server %s and port %d\n", hostname, port);
2252 // sip->query_data = purple_dnsquery_a(hostname, port, sipe_udp_host_resolved, sip);
2253 // if (sip->query_data == NULL) {
2254 // purple_connection_error(sip->gc, _("Could not resolve hostname"));
2259 static void sipe_login(PurpleAccount
*account
)
2261 PurpleConnection
*gc
;
2262 struct sipe_account_data
*sip
;
2264 gchar
*hosttoconnect
;
2266 const char *username
= purple_account_get_username(account
);
2267 gc
= purple_account_get_connection(account
);
2269 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
2270 gc
->wants_to_die
= TRUE
;
2271 purple_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
2275 if (!purple_account_get_bool(account
, "ssl", FALSE
)){
2276 if (!purple_ssl_is_supported())
2278 gc
->wants_to_die
= TRUE
;
2279 purple_connection_error(gc
,
2280 _("SSL support is needed for SSL/TLS support. Please install a supported "
2287 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
2289 sip
->account
= account
;
2290 sip
->registerexpire
= 900;
2291 sip
->udp
= purple_account_get_bool(account
, "udp", FALSE
);
2292 sip
->use_ssl
= purple_account_get_bool(account
, "ssl", FALSE
);
2294 purple_debug_info("sipe", "sip->use_ssl->%d\n", sip
->use_ssl
);
2296 /* TODO: is there a good default grow size? */
2298 sip
->txbuf
= purple_circ_buffer_new(0);
2300 userserver
= g_strsplit(username
, "@", 2);
2301 purple_connection_set_display_name(gc
, userserver
[0]);
2302 sip
->username
= g_strdup(g_strjoin("@", userserver
[0], userserver
[1], NULL
));
2303 sip
->sipdomain
= g_strdup(userserver
[1]);
2304 sip
->password
= g_strdup(purple_connection_get_password(gc
));
2305 g_strfreev(userserver
);
2308 // Communicator queries _sipinternaltls._tcp.domain.com and uses that
2309 // information to connect to the OCS server.
2311 // XXX FIXME: eventually we should also query for sipexternaltls as well
2312 // if Pidgin is not on the local LAN
2313 // This doesn't quite work as advertised yet so make sure your have
2314 // your OCS FQDN in the proxy setting in the SIPE account settings
2316 sip
->srv_query_data
= purple_srv_resolve("sipinternaltls", "tcp", sip
->sipdomain
, srvresolved
, sip
);
2319 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - realhostname: %s\r\n", sip
->realhostname
);
2321 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
2323 purple_connection_update_progress(gc
, _("Connecting"), 1, 2);
2325 /* TODO: Set the status correctly. */
2326 sip
->status
= g_strdup("available");
2328 if (!purple_account_get_bool(account
, "useproxy", FALSE
)) {
2329 purple_debug(PURPLE_DEBUG_MISC
, "sipe", "sipe_login - checking realhostname again: %s\r\n", sip
->realhostname
);
2330 hosttoconnect
= g_strdup(sip
->sipdomain
);
2332 hosttoconnect
= g_strdup(purple_account_get_string(account
, "proxy", sip
->sipdomain
));
2336 purple_debug_info("sipe", "HosttoConnect->%s\n", hosttoconnect
);
2339 sip
->gsc
= purple_ssl_connect(account
,hosttoconnect
, purple_account_get_int(account
, "port", 5061), login_cb_ssl
, sipe_ssl_connect_failure
, gc
);
2342 sip
->srv_query_data
= purple_srv_resolve("sip",
2343 sip
->udp
? "udp" : "tcp", hosttoconnect
, srvresolved
, sip
);
2346 g_free(hosttoconnect
);
2349 static void sipe_close(PurpleConnection
*gc
)
2351 struct sipe_account_data
*sip
= gc
->proto_data
;
2355 do_register_exp(sip
, 0);
2356 connection_free_all(sip
);
2358 if (sip
->query_data
!= NULL
)
2359 purple_dnsquery_destroy(sip
->query_data
);
2361 if (sip
->srv_query_data
!= NULL
)
2362 purple_srv_cancel(sip
->srv_query_data
);
2364 if (sip
->listen_data
!= NULL
)
2365 purple_network_listen_cancel(sip
->listen_data
);
2367 g_free(sip
->sipdomain
);
2368 g_free(sip
->username
);
2369 g_free(sip
->password
);
2370 g_free(sip
->registrar
.nonce
);
2371 g_free(sip
->registrar
.opaque
);
2372 g_free(sip
->registrar
.target
);
2373 g_free(sip
->registrar
.realm
);
2374 g_free(sip
->registrar
.digest_session_key
);
2375 g_free(sip
->proxy
.nonce
);
2376 g_free(sip
->proxy
.opaque
);
2377 g_free(sip
->proxy
.target
);
2378 g_free(sip
->proxy
.realm
);
2379 g_free(sip
->proxy
.digest_session_key
);
2381 purple_circ_buffer_destroy(sip
->txbuf
);
2382 g_free(sip
->realhostname
);
2383 if (sip
->listenpa
) purple_input_remove(sip
->listenpa
);
2384 if (sip
->tx_handler
) purple_input_remove(sip
->tx_handler
);
2385 if (sip
->resendtimeout
) purple_timeout_remove(sip
->resendtimeout
);
2386 if (sip
->registertimeout
) purple_timeout_remove(sip
->registertimeout
);
2388 g_free(gc
->proto_data
);
2389 gc
->proto_data
= NULL
;
2392 /* not needed since privacy is checked for every subscribe */
2393 static void dummy_add_deny(PurpleConnection
*gc
, const char *name
) {
2396 static void dummy_permit_deny(PurpleConnection
*gc
)
2400 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
)
2406 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
)
2412 static void sipe_plugin_destroy(PurplePlugin
*plugin
)
2416 static PurplePlugin
*my_protocol
= NULL
;
2418 static PurplePluginProtocolInfo prpl_info
=
2421 NULL
, /* user_splits */
2422 NULL
, /* protocol_options */
2423 NO_BUDDY_ICONS
, /* icon_spec */
2424 sipe_list_icon
, /* list_icon */
2425 NULL
, /* list_emblems */
2426 NULL
, /* status_text */
2427 NULL
, /* tooltip_text */
2428 sipe_status_types
, /* away_states */
2429 NULL
, /* blist_node_menu */
2430 NULL
, /* chat_info */
2431 NULL
, /* chat_info_defaults */
2432 sipe_login
, /* login */
2433 sipe_close
, /* close */
2434 sipe_im_send
, /* send_im */
2435 NULL
, /* set_info */
2436 // sipe_typing, /* send_typing */
2437 NULL
, /* send_typing */
2438 NULL
, /* get_info */
2439 sipe_set_status
, /* set_status */
2440 NULL
, /* set_idle */
2441 NULL
, /* change_passwd */
2442 sipe_add_buddy
, /* add_buddy */
2443 NULL
, /* add_buddies */
2444 sipe_remove_buddy
, /* remove_buddy */
2445 NULL
, /* remove_buddies */
2446 dummy_add_deny
, /* add_permit */
2447 dummy_add_deny
, /* add_deny */
2448 dummy_add_deny
, /* rem_permit */
2449 dummy_add_deny
, /* rem_deny */
2450 dummy_permit_deny
, /* set_permit_deny */
2451 NULL
, /* join_chat */
2452 NULL
, /* reject_chat */
2453 NULL
, /* get_chat_name */
2454 NULL
, /* chat_invite */
2455 NULL
, /* chat_leave */
2456 NULL
, /* chat_whisper */
2457 NULL
, /* chat_send */
2458 sipe_keep_alive
, /* keepalive */
2459 NULL
, /* register_user */
2460 NULL
, /* get_cb_info */
2461 NULL
, /* get_cb_away */
2462 NULL
, /* alias_buddy */
2463 NULL
, /* group_buddy */
2464 NULL
, /* rename_group */
2465 NULL
, /* buddy_free */
2466 NULL
, /* convo_closed */
2467 NULL
, /* normalize */
2468 NULL
, /* set_buddy_icon */
2469 NULL
, /* remove_group */
2470 NULL
, /* get_cb_real_name */
2471 NULL
, /* set_chat_topic */
2472 NULL
, /* find_blist_chat */
2473 NULL
, /* roomlist_get_list */
2474 NULL
, /* roomlist_cancel */
2475 NULL
, /* roomlist_expand_category */
2476 NULL
, /* can_receive_file */
2477 NULL
, /* send_file */
2478 NULL
, /* new_xfer */
2479 NULL
, /* offline_message */
2480 NULL
, /* whiteboard_prpl_ops */
2481 sipe_send_raw
, /* send_raw */
2485 static PurplePluginInfo info
= {
2486 PURPLE_PLUGIN_MAGIC
,
2487 PURPLE_MAJOR_VERSION
,
2488 PURPLE_MINOR_VERSION
,
2489 PURPLE_PLUGIN_PROTOCOL
, /**< type */
2490 NULL
, /**< ui_requirement */
2492 NULL
, /**< dependencies */
2493 PURPLE_PRIORITY_DEFAULT
, /**< priority */
2494 "prpl-sipe", /**< id */
2495 "SIPE", /**< name */
2496 VERSION
, /**< version */
2497 N_("SIP/SIMPLE Exchange Protocol Plugin"), /** summary */
2498 N_("The SIP/SIMPLE Exchange Protocol Plugin"), /** description */
2499 "Anibal Avelar <avelar@gmail.com>", /**< author */
2500 PURPLE_WEBSITE
, /**< homepage */
2501 sipe_plugin_load
, /**< load */
2502 sipe_plugin_unload
, /**< unload */
2503 sipe_plugin_destroy
, /**< destroy */
2504 NULL
, /**< ui_info */
2505 &prpl_info
, /**< extra_info */
2514 static void init_plugin(PurplePlugin
*plugin
)
2516 PurpleAccountUserSplit
*split
;
2517 PurpleAccountOption
*option
;
2519 purple_plugin_register(plugin
);
2521 //split = purple_account_user_split_new(_("Server"), "", '@');
2522 //prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2523 option
= purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
2524 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2525 option
= purple_account_option_string_new(_("Proxy Server"), "proxy", "");
2526 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2528 /*option = purple_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
2529 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2531 option
= purple_account_option_bool_new(_("Use SSL/TLS"), "ssl", FALSE
);
2532 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
,option
);
2534 option
= purple_account_option_bool_new(_("Use UDP"), "udp", FALSE
);
2535 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2537 option
= purple_account_option_int_new(_("Connect port"), "port", 5060);
2538 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2540 option
= purple_account_option_bool_new(_("Use Kerberos"), "krb5", FALSE
);
2541 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2543 // XXX FIXME: Add code to programmatically determine if a KRB REALM is specified in /etc/krb5.conf
2544 option
= purple_account_option_string_new(_("Kerberos Realm"), "krb5_realm", "");
2545 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2547 /*option = purple_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2548 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2549 option = purple_account_option_string_new(_("Proxy"), "proxy", "");
2550 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2551 option
= purple_account_option_string_new(_("Auth User"), "authuser", "");
2552 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2553 option
= purple_account_option_string_new(_("Auth Domain"), "authdomain", "");
2554 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2555 my_protocol
= plugin
;
2559 /* I had to redefined the function for it load, but works */
2560 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
2561 plugin
->info
= &(info
);
2562 init_plugin((plugin
));
2563 sipe_plugin_load((plugin
));
2564 return purple_plugin_register(plugin
);