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
28 #include "sip-internal.h"
30 #include "accountopt.h"
32 #include "conversation.h"
50 #include "gaim-compat.h"
52 static char *gentag() {
53 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
56 static char *genbranch() {
57 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
58 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
59 rand() & 0xFFFF, rand() & 0xFFFF);
62 static char *gencallid() {
63 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
64 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
65 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
66 rand() & 0xFFFF, rand() & 0xFFFF);
69 static const char *sipe_list_icon(GaimAccount
*a
, GaimBuddy
*b
) {
73 static void sipe_keep_alive(GaimConnection
*gc
) {
74 struct sipe_account_data
*sip
= gc
->proto_data
;
75 if(sip
->udp
) { /* in case of UDP send a packet only with a 0 byte to
76 remain in the NAT table */
77 gchar buf
[2] = {0, 0};
78 gaim_debug_info("sipe", "sending keep alive\n");
79 sendto(sip
->fd
, buf
, 1, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
));
84 static gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
);
86 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
);
87 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
90 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*);
92 static void send_service(struct sipe_account_data
*sip
);
94 static void do_notifies(struct sipe_account_data
*sip
) {
95 GSList
*tmp
= sip
->watcher
;
96 gaim_debug_info("sipe", "do_notifies()\n");
97 //if((sip->republish != -1) || sip->republish < time(NULL)) {
98 // if(gaim_account_get_bool(sip->account, "doservice", TRUE)) {
104 gaim_debug_info("sipe", "notifying %s\n", ((struct sipe_watcher
*)tmp
->data
)->name
);
105 send_notify(sip
, tmp
->data
);
110 static void sipe_set_status(GaimAccount
*account
, GaimStatus
*status
) {
111 GaimStatusPrimitive primitive
= gaim_status_type_get_primitive(gaim_status_get_type(status
));
112 struct sipe_account_data
*sip
= NULL
;
114 if (!gaim_status_is_active(status
))
118 sip
= account
->gc
->proto_data
;
123 if (primitive
== GAIM_STATUS_AVAILABLE
)
124 sip
->status
= g_strdup("available");
126 sip
->status
= g_strdup("busy");
132 static struct sip_connection
*connection_find(struct sipe_account_data
*sip
, int fd
) {
133 struct sip_connection
*ret
= NULL
;
134 GSList
*entry
= sip
->openconns
;
137 if(ret
->fd
== fd
) return ret
;
143 static struct sipe_watcher
*watcher_find(struct sipe_account_data
*sip
,
145 struct sipe_watcher
*watcher
;
146 GSList
*entry
= sip
->watcher
;
148 watcher
= entry
->data
;
149 if(!strcmp(name
, watcher
->name
)) return watcher
;
155 static struct sipe_watcher
*watcher_create(struct sipe_account_data
*sip
,
156 const gchar
*name
, const gchar
*callid
, const gchar
*ourtag
,
157 const gchar
*theirtag
, gboolean needsxpidf
) {
158 struct sipe_watcher
*watcher
= g_new0(struct sipe_watcher
, 1);
159 watcher
->name
= g_strdup(name
);
160 watcher
->dialog
.callid
= g_strdup(callid
);
161 watcher
->dialog
.ourtag
= g_strdup(ourtag
);
162 watcher
->dialog
.theirtag
= g_strdup(theirtag
);
163 watcher
->needsxpidf
= needsxpidf
;
164 sip
->watcher
= g_slist_append(sip
->watcher
, watcher
);
168 static void watcher_remove(struct sipe_account_data
*sip
, const gchar
*name
) {
169 struct sipe_watcher
*watcher
= watcher_find(sip
, name
);
170 sip
->watcher
= g_slist_remove(sip
->watcher
, watcher
);
171 g_free(watcher
->name
);
172 g_free(watcher
->dialog
.callid
);
173 g_free(watcher
->dialog
.ourtag
);
174 g_free(watcher
->dialog
.theirtag
);
178 static struct sip_connection
*connection_create(struct sipe_account_data
*sip
, int fd
) {
179 struct sip_connection
*ret
= g_new0(struct sip_connection
, 1);
181 sip
->openconns
= g_slist_append(sip
->openconns
, ret
);
185 static void connection_remove(struct sipe_account_data
*sip
, int fd
) {
186 struct sip_connection
*conn
= connection_find(sip
, fd
);
187 sip
->openconns
= g_slist_remove(sip
->openconns
, conn
);
188 if(conn
->inputhandler
) gaim_input_remove(conn
->inputhandler
);
193 static void connection_free_all(struct sipe_account_data
*sip
) {
194 struct sip_connection
*ret
= NULL
;
195 GSList
*entry
= sip
->openconns
;
198 connection_remove(sip
, ret
->fd
);
199 entry
= sip
->openconns
;
203 static void sipe_add_buddy(GaimConnection
*gc
, GaimBuddy
*buddy
, GaimGroup
*group
)
205 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
206 struct sipe_buddy
*b
;
207 if(strncmp("sip:", buddy
->name
, 4)) {
208 gchar
*buf
= g_strdup_printf("sip:%s", buddy
->name
);
209 gaim_blist_rename_buddy(buddy
, buf
);
212 if(!g_hash_table_lookup(sip
->buddies
, buddy
->name
)) {
213 b
= g_new0(struct sipe_buddy
, 1);
214 gaim_debug_info("sipe", "sipe_add_buddy %s\n", buddy
->name
);
215 b
->name
= g_strdup(buddy
->name
);
216 g_hash_table_insert(sip
->buddies
, b
->name
, b
);
218 gaim_debug_info("sipe", "buddy %s already in internal list\n", buddy
->name
);
222 static void sipe_get_buddies(GaimConnection
*gc
) {
223 GaimBlistNode
*gnode
, *cnode
, *bnode
;
225 gaim_debug_info("sipe", "sipe_get_buddies\n");
227 for(gnode
= gaim_get_blist()->root
; gnode
; gnode
= gnode
->next
) {
228 if(!GAIM_BLIST_NODE_IS_GROUP(gnode
)) continue;
229 for(cnode
= gnode
->child
; cnode
; cnode
= cnode
->next
) {
230 if(!GAIM_BLIST_NODE_IS_CONTACT(cnode
)) continue;
231 for(bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
232 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode
)) continue;
233 if(((GaimBuddy
*)bnode
)->account
== gc
->account
)
234 sipe_add_buddy(gc
, (GaimBuddy
*)bnode
, (GaimGroup
*)gnode
);
240 static void sipe_remove_buddy(GaimConnection
*gc
, GaimBuddy
*buddy
, GaimGroup
*group
)
242 struct sipe_account_data
*sip
= (struct sipe_account_data
*)gc
->proto_data
;
243 struct sipe_buddy
*b
= g_hash_table_lookup(sip
->buddies
, buddy
->name
);
244 g_hash_table_remove(sip
->buddies
, buddy
->name
);
249 static GList
*sipe_status_types(GaimAccount
*acc
) {
250 GaimStatusType
*type
;
253 type
= gaim_status_type_new_with_attrs(
254 GAIM_STATUS_AVAILABLE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
,
255 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING
),
257 types
= g_list_append(types
, type
);
259 type
= gaim_status_type_new_full(
260 GAIM_STATUS_OFFLINE
, NULL
, NULL
, TRUE
, TRUE
, FALSE
);
261 types
= g_list_append(types
, type
);
266 static gchar
*auth_header(struct sipe_account_data
*sip
,
267 struct sip_auth
*auth
, const gchar
*method
, const gchar
*target
) {
272 const char *authdomain
;
273 const char *authuser
;
275 authdomain
= gaim_account_get_string(sip
->account
, "authdomain", "");
276 authuser
= gaim_account_get_string(sip
->account
, "authuser", sip
->username
);
278 if(!authuser
|| strlen(authuser
) < 1) {
279 authuser
= sip
->username
;
282 if(auth
->type
== 1) { /* Digest */
283 sprintf(noncecount
, "%08d", auth
->nc
++);
284 response
= gaim_cipher_http_digest_calculate_response(
285 "md5", method
, target
, NULL
, NULL
,
286 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
287 gaim_debug(GAIM_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\"\r\n", authuser
, auth
->realm
, auth
->nonce
, target
, noncecount
, response
);
292 } else if(auth
->type
== 2) { /* NTLM */
293 if(auth
->nc
== 3 && auth
->nonce
) {
294 /* TODO: Don't hardcode "gaim" as the hostname */
295 ret
= gaim_ntlm_gen_type3_sipe(authuser
, sip
->password
, "gaim", authdomain
, (const guint8
*)auth
->nonce
, &auth
->flags
);
296 tmp
= g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"\r\n", auth
->opaque
, auth
->realm
, auth
->target
, ret
);
300 tmp
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"\r\n", auth
->realm
, auth
->target
);
304 sprintf(noncecount
, "%08d", auth
->nc
++);
305 response
= gaim_cipher_http_digest_calculate_response(
306 "md5", method
, target
, NULL
, NULL
,
307 auth
->nonce
, noncecount
, NULL
, auth
->digest_session_key
);
308 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "response %s\n", response
);
310 ret
= g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n", authuser
, auth
->realm
, auth
->nonce
, target
, noncecount
, response
);
315 static char *parse_attribute(const char *attrname
, const char *source
) {
316 const char *tmp
, *tmp2
;
318 int len
= strlen(attrname
);
320 if(!strncmp(source
, attrname
, len
)) {
322 tmp2
= g_strstr_len(tmp
, strlen(tmp
), "\"");
324 retval
= g_strndup(tmp
, tmp2
- tmp
);
326 retval
= g_strdup(tmp
);
332 static void fill_auth(struct sipe_account_data
*sip
, gchar
*hdr
, struct sip_auth
*auth
) {
334 const char *authuser
;
338 authuser
= gaim_account_get_string(sip
->account
, "authuser", sip
->username
);
340 if(!authuser
|| strlen(authuser
) < 1) {
341 authuser
= sip
->username
;
345 gaim_debug_error("sipe", "fill_auth: hdr==NULL\n");
349 if(!g_strncasecmp(hdr
, "NTLM", 4)) {
350 gaim_debug_info("sipe", "found NTLM\n");
352 parts
= g_strsplit(hdr
+5, "\", ", 0);
355 gaim_debug_info("sipe", "parts[i] %s\n", parts
[i
]);
356 if((tmp
= parse_attribute("gssapi-data=\"", parts
[i
]))) {
357 auth
->nonce
= g_memdup(gaim_ntlm_parse_type2_sipe(tmp
, &auth
->flags
), 8);
360 if((tmp
= parse_attribute("targetname=\"",
364 else if((tmp
= parse_attribute("realm=\"",
368 else if((tmp
= parse_attribute("opaque=\"", parts
[i
]))) {
375 if(!strstr(hdr
, "gssapi-data")) {
384 parts
= g_strsplit(hdr
, " ", 0);
386 if((tmp
= parse_attribute("nonce=\"", parts
[i
]))) {
389 else if((tmp
= parse_attribute("realm=\"", parts
[i
]))) {
396 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "nonce: %s realm: %s\n", auth
->nonce
? auth
->nonce
: "(null)", auth
->realm
? auth
->realm
: "(null)");
398 auth
->digest_session_key
= gaim_cipher_http_digest_calculate_session_key(
399 "md5", authuser
, auth
->realm
, sip
->password
, auth
->nonce
, NULL
);
405 static void sipe_canwrite_cb(gpointer data
, gint source
, GaimInputCondition cond
) {
406 GaimConnection
*gc
= data
;
407 struct sipe_account_data
*sip
= gc
->proto_data
;
411 max_write
= gaim_circ_buffer_get_max_read(sip
->txbuf
);
414 gaim_input_remove(sip
->tx_handler
);
419 written
= write(sip
->fd
, sip
->txbuf
->outptr
, max_write
);
421 if(written
< 0 && errno
== EAGAIN
)
423 else if(written
<= 0) {
424 /*TODO: do we really want to disconnect on a failure to write?*/
425 gaim_connection_error(gc
, _("Could not write"));
429 gaim_circ_buffer_mark_read(sip
->txbuf
, written
);
432 static void sipe_canwrite_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, GaimInputCondition cond
) {
433 GaimConnection
*gc
= data
;
434 struct sipe_account_data
*sip
= gc
->proto_data
;
438 max_write
= gaim_circ_buffer_get_max_read(sip
->txbuf
);
441 gaim_input_remove(sip
->tx_handler
);
446 written
= purple_ssl_write(sip
->gsc
, sip
->txbuf
->outptr
, max_write
);
448 if(written
< 0 && errno
== EAGAIN
)
450 else if(written
<= 0) {
451 /*TODO: do we really want to disconnect on a failure to write?*/
452 gaim_connection_error(gc
, _("Could not write"));
456 gaim_circ_buffer_mark_read(sip
->txbuf
, written
);
459 static void sipe_input_cb(gpointer data
, gint source
, GaimInputCondition cond
);
461 static void send_later_cb(gpointer data
, gint source
, const gchar
*error
) {
462 GaimConnection
*gc
= data
;
463 struct sipe_account_data
*sip
;
464 struct sip_connection
*conn
;
466 if (!GAIM_CONNECTION_IS_VALID(gc
))
474 gaim_connection_error(gc
, _("Could not connect"));
478 sip
= gc
->proto_data
;
480 sip
->connecting
= FALSE
;
482 sipe_canwrite_cb(gc
, sip
->fd
, GAIM_INPUT_WRITE
);
484 /* If there is more to write now, we need to register a handler */
485 if(sip
->txbuf
->bufused
> 0)
486 sip
->tx_handler
= gaim_input_add(sip
->fd
, GAIM_INPUT_WRITE
,
487 sipe_canwrite_cb
, gc
);
489 conn
= connection_create(sip
, source
);
490 conn
->inputhandler
= gaim_input_add(sip
->fd
, GAIM_INPUT_READ
, sipe_input_cb
, gc
);
493 static void send_later_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, GaimInputCondition cond
) {
494 GaimConnection
*gc
= data
;
495 struct sipe_account_data
*sip
;
496 struct sip_connection
*conn
;
498 if (!GAIM_CONNECTION_IS_VALID(gc
))
500 purple_ssl_close(gsc
);
504 sip
= gc
->proto_data
;
506 sip
->connecting
= FALSE
;
508 sipe_canwrite_cb_ssl(gc
, gsc
, GAIM_INPUT_WRITE
);
510 /* If there is more to write now, we need to register a handler */
511 if(sip
->txbuf
->bufused
> 0)
512 purple_ssl_input_add(gsc
, sipe_canwrite_cb_ssl
, gc
);
514 conn
= connection_create(sip
, gsc
->fd
);
515 purple_ssl_input_add(sip
->gsc
, sipe_input_cb_ssl
, gc
);
519 static void sendlater(GaimConnection
*gc
, const char *buf
) {
520 struct sipe_account_data
*sip
= gc
->proto_data
;
522 if(!sip
->connecting
) {
523 gaim_debug_info("sipe", "connecting to %s port %d\n", sip
->realhostname
? sip
->realhostname
: "{NULL}", sip
->realport
);
525 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, send_later_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
528 if(gaim_proxy_connect(gc
, sip
->account
, sip
->realhostname
, sip
->realport
, send_later_cb
, gc
) == NULL
) {
529 gaim_connection_error(gc
, _("Couldn't create socket"));
532 sip
->connecting
= TRUE
;
535 if(gaim_circ_buffer_get_max_read(sip
->txbuf
) > 0)
536 gaim_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
538 gaim_circ_buffer_append(sip
->txbuf
, buf
, strlen(buf
));
541 static void sendout_pkt(GaimConnection
*gc
, const char *buf
) {
542 struct sipe_account_data
*sip
= gc
->proto_data
;
543 time_t currtime
= time(NULL
);
544 int writelen
= strlen(buf
);
546 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime
), buf
);
548 if(sendto(sip
->fd
, buf
, writelen
, 0, (struct sockaddr
*)&sip
->serveraddr
, sizeof(struct sockaddr_in
)) < writelen
) {
549 gaim_debug_info("sipe", "could not send packet\n");
558 if(sip
->tx_handler
) {
563 ret
= purple_ssl_write(sip
->gsc
, buf
, writelen
);
565 ret
= write(sip
->fd
, buf
, writelen
);
569 if (ret
< 0 && errno
== EAGAIN
)
571 else if(ret
<= 0) { /* XXX: When does this happen legitimately? */
576 if (ret
< writelen
) {
577 if(!sip
->tx_handler
){
579 purple_ssl_input_add(sip
->gsc
, sipe_canwrite_cb_ssl
, gc
);
582 sip
->tx_handler
= gaim_input_add(sip
->fd
,
583 GAIM_INPUT_WRITE
, sipe_canwrite_cb
,
588 /* XXX: is it OK to do this? You might get part of a request sent
589 with part of another. */
590 if(sip
->txbuf
->bufused
> 0)
591 gaim_circ_buffer_append(sip
->txbuf
, "\r\n", 2);
593 gaim_circ_buffer_append(sip
->txbuf
, buf
+ ret
,
599 static int sipe_send_raw(GaimConnection
*gc
, const char *buf
, int len
)
601 sendout_pkt(gc
, buf
);
605 static void sendout_sipmsg(struct sipe_account_data
*sip
, struct sipmsg
*msg
) {
606 GSList
*tmp
= msg
->headers
;
609 GString
*outstr
= g_string_new("");
610 g_string_append_printf(outstr
, "%s %s SIP/2.0\r\n", msg
->method
, msg
->target
);
612 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
613 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
614 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
615 tmp
= g_slist_next(tmp
);
617 g_string_append_printf(outstr
, "\r\n%s", msg
->body
? msg
->body
: "");
618 sendout_pkt(sip
->gc
, outstr
->str
);
619 g_string_free(outstr
, TRUE
);
622 static void send_sip_response(GaimConnection
*gc
, struct sipmsg
*msg
, int code
,
623 const char *text
, const char *body
) {
624 GSList
*tmp
= msg
->headers
;
627 GString
*outstr
= g_string_new("");
629 /* When sending the acknowlegements and errors, the content length from the original
630 message is still here, but there is no body; we need to make sure we're sending the
631 correct content length */
632 sipmsg_remove_header(msg
, "Content-Length");
635 sprintf(len
, "%" G_GSIZE_FORMAT
, strlen(body
));
636 sipmsg_add_header(msg
, "Content-Length", len
);
639 sipmsg_add_header(msg
, "Content-Length", "0");
640 g_string_append_printf(outstr
, "SIP/2.0 %d %s\r\n", code
, text
);
642 name
= ((struct siphdrelement
*) (tmp
->data
))->name
;
643 value
= ((struct siphdrelement
*) (tmp
->data
))->value
;
645 g_string_append_printf(outstr
, "%s: %s\r\n", name
, value
);
646 tmp
= g_slist_next(tmp
);
648 g_string_append_printf(outstr
, "\r\n%s", body
? body
: "");
649 sendout_pkt(gc
, outstr
->str
);
650 g_string_free(outstr
, TRUE
);
653 static void transactions_remove(struct sipe_account_data
*sip
, struct transaction
*trans
) {
654 if(trans
->msg
) sipmsg_free(trans
->msg
);
655 sip
->transactions
= g_slist_remove(sip
->transactions
, trans
);
659 static void transactions_add_buf(struct sipe_account_data
*sip
, const gchar
*buf
, void *callback
) {
660 struct transaction
*trans
= g_new0(struct transaction
, 1);
661 trans
->time
= time(NULL
);
662 trans
->msg
= sipmsg_parse_msg(buf
);
663 trans
->cseq
= sipmsg_find_header(trans
->msg
, "CSeq");
664 trans
->callback
= callback
;
665 sip
->transactions
= g_slist_append(sip
->transactions
, trans
);
668 static struct transaction
*transactions_find(struct sipe_account_data
*sip
, struct sipmsg
*msg
) {
669 struct transaction
*trans
;
670 GSList
*transactions
= sip
->transactions
;
671 gchar
*cseq
= sipmsg_find_header(msg
, "CSeq");
673 while(transactions
) {
674 trans
= transactions
->data
;
675 if(!strcmp(trans
->cseq
, cseq
)) {
678 transactions
= transactions
->next
;
684 static void send_sip_request(GaimConnection
*gc
, const gchar
*method
,
685 const gchar
*url
, const gchar
*to
, const gchar
*addheaders
,
686 const gchar
*body
, struct sip_dialog
*dialog
, TransCallback tc
) {
687 struct sipe_account_data
*sip
= gc
->proto_data
;
688 char *callid
= dialog
? g_strdup(dialog
->callid
) : gencallid();
690 const char *addh
= "";
691 gchar
*branch
= genbranch();
695 if(!strcmp(method
, "REGISTER")) {
698 callid
= g_strdup(sip
->regcallid
);
700 else sip
->regcallid
= g_strdup(callid
);
703 if(addheaders
) addh
= addheaders
;
704 if(sip
->registrar
.type
&& !strcmp(method
, "REGISTER")) {
705 buf
= auth_header(sip
, &sip
->registrar
, method
, url
);
706 auth
= g_strdup_printf("Authorization: %s", buf
);
708 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "1 header %s", auth
);
711 if(!strcmp(method
,"SUBSCRIBE") || !strcmp(method
,"SERVICE") || !strcmp(method
,"MESSAGE") || !strcmp(method
,"INVITE") || !strcmp(method
,"NOTIFY")) {
712 buf
= g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"\r\n", sip
->registrar
.realm
, sip
->registrar
.target
);
713 auth
= g_strdup_printf("Proxy-Authorization: %s", buf
);
714 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "3 header %s", auth
);
721 buf
= g_strdup_printf("%s %s SIP/2.0\r\n"
722 "Via: SIP/2.0/%s %s:%d;branch=%s\r\n"
723 /* Don't know what epid is, but LCS wants it */
724 "From: <sip:%s>;tag=%s;epid=1234567890\r\n"
726 "Max-Forwards: 10\r\n"
728 "User-Agent: Gaim/" VERSION
"\r\n"
731 "Content-Length: %" G_GSIZE_FORMAT
"\r\n\r\n%s",
734 sip
->udp
? "UDP" : "TCP",
735 sipe_network_get_local_system_ip(),
739 dialog
? dialog
->ourtag
: tag
,
741 dialog
? ";tag=" : "",
742 dialog
? dialog
->theirtag
: "",
756 /* add to ongoing transactions */
758 transactions_add_buf(sip
, buf
, tc
);
760 sendout_pkt(gc
, buf
);
765 static char *get_contact_register(struct sipe_account_data
*sip
) {
766 return g_strdup_printf("<sip:%s:%d;transport=%s>;methods=\"INVITE, MESSAGE, INFO, SUBSCRIBE, BYE, CANCEL, NOTIFY, ACK, BENOTIFY\";proxy=replace", sipe_network_get_local_system_ip(),sip
->listenport
, sip
->udp
? "udp" : "tcp");
769 static char *get_contact(struct sipe_account_data
*sip
) {
770 //return g_strdup_printf("<sip:%s@%s:%d;maddr=%s;transport=%s>;proxy=replace", sip->username, sip->servername, sip->listenport, sipe_network_get_local_system_ip() , sip->udp ? "udp" : "tcp");
771 return g_strdup_printf("<sip:%s:%d;maddr=%s;transport=%s>;proxy=replace", sip
->username
, sip
->listenport
, sipe_network_get_local_system_ip() , sip
->udp
? "udp" : "tcp");
774 static void do_register_exp(struct sipe_account_data
*sip
, int expire
) {
775 char *uri
= g_strdup_printf("sip:%s", sip
->servername
);
776 char *to
= g_strdup_printf("sip:%s", sip
->username
);
777 char *contact
= get_contact_register(sip
);
778 //char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire);
779 // 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);
780 //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);
781 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
);
784 sip
->registerstatus
= 1;
787 sip
->reregister
= time(NULL
) + expire
- 50;
789 sip
->reregister
= time(NULL
) + 600;
792 send_sip_request(sip
->gc
, "REGISTER", uri
, to
, hdr
, "", NULL
,
793 process_register_response
);
800 static void do_register(struct sipe_account_data
*sip
) {
801 do_register_exp(sip
, sip
->registerexpire
);
804 static gchar
*parse_from(const gchar
*hdr
) {
806 const gchar
*tmp
, *tmp2
= hdr
;
808 if(!hdr
) return NULL
;
809 gaim_debug_info("sipe", "parsing address out of %s\n", hdr
);
810 tmp
= strchr(hdr
, '<');
812 /* i hate the different SIP UA behaviours... */
813 if(tmp
) { /* sip address in <...> */
815 tmp
= strchr(tmp2
, '>');
817 from
= g_strndup(tmp2
, tmp
- tmp2
);
819 gaim_debug_info("sipe", "found < without > in From\n");
823 tmp
= strchr(tmp2
, ';');
825 from
= g_strndup(tmp2
, tmp
- tmp2
);
827 from
= g_strdup(tmp2
);
830 gaim_debug_info("sipe", "got %s\n", from
);
834 static gboolean
process_subscribe_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
) {
837 if(msg
->response
== 200 || msg
->response
== 202) {
838 gaim_debug_info("sipe", "Devolvio un response %d\n", msg
->response
);
842 to
= parse_from(sipmsg_find_header(tc
->msg
, "To")); /* cant be NULL since it is our own msg */
844 /* we can not subscribe -> user is offline (TODO unknown status?) */
846 gaim_prpl_got_user_status(sip
->account
, to
, "offline", NULL
);
851 static void sipe_subscribe(struct sipe_account_data
*sip
, struct sipe_buddy
*buddy
) {
852 gchar
*contact
="Accept: application/pidf+xml, application/xpidf+xml\r\nEvent: presence\r\n";
856 if(strstr(buddy
->name
, "sip:"))
857 to
= g_strdup(buddy
->name
);
859 to
= g_strdup_printf("sip:%s", buddy
->name
);
861 tmp
= get_contact(sip
);
862 contact
= g_strdup_printf("%sContact: %s\r\n", contact
, tmp
);
865 /* subscribe to buddy presence
866 * we dont need to know the status so we do not need a callback */
868 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
,
869 process_subscribe_response
);
874 /* resubscribe before subscription expires */
875 /* add some jitter */
876 buddy
->resubscribe
= time(NULL
)+1140+(rand()%50);
879 static gboolean
sipe_add_lcs_contacts(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
) {
881 xmlnode
*item
, *group
, *isc
;
882 const char *name_group
, *group_id
;
888 struct sipe_buddy
*bs
;
889 struct sipe_group
*gr
;
890 int len
= msg
->bodylen
;
892 //Reserved to max 10 groups. ToDO be dynamic
893 gr
= g_new0(struct sipe_group
, 10);
895 tmp
= sipmsg_find_header(msg
, "Event");
896 if(tmp
&& !strncmp(tmp
, "vnd-microsoft-roaming-contacts", 30)){
898 gaim_debug_info("sipe", "sipe_add_lcs_contacts->%s-%d\n", msg
->body
, len
);
899 /*Convert the contact from XML to Gaim Buddies*/
900 isc
= xmlnode_from_str(msg
->body
, len
);
902 /* ToDo. Find for all groups */
903 //if ((group = xmlnode_get_child(isc, "group"))) {
904 for(group
= xmlnode_get_child(isc
, "group"); group
; group
= xmlnode_get_next_twin(group
)) {
905 name_group
= xmlnode_get_attrib(group
, "name");
906 group_id
= xmlnode_get_attrib(group
, "id");
907 if(!strncmp(name_group
, "~", 1)){
908 name_group
=g_strdup("General");
910 gr
[ng
].name_group
= g_strdup(name_group
);
911 gr
[ng
].id
= g_strdup(group_id
);
912 gaim_debug_info("sipe", "name_group->%s\n", name_group
);
913 g
= gaim_find_group(name_group
);
915 g
= gaim_group_new(name_group
);
916 gaim_blist_add_group(g
, NULL
);
919 g
= gaim_find_group("General");
921 g
= gaim_group_new("General");
922 gaim_blist_add_group(g
, NULL
);
928 for(i
= 0; i
< ng
;i
++){
929 gaim_debug_info("sipe", "id->%s\n", gr
[i
].id
);
930 gaim_debug_info("sipe", "id->%s\n", gr
[i
].name_group
);
933 for(item
= xmlnode_get_child(isc
, "contact"); item
; item
= xmlnode_get_next_twin(item
))
935 const char *uri
, *name
, *groups
;
938 uri
= xmlnode_get_attrib(item
, "uri");
939 name
= xmlnode_get_attrib(item
, "name");
940 groups
= xmlnode_get_attrib(item
, "groups");
941 parts
= g_strsplit(groups
, " ", 0);
942 gaim_debug_info("sipe", "URI->%s,Groups->%s\n", uri
, groups
);
945 gaim_debug_info("sipe", "Groups->parts[i] %s\n", parts
[i
]);
946 if(!strcmp(gr
[i
].id
,parts
[i
])){
947 gaim_debug_info("sipe", "Found Groups->gr[i].id(%s),gr[i].name_group (%s)\n",gr
[i
].id
,gr
[i
].name_group
);
949 buddy_name
= g_strdup_printf("sip:%s", uri
);
951 //b = gaim_find_buddy(sip->account, buddy_name);
952 b
= gaim_find_buddy_in_group(sip
->account
, buddy_name
, gr
[i
].g
);
954 b
= gaim_buddy_new(sip
->account
, buddy_name
, uri
);
958 //sipe_add_buddy(sip->gc, b , gr[i].g);
959 gaim_blist_add_buddy(b
, NULL
, gr
[i
].g
, NULL
);
960 gaim_blist_alias_buddy(b
, uri
);
961 bs
= g_new0(struct sipe_buddy
, 1);
962 bs
->name
= g_strdup(b
->name
);
963 g_hash_table_insert(sip
->buddies
, bs
->name
, bs
);
973 static void sipe_subscribe_buddylist(struct sipe_account_data
*sip
) {
974 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";
977 //to = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
978 to
= g_strdup_printf("sip:%s", sip
->username
);
980 tmp
= get_contact(sip
);
981 contact
= g_strdup_printf("%sContact: %s\r\n", contact
, tmp
);
983 send_sip_request(sip
->gc
, "SUBSCRIBE", to
, to
, contact
, "", NULL
, sipe_add_lcs_contacts
);
988 static void sipe_invite(struct sipe_account_data
*sip
, struct sipe_buddy
*buddy
) {
989 gchar
*contact
= "Supported: com.microsoft.rtc-multiparty\r\n";
992 //to = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
994 if(strstr(buddy
->name
, "sip:"))
995 to
= g_strdup(buddy
->name
);
997 to
= g_strdup_printf("sip:%s", buddy
->name
);
999 tmp
= get_contact(sip
);
1000 contact
= g_strdup_printf("%sContact: %s\r\n", contact
, tmp
);
1003 tmp
= g_strdup_printf("Roster-Manager:sip:%s@%s\r\nEndPoints: <sip:%s@%s>, <sip:%s@%s>", sip
->username
, sip
->servername
,sip
->username
, sip
->servername
, buddy
->name
, sip
->servername
);
1004 contact
= g_strdup_printf("%sContact: %s\r\n", contact
, tmp
);
1007 send_sip_request(sip
->gc
, "INVITE", to
, to
, contact
, "", NULL
, NULL
);
1013 static void sipe_buddy_resub(char *name
, struct sipe_buddy
*buddy
, struct sipe_account_data
*sip
) {
1014 time_t curtime
= time(NULL
);
1015 gaim_debug_info("sipe", "buddy resub\n");
1016 if(buddy
->resubscribe
< curtime
) {
1017 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "sipe_buddy_resub %s\n", name
);
1018 sipe_subscribe(sip
, buddy
);
1022 static gboolean
resend_timeout(struct sipe_account_data
*sip
) {
1023 GSList
*tmp
= sip
->transactions
;
1024 time_t currtime
= time(NULL
);
1026 struct transaction
*trans
= tmp
->data
;
1028 gaim_debug_info("sipe", "have open transaction age: %d\n", currtime
- trans
->time
);
1029 if((currtime
- trans
->time
> 5) && trans
->retries
>= 1) {
1032 if((currtime
- trans
->time
> 2) && trans
->retries
== 0) {
1034 sendout_sipmsg(sip
, trans
->msg
);
1041 static gboolean
subscribe_timeout(struct sipe_account_data
*sip
) {
1043 time_t curtime
= time(NULL
);
1044 /* register again if first registration expires */
1045 if(sip
->reregister
< curtime
) {
1048 /* check for every subscription if we need to resubscribe */
1049 //Fixxxer we need resub?
1050 g_hash_table_foreach(sip
->buddies
, (GHFunc
)sipe_buddy_resub
, (gpointer
)sip
);
1052 /* remove a timed out suscriber */
1056 struct sipe_watcher
*watcher
= tmp
->data
;
1057 if(watcher
->expire
< curtime
) {
1058 watcher_remove(sip
, watcher
->name
);
1061 if(tmp
) tmp
= tmp
->next
;
1067 static void sipe_send_message(struct sipe_account_data
*sip
, const char *to
, const char *msg
, const char *type
) {
1071 if(strncmp("sip:", to
, 4)) {
1072 fullto
= g_strdup_printf("sip:%s", to
);
1074 fullto
= g_strdup(to
);
1077 hdr
= g_strdup_printf("Content-Type: %s\r\n", type
);
1079 hdr
= g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
1081 tmp
= get_contact(sip
);
1082 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1085 send_sip_request(sip
->gc
, "MESSAGE", fullto
, fullto
, hdr
, msg
, NULL
, NULL
);
1090 static int sipe_im_send(GaimConnection
*gc
, const char *who
, const char *what
, GaimMessageFlags flags
) {
1091 struct sipe_account_data
*sip
= gc
->proto_data
;
1092 char *to
= g_strdup(who
);
1093 char *text
= gaim_unescape_html(what
);
1094 sipe_send_message(sip
, to
, text
, NULL
);
1100 static void process_incoming_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
) {
1103 gboolean found
= FALSE
;
1105 from
= parse_from(sipmsg_find_header(msg
, "From"));
1109 gaim_debug_info("sipe", "got message from %s: %s\n", from
, msg
->body
);
1111 contenttype
= sipmsg_find_header(msg
, "Content-Type");
1112 if(!contenttype
|| !strncmp(contenttype
, "text/plain", 10) || !strncmp(contenttype
, "text/html", 9)) {
1113 serv_got_im(sip
->gc
, from
, msg
->body
, 0, time(NULL
));
1114 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1117 if(!strncmp(contenttype
, "application/im-iscomposing+xml", 30)) {
1118 xmlnode
*isc
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1123 gaim_debug_info("sipe", "process_incoming_message: can not parse iscomposing\n");
1127 state
= xmlnode_get_child(isc
, "state");
1130 gaim_debug_info("sipe", "process_incoming_message: no state found\n");
1135 statedata
= xmlnode_get_data(state
);
1137 if(strstr(statedata
, "active")) serv_got_typing(sip
->gc
, from
, 0, GAIM_TYPING
);
1138 else serv_got_typing_stopped(sip
->gc
, from
);
1143 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1147 gaim_debug_info("sipe", "got unknown mime-type");
1148 send_sip_response(sip
->gc
, msg
, 415, "Unsupported media type", NULL
);
1154 gboolean
process_register_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
) {
1156 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "in process register response response: %d\n", msg
->response
);
1157 switch (msg
->response
) {
1159 sip
->registerstatus
= 3;
1160 gaim_connection_set_state(sip
->gc
, GAIM_CONNECTED
);
1162 // if(sip->registerstatus < 3) { /* registered */
1166 /* get buddies from blist */
1167 sipe_get_buddies(sip
->gc
);
1169 subscribe_timeout(sip
);
1171 tmp
= sipmsg_find_header(msg
, "Allow-Events");
1172 if(tmp
&& strstr(tmp
, "vnd-microsoft-provisioning")){
1173 sipe_subscribe_buddylist(sip
);
1178 if(sip
->registerstatus
!= 2) {
1179 gaim_debug_info("sipe", "REGISTER retries %d\n", sip
->registrar
.retries
);
1180 if(sip
->registrar
.retries
> 3) {
1181 sip
->gc
->wants_to_die
= TRUE
;
1182 gaim_connection_error(sip
->gc
, _("Wrong Password"));
1185 tmp
= sipmsg_find_header(msg
, "WWW-Authenticate");
1186 fill_auth(sip
, tmp
, &sip
->registrar
);
1187 sip
->registerstatus
= 2;
1195 static void process_incoming_notify(struct sipe_account_data
*sip
, struct sipmsg
*msg
) {
1200 xmlnode
*basicstatus
= NULL
, *tuple
, *status
;
1201 gboolean isonline
= FALSE
;
1203 fromhdr
= sipmsg_find_header(msg
, "From");
1204 from
= parse_from(fromhdr
);
1207 pidf
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
1210 gaim_debug_info("sipe", "process_incoming_notify: no parseable pidf\n");
1214 gaim_debug_info("sipe", "process_incoming_notify: body(%s)\n",msg
->body
);
1216 if ((tuple
= xmlnode_get_child(pidf
, "tuple")))
1217 if ((status
= xmlnode_get_child(tuple
, "status")))
1218 basicstatus
= xmlnode_get_child(status
, "basic");
1221 gaim_debug_info("sipe", "process_incoming_notify: no basic found\n");
1226 tmp2
= xmlnode_get_data(basicstatus
);
1228 gaim_debug_info("sipe", "process_incoming_notify: basic-status(%s)\n",tmp2
);
1232 gaim_debug_info("sipe", "process_incoming_notify: no basic data found\n");
1237 if(strstr(tmp2
, "open")) {
1243 if(isonline
) gaim_prpl_got_user_status(sip
->account
, from
, "available", NULL
);
1244 else gaim_prpl_got_user_status(sip
->account
, from
, "offline", NULL
);
1249 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
1252 static gchar
*find_tag(const gchar
*hdr
) {
1253 const gchar
*tmp
= strstr(hdr
, ";tag="), *tmp2
;
1255 if(!tmp
) return NULL
;
1257 if((tmp2
= strchr(tmp
, ';'))) {
1258 return g_strndup(tmp
, tmp2
- tmp
);
1260 return g_strdup(tmp
);
1263 static gchar
* gen_xpidf(struct sipe_account_data
*sip
) {
1264 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1266 "<presentity uri=\"sip:%s@%s;method=SUBSCRIBE\"/>\n"
1267 "<display name=\"sip:%s@%s\"/>\n"
1268 "<atom id=\"1234\">\n"
1269 "<address uri=\"sip:%s@%s\">\n"
1270 "<status status=\"%s\"/>\n"
1286 static gchar
* gen_pidf(struct sipe_account_data
*sip
) {
1287 gchar
*doc
= g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1288 "<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@%s\">\n"
1289 "<tuple id=\"0\">\n"
1291 "<basic>open</basic>\n"
1293 " <ep:activity>%s</ep:activity>\n"
1297 "<ci:display-name>FixXxeR</ci:display-name>\n"
1305 static void send_notify(struct sipe_account_data
*sip
, struct sipe_watcher
*watcher
) {
1306 gchar
*doc
= watcher
->needsxpidf
? gen_xpidf(sip
) : gen_pidf(sip
);
1307 gchar
*hdr
= watcher
->needsxpidf
? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n";
1308 send_sip_request(sip
->gc
, "NOTIFY", watcher
->name
, watcher
->name
, hdr
, doc
, &watcher
->dialog
, NULL
);
1312 static gboolean
process_service_response(struct sipe_account_data
*sip
, struct sipmsg
*msg
, struct transaction
*tc
) {
1313 if(msg
->response
!= 200 && msg
->response
!= 408) {
1314 /* never send again */
1315 sip
->republish
= -1;
1320 static void send_service(struct sipe_account_data
*sip
) {
1321 gchar
*uri
= g_strdup_printf("sip:%s@%s", sip
->username
, sip
->servername
);
1322 gchar
*doc
= gen_pidf(sip
);
1323 gchar
*hdr
= g_strdup("Content-Type: application/SOAP+xml\r\n");
1324 gchar
*tmp
= get_contact(sip
);
1325 hdr
= g_strdup_printf("Contact: %s\r\n%s", tmp
, hdr
);
1327 send_sip_request(sip
->gc
, "SERVICE", uri
, uri
,
1329 doc
, NULL
, process_service_response
);
1330 sip
->republish
= time(NULL
) + 500;
1336 static void process_incoming_subscribe(struct sipe_account_data
*sip
, struct sipmsg
*msg
) {
1337 const char *from_hdr
= sipmsg_find_header(msg
, "From");
1338 gchar
*from
= parse_from(from_hdr
);
1339 gchar
*theirtag
= find_tag(from_hdr
);
1340 gchar
*ourtag
= find_tag(sipmsg_find_header(msg
, "To"));
1341 gboolean tagadded
= FALSE
;
1342 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
1343 gchar
*expire
= sipmsg_find_header(msg
, "Expire");
1344 // gchar *ms-received-port =find_received_port(sipmsg_find_header(msg, "Contact"));
1346 struct sipe_watcher
*watcher
= watcher_find(sip
, from
);
1351 if(!watcher
) { /* new subscription */
1352 gchar
*acceptheader
= sipmsg_find_header(msg
, "Accept");
1353 gboolean needsxpidf
= FALSE
;
1354 if(!gaim_privacy_check(sip
->account
, from
)) {
1355 send_sip_response(sip
->gc
, msg
, 202, "Ok", NULL
);
1359 gchar
*tmp
= acceptheader
;
1360 gboolean foundpidf
= FALSE
;
1361 gboolean foundxpidf
= FALSE
;
1362 while(tmp
&& tmp
< acceptheader
+ strlen(acceptheader
)) {
1363 gchar
*tmp2
= strchr(tmp
, ',');
1364 if(tmp2
) *tmp2
= '\0';
1365 if(!strcmp("application/pidf+xml", tmp
))
1367 if(!strcmp("application/xpidf+xml", tmp
))
1372 while(*tmp
== ' ') tmp
++;
1376 if(!foundpidf
&& foundxpidf
) needsxpidf
= TRUE
;
1377 g_free(acceptheader
);
1379 watcher
= watcher_create(sip
, from
, callid
, ourtag
, theirtag
, needsxpidf
);
1382 gchar
*to
= g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg
, "To"), ourtag
);
1383 sipmsg_remove_header(msg
, "To");
1384 sipmsg_add_header(msg
, "To", to
);
1388 watcher
->expire
= time(NULL
) + strtol(expire
, NULL
, 10);
1390 watcher
->expire
= time(NULL
) + 600;
1392 sipmsg_remove_header(msg
, "Contact");
1393 tmp
= get_contact(sip
);
1394 sipmsg_add_header(msg
, "Contact", tmp
);
1396 gaim_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
);
1397 send_sip_response(sip
->gc
, msg
, 200, "Ok", NULL
);
1398 send_notify(sip
, watcher
);
1407 static void process_input_message(struct sipe_account_data
*sip
, struct sipmsg
*msg
) {
1408 gboolean found
= FALSE
;
1409 gaim_debug_info("sipe", "msg->response(%d),msg->method(%s)\n",msg
->response
,msg
->method
);
1410 if(msg
->response
== 0) { /* request */
1411 if(!strcmp(msg
->method
, "MESSAGE")) {
1412 process_incoming_message(sip
, msg
);
1414 } else if(!strcmp(msg
->method
, "NOTIFY")) {
1415 gaim_debug_info("sipe","send->process_incoming_notify\n");
1416 process_incoming_notify(sip
, msg
);
1418 } else if(!strcmp(msg
->method
, "SUBSCRIBE")) {
1419 gaim_debug_info("sipe","send->process_incoming_subscribe\n");
1420 process_incoming_subscribe(sip
, msg
);
1423 send_sip_response(sip
->gc
, msg
, 501, "Not implemented", NULL
);
1425 } else { /* response */
1426 struct transaction
*trans
= transactions_find(sip
, msg
);
1428 if(msg
->response
== 407) {
1429 gchar
*resend
, *auth
, *ptmp
;
1431 if(sip
->proxy
.retries
> 30) return;
1432 sip
->proxy
.retries
++;
1433 /* do proxy authentication */
1435 ptmp
= sipmsg_find_header(msg
, "Proxy-Authenticate");
1437 fill_auth(sip
, ptmp
, &sip
->proxy
);
1438 auth
= auth_header(sip
, &sip
->proxy
, trans
->msg
->method
, trans
->msg
->target
);
1439 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
1440 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
1442 resend
= sipmsg_to_string(trans
->msg
);
1443 /* resend request */
1444 sendout_pkt(sip
->gc
, resend
);
1447 if(msg
->response
== 100) {
1448 /* ignore provisional response */
1449 gaim_debug_info("sipe", "got trying response\n");
1451 sip
->proxy
.retries
= 0;
1452 if(!strcmp(trans
->msg
->method
, "REGISTER")) {
1453 if(msg
->response
== 401) sip
->registrar
.retries
++;
1454 else sip
->registrar
.retries
= 0;
1455 gaim_debug_info("sipe", "RE-REGISTER\n");
1457 if(msg
->response
== 401) {
1458 gchar
*resend
, *auth
, *ptmp
;
1460 if(sip
->registrar
.retries
> 4) return;
1461 sip
->registrar
.retries
++;
1463 ptmp
= sipmsg_find_header(msg
, "WWW-Authenticate");
1465 fill_auth(sip
, ptmp
, &sip
->registrar
);
1466 auth
= auth_header(sip
, &sip
->registrar
, trans
->msg
->method
, trans
->msg
->target
);
1467 sipmsg_remove_header(trans
->msg
, "Proxy-Authorization");
1468 sipmsg_add_header(trans
->msg
, "Proxy-Authorization", auth
);
1470 //sipmsg_remove_header(trans->msg, "Authorization");
1471 //sipmsg_add_header(trans->msg, "Authorization", auth);
1473 resend
= sipmsg_to_string(trans
->msg
);
1474 /* resend request */
1475 sendout_pkt(sip
->gc
, resend
);
1479 if(trans
->callback
) {
1480 /* call the callback to process response*/
1481 (trans
->callback
)(sip
, msg
, trans
);
1483 transactions_remove(sip
, trans
);
1488 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "received response to unknown transaction");
1492 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "received a unknown sip message with method %s and response %d\n", msg
->method
, msg
->response
);
1496 static void process_input(struct sipe_account_data
*sip
, struct sip_connection
*conn
)
1504 /* according to the RFC remove CRLF at the beginning */
1505 while(*cur
== '\r' || *cur
== '\n') {
1508 if(cur
!= conn
->inbuf
) {
1509 memmove(conn
->inbuf
, cur
, conn
->inbufused
- (cur
- conn
->inbuf
));
1510 conn
->inbufused
= strlen(conn
->inbuf
);
1513 /* Received a full Header? */
1514 if((cur
= strstr(conn
->inbuf
, "\r\n\r\n")) != NULL
) {
1515 time_t currtime
= time(NULL
);
1518 gaim_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), conn
->inbuf
);
1519 msg
= sipmsg_parse_header(conn
->inbuf
);
1522 restlen
= conn
->inbufused
- (cur
- conn
->inbuf
);
1523 if(restlen
>= msg
->bodylen
) {
1524 dummy
= g_malloc(msg
->bodylen
+ 1);
1525 memcpy(dummy
, cur
, msg
->bodylen
);
1526 dummy
[msg
->bodylen
] = '\0';
1528 cur
+= msg
->bodylen
;
1529 memmove(conn
->inbuf
, cur
, conn
->inbuflen
- (cur
- conn
->inbuf
));
1530 conn
->inbufused
= strlen(conn
->inbuf
);
1535 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "in process response response: %d\n", msg
->response
);
1536 process_input_message(sip
, msg
);
1538 gaim_debug(GAIM_DEBUG_MISC
, "sipe", "received a incomplete sip msg: %s\n", conn
->inbuf
);
1542 static void sipe_udp_process(gpointer data
, gint source
, GaimInputCondition con
) {
1543 GaimConnection
*gc
= data
;
1544 struct sipe_account_data
*sip
= gc
->proto_data
;
1549 static char buffer
[65536];
1550 if((len
= recv(source
, buffer
, sizeof(buffer
) - 1, 0)) > 0) {
1552 gaim_debug_info("sipe", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime
), buffer
);
1553 msg
= sipmsg_parse_msg(buffer
);
1554 if(msg
) process_input_message(sip
, msg
);
1558 static void sipe_input_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
)
1560 PurpleConnection
*gc
= data
;
1561 struct sipe_account_data
*sip
= gc
->proto_data
;
1563 static char buf
[4096];
1565 /* TODO: It should be possible to make this check unnecessary */
1566 if(!PURPLE_CONNECTION_IS_VALID(gc
)) {
1567 purple_ssl_close(gsc
);
1571 struct sip_connection
*conn
= connection_find(sip
, sip
->gsc
->fd
);
1573 gaim_debug_error("sipe", "Connection not found!\n");
1578 if(conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
1579 conn
->inbuflen
+= SIMPLE_BUF_INC
;
1580 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
1583 len
= purple_ssl_read(gsc
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
1585 if (len
< 0 && errno
== EAGAIN
) {
1586 /* Try again later */
1588 } else if (len
< 0) {
1589 purple_debug_info("sipe", "sipe_input_cb_ssl: read error\n");
1590 connection_remove(sip
, sip
->gsc
->fd
);
1591 if(sip
->fd
== gsc
->fd
) sip
->fd
= -1;
1593 } else if (len
== 0) {
1594 purple_connection_error(gc
, _("Server has disconnected"));
1595 connection_remove(sip
, sip
->gsc
->fd
);
1596 if(sip
->fd
== gsc
->fd
) sip
->fd
= -1;
1600 conn
->inbufused
+= len
;
1601 conn
->inbuf
[conn
->inbufused
] = '\0';
1603 process_input(sip
, conn
);
1607 static void sipe_input_cb(gpointer data
, gint source
, GaimInputCondition cond
)
1609 GaimConnection
*gc
= data
;
1610 struct sipe_account_data
*sip
= gc
->proto_data
;
1612 struct sip_connection
*conn
= connection_find(sip
, source
);
1614 gaim_debug_error("sipe", "Connection not found!\n");
1618 if(conn
->inbuflen
< conn
->inbufused
+ SIMPLE_BUF_INC
) {
1619 conn
->inbuflen
+= SIMPLE_BUF_INC
;
1620 conn
->inbuf
= g_realloc(conn
->inbuf
, conn
->inbuflen
);
1623 len
= read(source
, conn
->inbuf
+ conn
->inbufused
, SIMPLE_BUF_INC
- 1);
1625 if(len
< 0 && errno
== EAGAIN
)
1628 gaim_debug_info("sipe", "sipe_input_cb: read error\n");
1629 connection_remove(sip
, source
);
1630 if(sip
->fd
== source
) sip
->fd
= -1;
1634 conn
->inbufused
+= len
;
1635 conn
->inbuf
[conn
->inbufused
] = '\0';
1637 process_input(sip
, conn
);
1640 /* Callback for new connections on incoming TCP port */
1641 static void sipe_newconn_cb(gpointer data
, gint source
, GaimInputCondition cond
) {
1642 GaimConnection
*gc
= data
;
1643 struct sipe_account_data
*sip
= gc
->proto_data
;
1644 struct sip_connection
*conn
;
1646 int newfd
= accept(source
, NULL
, NULL
);
1648 conn
= connection_create(sip
, newfd
);
1650 conn
->inputhandler
= gaim_input_add(newfd
, GAIM_INPUT_READ
, sipe_input_cb
, gc
);
1653 static void login_cb(gpointer data
, gint source
, const gchar
*error_message
) {
1654 GaimConnection
*gc
= data
;
1655 struct sipe_account_data
*sip
;
1656 struct sip_connection
*conn
;
1658 if (!GAIM_CONNECTION_IS_VALID(gc
))
1666 gaim_connection_error(gc
, _("Could not connect"));
1670 sip
= gc
->proto_data
;
1673 conn
= connection_create(sip
, source
);
1675 sip
->registertimeout
= gaim_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
1679 conn
->inputhandler
= gaim_input_add(sip
->fd
, GAIM_INPUT_READ
, sipe_input_cb
, gc
);
1682 static void login_cb_ssl(gpointer data
, PurpleSslConnection
*gsc
, PurpleInputCondition cond
) {
1683 GaimConnection
*gc
= data
;
1684 struct sipe_account_data
*sip
;
1685 struct sip_connection
*conn
;
1687 if (!GAIM_CONNECTION_IS_VALID(gc
))
1689 purple_ssl_close(gsc
);
1693 sip
= gc
->proto_data
;
1695 conn
= connection_create(sip
, sip
->fd
);
1697 purple_ssl_input_add(gsc
, sipe_input_cb_ssl
, gc
);
1700 static guint
sipe_ht_hash_nick(const char *nick
) {
1701 char *lc
= g_utf8_strdown(nick
, -1);
1702 guint bucket
= g_str_hash(lc
);
1708 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
) {
1709 return (gaim_utf8_strcasecmp(nick1
, nick2
) == 0);
1712 static void sipe_udp_host_resolved_listen_cb(int listenfd
, gpointer data
) {
1713 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
1715 sip
->listen_data
= NULL
;
1717 if(listenfd
== -1) {
1718 gaim_connection_error(sip
->gc
, _("Could not create listen socket"));
1724 sip
->listenport
= gaim_network_get_port_from_fd(sip
->fd
);
1725 sip
->listenfd
= sip
->fd
;
1727 sip
->listenpa
= gaim_input_add(sip
->fd
, GAIM_INPUT_READ
, sipe_udp_process
, sip
->gc
);
1729 sip
->resendtimeout
= gaim_timeout_add(2500, (GSourceFunc
) resend_timeout
, sip
);
1730 sip
->registertimeout
= gaim_timeout_add((rand()%100)+10*1000, (GSourceFunc
)subscribe_timeout
, sip
);
1734 static void sipe_udp_host_resolved(GSList
*hosts
, gpointer data
, const char *error_message
) {
1735 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
1738 sip
->query_data
= NULL
;
1740 if (!hosts
|| !hosts
->data
) {
1741 gaim_connection_error(sip
->gc
, _("Couldn't resolve host"));
1745 addr_size
= GPOINTER_TO_INT(hosts
->data
);
1746 hosts
= g_slist_remove(hosts
, hosts
->data
);
1747 memcpy(&(sip
->serveraddr
), hosts
->data
, addr_size
);
1748 g_free(hosts
->data
);
1749 hosts
= g_slist_remove(hosts
, hosts
->data
);
1751 hosts
= g_slist_remove(hosts
, hosts
->data
);
1752 g_free(hosts
->data
);
1753 hosts
= g_slist_remove(hosts
, hosts
->data
);
1756 /* create socket for incoming connections */
1757 sip
->listen_data
= gaim_network_listen_range(5060, 5160, SOCK_DGRAM
,
1758 sipe_udp_host_resolved_listen_cb
, sip
);
1759 if (sip
->listen_data
== NULL
) {
1760 gaim_connection_error(sip
->gc
, _("Could not create listen socket"));
1765 static void sipe_ssl_connect_failure(PurpleSslConnection
*gsc
, PurpleSslErrorType error
,
1768 PurpleConnection
*gc
= data
;
1769 struct sipe_account_data
*sip
;
1771 /* If the connection is already disconnected, we don't need to do anything else */
1772 if(!PURPLE_CONNECTION_IS_VALID(gc
))
1775 sip
= gc
->proto_data
;
1779 case PURPLE_SSL_CONNECT_FAILED
:
1780 purple_connection_error(gc
, _("Connection Failed"));
1782 case PURPLE_SSL_HANDSHAKE_FAILED
:
1783 purple_connection_error(gc
, _("SSL Handshake Failed"));
1789 sipe_tcp_connect_listen_cb(int listenfd
, gpointer data
) {
1790 struct sipe_account_data
*sip
= (struct sipe_account_data
*) data
;
1791 GaimProxyConnectData
*connect_data
;
1793 sip
->listen_data
= NULL
;
1795 sip
->listenfd
= listenfd
;
1796 if(sip
->listenfd
== -1) {
1797 gaim_connection_error(sip
->gc
, _("Could not create listen socket"));
1801 gaim_debug_info("sipe", "listenfd: %d\n", sip
->listenfd
);
1802 sip
->listenport
= gaim_network_get_port_from_fd(sip
->listenfd
);
1803 sip
->listenpa
= gaim_input_add(sip
->listenfd
, GAIM_INPUT_READ
,
1804 sipe_newconn_cb
, sip
->gc
);
1805 gaim_debug_info("sipe", "connecting to %s port %d\n",
1806 sip
->realhostname
, sip
->realport
);
1807 /* open tcp connection to the server */
1809 sip
->gsc
= purple_ssl_connect(sip
->account
,sip
->realhostname
, sip
->realport
, login_cb_ssl
, sipe_ssl_connect_failure
, sip
->gc
);
1812 connect_data
= gaim_proxy_connect(sip
->gc
, sip
->account
, sip
->realhostname
,
1813 sip
->realport
, login_cb
, sip
->gc
);
1815 if(connect_data
== NULL
) {
1816 gaim_connection_error(sip
->gc
, _("Couldn't create socket"));
1822 static void srvresolved(GaimSrvResponse
*resp
, int results
, gpointer data
) {
1823 struct sipe_account_data
*sip
;
1828 sip
->srv_query_data
= NULL
;
1830 port
= gaim_account_get_int(sip
->account
, "port", 0);
1832 /* find the host to connect to */
1834 hostname
= g_strdup(resp
->hostname
);
1839 if(!gaim_account_get_bool(sip
->account
, "useproxy", FALSE
)) {
1840 hostname
= g_strdup(sip
->servername
);
1842 hostname
= g_strdup(gaim_account_get_string(sip
->account
, "proxy", sip
->servername
));
1846 sip
->realhostname
= hostname
;
1847 sip
->realport
= port
;
1848 if(!sip
->realport
) sip
->realport
= 5060;
1852 /* create socket for incoming connections */
1853 sip
->listen_data
= gaim_network_listen_range(5060, 5160, SOCK_STREAM
,
1854 sipe_tcp_connect_listen_cb
, sip
);
1855 if (sip
->listen_data
== NULL
) {
1856 gaim_connection_error(sip
->gc
, _("Could not create listen socket"));
1860 gaim_debug_info("sipe", "using udp with server %s and port %d\n", hostname
, port
);
1862 sip
->query_data
= gaim_dnsquery_a(hostname
, port
, sipe_udp_host_resolved
, sip
);
1863 if (sip
->query_data
== NULL
) {
1864 gaim_connection_error(sip
->gc
, _("Could not resolve hostname"));
1869 static void sipe_login(GaimAccount
*account
)
1872 struct sipe_account_data
*sip
;
1874 gchar
*hosttoconnect
;
1876 const char *username
= gaim_account_get_username(account
);
1877 gc
= gaim_account_get_connection(account
);
1879 if (strpbrk(username
, " \t\v\r\n") != NULL
) {
1880 gc
->wants_to_die
= TRUE
;
1881 gaim_connection_error(gc
, _("SIP Exchange usernames may not contain whitespaces"));
1885 if(!gaim_account_get_bool(account
, "ssl", FALSE
)){
1886 if (!purple_ssl_is_supported())
1888 gc
->wants_to_die
= TRUE
;
1889 purple_connection_error(gc
,
1890 _("SSL support is needed for SSL/TLS support. Please install a supported "
1897 gc
->proto_data
= sip
= g_new0(struct sipe_account_data
, 1);
1899 sip
->account
= account
;
1900 sip
->registerexpire
= 900;
1901 sip
->udp
= gaim_account_get_bool(account
, "udp", FALSE
);
1902 sip
->use_ssl
= gaim_account_get_bool(account
, "ssl", FALSE
);
1903 /* TODO: is there a good default grow size? */
1905 sip
->txbuf
= gaim_circ_buffer_new(0);
1907 userserver
= g_strsplit(username
, "@", 2);
1908 gaim_connection_set_display_name(gc
, userserver
[0]);
1909 sip
->username
= g_strdup(g_strjoin("@", userserver
[0], userserver
[1], NULL
));
1910 sip
->servername
= g_strdup(userserver
[1]);
1911 sip
->password
= g_strdup(gaim_connection_get_password(gc
));
1912 g_strfreev(userserver
);
1914 sip
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
1916 gaim_connection_update_progress(gc
, _("Connecting"), 1, 2);
1918 /* TODO: Set the status correctly. */
1919 sip
->status
= g_strdup("available");
1921 if(!gaim_account_get_bool(account
, "useproxy", FALSE
)) {
1922 hosttoconnect
= g_strdup(sip
->servername
);
1924 hosttoconnect
= g_strdup(gaim_account_get_string(account
, "proxy", sip
->servername
));
1928 gaim_debug_info("sipe", "HosttoConnect->%s\n", hosttoconnect
);
1932 sip
->srv_query_data
= gaim_srv_resolve("sip",
1933 sip
->udp
? "udp" : "tcp", hosttoconnect
, srvresolved
, sip
);
1935 g_free(hosttoconnect
);
1938 static void sipe_close(GaimConnection
*gc
)
1940 struct sipe_account_data
*sip
= gc
->proto_data
;
1944 do_register_exp(sip
, 0);
1945 connection_free_all(sip
);
1947 if (sip
->query_data
!= NULL
)
1948 gaim_dnsquery_destroy(sip
->query_data
);
1950 if (sip
->srv_query_data
!= NULL
)
1951 gaim_srv_cancel(sip
->srv_query_data
);
1953 if (sip
->listen_data
!= NULL
)
1954 gaim_network_listen_cancel(sip
->listen_data
);
1956 g_free(sip
->servername
);
1957 g_free(sip
->username
);
1958 g_free(sip
->password
);
1959 g_free(sip
->registrar
.nonce
);
1960 g_free(sip
->registrar
.opaque
);
1961 g_free(sip
->registrar
.target
);
1962 g_free(sip
->registrar
.realm
);
1963 g_free(sip
->registrar
.digest_session_key
);
1964 g_free(sip
->proxy
.nonce
);
1965 g_free(sip
->proxy
.opaque
);
1966 g_free(sip
->proxy
.target
);
1967 g_free(sip
->proxy
.realm
);
1968 g_free(sip
->proxy
.digest_session_key
);
1970 gaim_circ_buffer_destroy(sip
->txbuf
);
1971 g_free(sip
->realhostname
);
1972 if(sip
->listenpa
) gaim_input_remove(sip
->listenpa
);
1973 if(sip
->tx_handler
) gaim_input_remove(sip
->tx_handler
);
1974 if(sip
->resendtimeout
) gaim_timeout_remove(sip
->resendtimeout
);
1975 if(sip
->registertimeout
) gaim_timeout_remove(sip
->registertimeout
);
1977 g_free(gc
->proto_data
);
1978 gc
->proto_data
= NULL
;
1981 /* not needed since privacy is checked for every subscribe */
1982 static void dummy_add_deny(GaimConnection
*gc
, const char *name
) {
1985 static void dummy_permit_deny(GaimConnection
*gc
) {
1988 static gboolean
sipe_plugin_load(PurplePlugin
*plugin
) {
1993 static gboolean
sipe_plugin_unload(PurplePlugin
*plugin
) {
1998 static void sipe_plugin_destroy(PurplePlugin
*plugin
) {
2001 static PurplePlugin
*my_protocol
= NULL
;
2003 static PurplePluginProtocolInfo prpl_info
=
2006 NULL
, /* user_splits */
2007 NULL
, /* protocol_options */
2008 NO_BUDDY_ICONS
, /* icon_spec */
2009 sipe_list_icon
, /* list_icon */
2010 NULL
, /* list_emblems */
2011 NULL
, /* status_text */
2012 NULL
, /* tooltip_text */
2013 sipe_status_types
, /* away_states */
2014 NULL
, /* blist_node_menu */
2015 NULL
, /* chat_info */
2016 NULL
, /* chat_info_defaults */
2017 sipe_login
, /* login */
2018 sipe_close
, /* close */
2019 sipe_im_send
, /* send_im */
2020 NULL
, /* set_info */
2021 // sipe_typing, /* send_typing */
2022 NULL
, /* send_typing */
2023 NULL
, /* get_info */
2024 sipe_set_status
, /* set_status */
2025 NULL
, /* set_idle */
2026 NULL
, /* change_passwd */
2027 sipe_add_buddy
, /* add_buddy */
2028 NULL
, /* add_buddies */
2029 sipe_remove_buddy
, /* remove_buddy */
2030 NULL
, /* remove_buddies */
2031 dummy_add_deny
, /* add_permit */
2032 dummy_add_deny
, /* add_deny */
2033 dummy_add_deny
, /* rem_permit */
2034 dummy_add_deny
, /* rem_deny */
2035 dummy_permit_deny
, /* set_permit_deny */
2036 NULL
, /* join_chat */
2037 NULL
, /* reject_chat */
2038 NULL
, /* get_chat_name */
2039 NULL
, /* chat_invite */
2040 NULL
, /* chat_leave */
2041 NULL
, /* chat_whisper */
2042 NULL
, /* chat_send */
2043 sipe_keep_alive
, /* keepalive */
2044 NULL
, /* register_user */
2045 NULL
, /* get_cb_info */
2046 NULL
, /* get_cb_away */
2047 NULL
, /* alias_buddy */
2048 NULL
, /* group_buddy */
2049 NULL
, /* rename_group */
2050 NULL
, /* buddy_free */
2051 NULL
, /* convo_closed */
2052 NULL
, /* normalize */
2053 NULL
, /* set_buddy_icon */
2054 NULL
, /* remove_group */
2055 NULL
, /* get_cb_real_name */
2056 NULL
, /* set_chat_topic */
2057 NULL
, /* find_blist_chat */
2058 NULL
, /* roomlist_get_list */
2059 NULL
, /* roomlist_cancel */
2060 NULL
, /* roomlist_expand_category */
2061 NULL
, /* can_receive_file */
2062 NULL
, /* send_file */
2063 NULL
, /* new_xfer */
2064 NULL
, /* offline_message */
2065 NULL
, /* whiteboard_prpl_ops */
2066 sipe_send_raw
, /* send_raw */
2070 static GaimPluginInfo info
= {
2071 PURPLE_PLUGIN_MAGIC
,
2072 PURPLE_MAJOR_VERSION
,
2073 PURPLE_MINOR_VERSION
,
2074 PURPLE_PLUGIN_PROTOCOL
, /**< type */
2075 NULL
, /**< ui_requirement */
2077 NULL
, /**< dependencies */
2078 PURPLE_PRIORITY_DEFAULT
, /**< priority */
2079 "prpl-sipe", /**< id */
2080 "SIPE", /**< name */
2081 VERSION
, /**< version */
2082 N_("SIP/SIMPLE Exchange Protocol Plugin"), /** summary */
2083 N_("The SIP/SIMPLE Exchange Protocol Plugin"), /** description */
2084 "Anibal Avelar <avelar@gmail.com>", /**< author */
2085 PURPLE_WEBSITE
, /**< homepage */
2086 sipe_plugin_load
, /**< load */
2087 sipe_plugin_unload
, /**< unload */
2088 sipe_plugin_destroy
, /**< destroy */
2089 NULL
, /**< ui_info */
2090 &prpl_info
, /**< extra_info */
2099 static void init_plugin(PurplePlugin
*plugin
)
2101 PurpleAccountUserSplit
*split
;
2102 PurpleAccountOption
*option
;
2104 purple_plugin_register(plugin
);
2106 //split = gaim_account_user_split_new(_("Server"), "", '@');
2107 //prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
2108 option
= gaim_account_option_bool_new(_("Use proxy"), "useproxy", FALSE
);
2109 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2110 option
= gaim_account_option_string_new(_("Proxy Server"), "proxy", "");
2111 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2113 /*option = gaim_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "doservice", TRUE);
2114 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2116 option
= purple_account_option_bool_new(_("Use SSL/TLS"), "ssl", FALSE
);
2117 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
,option
);
2119 option
= gaim_account_option_bool_new(_("Use UDP"), "udp", FALSE
);
2120 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2122 option
= gaim_account_option_int_new(_("Connect port"), "port", 5060);
2123 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2125 /*option = gaim_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
2126 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
2127 option = gaim_account_option_string_new(_("Proxy"), "proxy", "");
2128 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);*/
2129 option
= gaim_account_option_string_new(_("Auth User"), "authuser", "");
2130 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2131 option
= gaim_account_option_string_new(_("Auth Domain"), "authdomain", "");
2132 prpl_info
.protocol_options
= g_list_append(prpl_info
.protocol_options
, option
);
2133 my_protocol
= plugin
;
2137 /* I had to redefined the function for it load, but works */
2138 gboolean
purple_init_plugin(PurplePlugin
*plugin
){
2139 plugin
->info
= &(info
);
2140 init_plugin((plugin
));
2141 sipe_plugin_load((plugin
));
2142 return purple_plugin_register(plugin
);