src/protocols/simple/simple.c

branch
gaim
changeset 20470
77693555855f
parent 13071
b98e72d4089a
parent 20469
b2836a24d81e
child 20471
1966704b3e42
equal deleted inserted replaced
13071:b98e72d4089a 20470:77693555855f
1 /**
2 * @file simple.c
3 *
4 * gaim
5 *
6 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
7 *
8 * ***
9 * Thanks to Google's Summer of Code Program and the helpful mentors
10 * ***
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27 #include "internal.h"
28
29 #include "accountopt.h"
30 #include "blist.h"
31 #include "conversation.h"
32 #include "debug.h"
33 #include "notify.h"
34 #include "privacy.h"
35 #include "prpl.h"
36 #include "plugin.h"
37 #include "util.h"
38 #include "version.h"
39 #include "network.h"
40 #include "xmlnode.h"
41
42 #include "simple.h"
43 #include "sipmsg.h"
44 #include "dnssrv.h"
45 #include "ntlm.h"
46
47 static char *gentag() {
48 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);
49 }
50
51 static char *genbranch() {
52 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X",
53 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
54 rand() & 0xFFFF, rand() & 0xFFFF);
55 }
56
57 static char *gencallid() {
58 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx",
59 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
60 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF,
61 rand() & 0xFFFF, rand() & 0xFFFF);
62 }
63
64 static char *get_my_ip() {
65 static char my_ip[42];
66 const char *tmp = gaim_network_get_public_ip();
67
68 if(!tmp || !strcmp(tmp,"0.0.0.0")) {
69 tmp = gaim_network_get_my_ip(-1);
70 }
71 strcpy(my_ip, tmp ? tmp : "0.0.0.0");
72 return my_ip;
73 }
74
75 static const char *simple_list_icon(GaimAccount *a, GaimBuddy *b) {
76 return "simple";
77 }
78
79 static void simple_keep_alive(GaimConnection *gc) {
80 struct simple_account_data *sip = gc->proto_data;
81 if(sip->udp) { /* in case of UDP send a packet only with a 0 byte to
82 remain in the NAT table */
83 gchar buf[2]={0,0};
84 gaim_debug_info("simple", "sending keep alive\n");
85 sendto(sip->fd, buf, 1, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in));
86 }
87 return;
88 }
89
90 static gboolean process_register_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc);
91 static void send_notify(struct simple_account_data *sip, struct simple_watcher *);
92
93 static void send_publish(struct simple_account_data *sip);
94
95 static void do_notifies(struct simple_account_data *sip) {
96 GSList *tmp = sip->watcher;
97 gaim_debug_info("simple", "do_notifies()\n");
98 if((sip->republish != -1) || sip->republish < time(NULL)) {
99 if(gaim_account_get_bool(sip->account, "dopublish", TRUE)) {
100 send_publish(sip);
101 }
102 }
103
104 while(tmp) {
105 gaim_debug_info("simple", "notifying %s\n", ((struct simple_watcher*)tmp->data)->name);
106 send_notify(sip, tmp->data);
107 tmp = tmp->next;
108 }
109 }
110
111 static void simple_set_status(GaimAccount *account, GaimStatus *status) {
112 GaimStatusPrimitive primitive = gaim_status_type_get_primitive(gaim_status_get_type(status));
113 struct simple_account_data *sip = NULL;
114
115 if (!gaim_status_is_active(status))
116 return;
117
118 if (account->gc)
119 sip = account->gc->proto_data;
120
121 if (sip)
122 {
123 g_free(sip->status);
124 if (primitive == GAIM_STATUS_AVAILABLE)
125 sip->status = g_strdup("available");
126 else
127 sip->status = g_strdup("busy");
128
129 do_notifies(sip);
130 }
131 }
132
133 static struct sip_connection *connection_find(struct simple_account_data *sip, int fd) {
134 struct sip_connection *ret = NULL;
135 GSList *entry = sip->openconns;
136 while(entry) {
137 ret = entry->data;
138 if(ret->fd == fd) return ret;
139 entry = entry->next;
140 }
141 return NULL;
142 }
143
144 static struct simple_watcher *watcher_find(struct simple_account_data *sip, gchar *name) {
145 struct simple_watcher *watcher;
146 GSList *entry = sip->watcher;
147 while(entry) {
148 watcher = entry->data;
149 if(!strcmp(name, watcher->name)) return watcher;
150 entry = entry->next;
151 }
152 return NULL;
153 }
154
155 static struct simple_watcher *watcher_create(struct simple_account_data *sip, gchar *name, gchar *callid, gchar *ourtag, gchar *theirtag) {
156 struct simple_watcher *watcher = g_new0(struct simple_watcher,1);
157 watcher->name = g_strdup(name);
158 watcher->dialog.callid = g_strdup(callid);
159 watcher->dialog.ourtag = g_strdup(ourtag);
160 watcher->dialog.theirtag = g_strdup(theirtag);
161 sip->watcher = g_slist_append(sip->watcher, watcher);
162 return watcher;
163 }
164
165 static void watcher_remove(struct simple_account_data *sip, gchar *name) {
166 struct simple_watcher *watcher = watcher_find(sip, name);
167 sip->watcher = g_slist_remove(sip->watcher, watcher);
168 g_free(watcher->name);
169 g_free(watcher->dialog.callid);
170 g_free(watcher->dialog.ourtag);
171 g_free(watcher->dialog.theirtag);
172 g_free(watcher);
173 }
174
175 static struct sip_connection *connection_create(struct simple_account_data *sip, int fd) {
176 struct sip_connection *ret = g_new0(struct sip_connection,1);
177 ret->fd = fd;
178 sip->openconns = g_slist_append(sip->openconns, ret);
179 return ret;
180 }
181
182 static void connection_remove(struct simple_account_data *sip, int fd) {
183 struct sip_connection *conn = connection_find(sip, fd);
184 sip->openconns = g_slist_remove(sip->openconns, conn);
185 if(conn->inputhandler) gaim_input_remove(conn->inputhandler);
186 g_free(conn->inbuf);
187 g_free(conn);
188 }
189
190 static void connection_free_all(struct simple_account_data *sip) {
191 struct sip_connection *ret = NULL;
192 GSList *entry = sip->openconns;
193 while(entry) {
194 ret = entry->data;
195 connection_remove(sip, ret->fd);
196 entry = sip->openconns;
197 }
198 }
199
200 static void simple_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
201 {
202 struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
203 struct simple_buddy *b;
204 if(strncmp("sip:", buddy->name,4)) {
205 gchar *buf = g_strdup_printf("sip:%s",buddy->name);
206 gaim_blist_rename_buddy(buddy, buf);
207 g_free(buf);
208 }
209 if(!g_hash_table_lookup(sip->buddies, buddy->name)) {
210 b = g_new0(struct simple_buddy, 1);
211 gaim_debug_info("simple","simple_add_buddy %s\n",buddy->name);
212 b->name = g_strdup(buddy->name);
213 g_hash_table_insert(sip->buddies, b->name, b);
214 } else {
215 gaim_debug_info("simple","buddy %s already in internal list\n", buddy->name);
216 }
217 }
218
219 static void simple_get_buddies(GaimConnection *gc) {
220 GaimBlistNode *gnode, *cnode, *bnode;
221
222 gaim_debug_info("simple","simple_get_buddies\n");
223
224 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
225 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) continue;
226 for(cnode = gnode->child; cnode; cnode = cnode->next) {
227 if(!GAIM_BLIST_NODE_IS_CONTACT(cnode)) continue;
228 for(bnode = cnode->child; bnode; bnode = bnode->next) {
229 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) continue;
230 if(((GaimBuddy*)bnode)->account == gc->account)
231 simple_add_buddy(gc, (GaimBuddy*)bnode, (GaimGroup *)gnode);
232 }
233 }
234 }
235 }
236
237 static void simple_remove_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
238 {
239 struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data;
240 struct simple_buddy *b = g_hash_table_lookup(sip->buddies, buddy->name);
241 g_hash_table_remove(sip->buddies, buddy->name);
242 g_free(b->name);
243 g_free(b);
244 }
245
246 static GList *simple_status_types(GaimAccount *acc) {
247 GaimStatusType *type;
248 GList *types = NULL;
249
250 type = gaim_status_type_new_with_attrs(
251 GAIM_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
252 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING),
253 NULL);
254 types = g_list_append(types, type);
255
256 type = gaim_status_type_new_full(
257 GAIM_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
258 types = g_list_append(types, type);
259
260 return types;
261 }
262
263 static gchar *auth_header(struct simple_account_data *sip, struct sip_auth *auth, gchar *method, gchar *target) {
264 gchar noncecount[9];
265 gchar *response;
266 gchar *ret;
267 gchar *tmp;
268
269 if(auth->type == 1) { /* Digest */
270 sprintf(noncecount, "%08d", auth->nc++);
271 response = gaim_cipher_http_digest_calculate_response(
272 "md5", method, target, NULL, NULL,
273 auth->nonce, noncecount, NULL, auth->digest_session_key);
274 gaim_debug(GAIM_DEBUG_MISC, "simple", "response %s\n", response);
275
276 ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n", sip->username, auth->realm, auth->nonce, target, noncecount, response);
277 g_free(response);
278 return ret;
279 } else if(auth->type == 2) { /* NTLM */
280 if(auth->nc == 3) {
281 ret = gaim_ntlm_gen_type3(sip->username, sip->password, "gaim", sip->servername, auth->nonce);
282 tmp = g_strdup_printf("NTLM qop=\"auth\" realm=\"%s\" targetname=\"%s\" response=\"%s\"\r\n", auth->realm, auth->target, ret);
283 g_free(ret);
284 return tmp;
285 }
286 ret = gaim_ntlm_gen_type1("gaim", sip->servername);
287 tmp = g_strdup_printf("NTLM qop=\"auth\" realm=\"%s\" targetname=\"%s\" response=\"%s\"\r\n", auth->realm, auth->target, ret);
288 g_free(ret);
289 return tmp;
290 }
291
292 sprintf(noncecount, "%08d", auth->nc++);
293 response = gaim_cipher_http_digest_calculate_response(
294 "md5", method, target, NULL, NULL,
295 auth->nonce, noncecount, NULL, auth->digest_session_key);
296 gaim_debug(GAIM_DEBUG_MISC, "simple", "response %s\n", response);
297
298 ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n", sip->username, auth->realm, auth->nonce, target, noncecount, response);
299 g_free(response);
300 return ret;
301 }
302
303 static char * parse_attribute(const char *attrname, char *source) {
304 char *tmp, *tmp2, *retval = NULL;
305 int len = strlen(attrname);
306
307 if(!strncmp(source, attrname, len)) {
308 tmp = source + len;
309 tmp2 = g_strstr_len(tmp, strlen(tmp), "\"");
310 if(tmp2)
311 retval = g_strndup(tmp, tmp2 - tmp);
312 }
313
314 return retval;
315 }
316
317 static void fill_auth(struct simple_account_data *sip, gchar *hdr, struct sip_auth *auth) {
318 int i=0;
319 char *tmp;
320 gchar **parts;
321 if(!hdr) {
322 gaim_debug_error("simple", "fill_auth: hdr==NULL\n");
323 return;
324 }
325
326 if(!g_strncasecmp(hdr, "NTLM", 4)) {
327 gaim_debug_info("simple", "found NTLM\n");
328 auth->type = 2;
329 if(!auth->nonce && !auth->nc) {
330 parts = g_strsplit(hdr, " ", 0);
331 while(parts[i]) {
332 if((tmp = parse_attribute("targetname=\"",
333 parts[i]))) {
334 auth->target = tmp;
335 }
336 else if((tmp = parse_attribute("realm=\"",
337 parts[i]))) {
338 auth->realm = tmp;
339 }
340 i++;
341 }
342 g_strfreev(parts);
343 parts = NULL;
344 auth->nc = 1;
345 }
346 if(!auth->nonce && auth->nc==2) {
347 auth->nc = 3;
348 auth->nonce = gaim_ntlm_parse_type2(hdr+5);
349 }
350 return;
351 }
352
353 auth->type = 1;
354 parts = g_strsplit(hdr, " ", 0);
355 while(parts[i]) {
356 if((tmp = parse_attribute("nonce=\"", parts[i]))) {
357 auth->nonce = tmp;
358 }
359 else if((tmp = parse_attribute("realm=\"", parts[i]))) {
360 auth->realm = tmp;
361 }
362 i++;
363 }
364 g_strfreev(parts);
365
366 gaim_debug(GAIM_DEBUG_MISC, "simple", "nonce: %s realm: %s ", auth->nonce ? auth->nonce : "(null)", auth->realm ? auth->realm : "(null)");
367
368 auth->digest_session_key = gaim_cipher_http_digest_calculate_session_key(
369 "md5", sip->username, auth->realm, sip->password, auth->nonce, NULL);
370
371 auth->nc=1;
372 }
373
374 static void simple_input_cb(gpointer data, gint source, GaimInputCondition cond);
375
376 static void send_later_cb(gpointer data, gint source, GaimInputCondition cond) {
377 GaimConnection *gc = data;
378 struct simple_account_data *sip = gc->proto_data;
379 struct sip_connection *conn;
380
381 if( source < 0 ) {
382 gaim_connection_error(gc,"Could not connect");
383 return;
384 }
385
386 sip->fd = source;
387 sip->connecting = FALSE;
388 write(sip->fd, sip->sendlater, strlen(sip->sendlater));
389 conn = connection_create(sip, source);
390 conn->inputhandler = gaim_input_add(sip->fd, GAIM_INPUT_READ, simple_input_cb, gc);
391 g_free(sip->sendlater);
392 sip->sendlater = NULL;
393 }
394
395
396 static void sendlater(GaimConnection *gc, const char *buf) {
397 struct simple_account_data *sip = gc->proto_data;
398 int error = 0;
399 if(!sip->connecting) {
400 gaim_debug_info("simple","connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport);
401 error = gaim_proxy_connect(sip->account, sip->realhostname, sip->realport, send_later_cb, gc);
402 if(error) {
403 gaim_connection_error(gc, _("Couldn't create socket"));
404 }
405 sip->connecting = TRUE;
406 }
407 if(sip->sendlater) {
408 gchar *old = sip->sendlater;
409 sip->sendlater = g_strdup_printf("%s\r\n%s",old, buf);
410 g_free(old);
411 } else {
412 sip->sendlater = g_strdup(buf);
413 }
414 }
415
416 static int sendout_pkt(GaimConnection *gc, const char *buf) {
417 struct simple_account_data *sip = gc->proto_data;
418 time_t currtime = time(NULL);
419 int ret = 0;
420
421 gaim_debug(GAIM_DEBUG_MISC, "simple", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf);
422 if(sip->udp) {
423 if(sendto(sip->fd, buf, strlen(buf), 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < strlen(buf)) {
424 gaim_debug_info("simple", "could not send packet\n");
425 }
426 } else {
427 if(sip->fd <0 ) {
428 sendlater(gc, buf);
429 return 0;
430 }
431 ret = write(sip->fd, buf, strlen(buf));
432 if(ret < 0) {
433 sendlater(gc,buf);
434 return 0;
435 }
436 }
437 return ret;
438 }
439
440 static void sendout_sipmsg(struct simple_account_data *sip, struct sipmsg *msg) {
441 GSList *tmp = msg->headers;
442 gchar *name;
443 gchar *value;
444 GString *outstr = g_string_new("");
445 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n", msg->method, msg->target);
446 while(tmp) {
447 name = ((struct siphdrelement*) (tmp->data))->name;
448 value = ((struct siphdrelement*) (tmp->data))->value;
449 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
450 tmp = g_slist_next(tmp);
451 }
452 g_string_append_printf(outstr, "\r\n%s", msg->body ? msg->body : "");
453 sendout_pkt(sip->gc, outstr->str);
454 g_string_free(outstr, TRUE);
455 }
456
457 static void send_sip_response(GaimConnection *gc, struct sipmsg *msg, int code, char *text, char *body) {
458 GSList *tmp = msg->headers;
459 gchar *name;
460 gchar *value;
461 GString *outstr = g_string_new("");
462
463 /* When sending the acknowlegements and errors, the content length from the original
464 message is still here, but there is no body; we need to make sure we're sending the
465 correct content length */
466 sipmsg_remove_header(msg, "Content-Length");
467 if(body) {
468 gchar len[12];
469 sprintf(len, "%" G_GSIZE_FORMAT , strlen(body));
470 sipmsg_add_header(msg, "Content-Length",len);
471 }
472 else
473 sipmsg_add_header(msg, "Content-Length", "0");
474 g_string_append_printf(outstr, "SIP/2.0 %d %s\r\n", code, text);
475 while(tmp) {
476 name = ((struct siphdrelement*) (tmp->data))->name;
477 value = ((struct siphdrelement*) (tmp->data))->value;
478
479 g_string_append_printf(outstr, "%s: %s\r\n", name, value);
480 tmp = g_slist_next(tmp);
481 }
482 g_string_append_printf(outstr, "\r\n%s", body ? body : "");
483 sendout_pkt(gc, outstr->str);
484 g_string_free(outstr, TRUE);
485 }
486
487 static void transactions_remove(struct simple_account_data *sip, struct transaction *trans) {
488 if(trans->msg) sipmsg_free(trans->msg);
489 sip->transactions = g_slist_remove(sip->transactions, trans);
490 g_free(trans);
491 }
492
493 static void transactions_add_buf(struct simple_account_data *sip, gchar *buf, void *callback) {
494 struct transaction *trans = g_new0(struct transaction, 1);
495 trans->time = time(NULL);
496 trans->msg = sipmsg_parse_msg(buf);
497 trans->cseq = sipmsg_find_header(trans->msg, "CSeq");
498 trans->callback = callback;
499 sip->transactions = g_slist_append(sip->transactions, trans);
500 }
501
502 static struct transaction *transactions_find(struct simple_account_data *sip, struct sipmsg *msg) {
503 struct transaction *trans;
504 GSList *transactions = sip->transactions;
505 gchar *cseq = sipmsg_find_header(msg, "CSeq");
506
507 while(transactions) {
508 trans = transactions->data;
509 if(!strcmp(trans->cseq, cseq)) {
510 return trans;
511 }
512 transactions = transactions->next;
513 }
514
515 return (struct transaction *)NULL;
516 }
517
518 static void send_sip_request(GaimConnection *gc, gchar *method, gchar *url, gchar *to, gchar *addheaders, gchar *body, struct sip_dialog *dialog, TransCallback tc) {
519 struct simple_account_data *sip = gc->proto_data;
520 char *callid= dialog ? g_strdup(dialog->callid) : gencallid();
521 char *auth="";
522 char *addh="";
523 gchar *branch = genbranch();
524 char *buf;
525
526 if(!strcmp(method,"REGISTER")) {
527 if(sip->regcallid) callid = g_strdup(sip->regcallid);
528 else sip->regcallid = g_strdup(callid);
529 }
530
531 if(addheaders) addh=addheaders;
532 if(sip->registrar.type && !strcmp(method,"REGISTER")) {
533 buf = auth_header(sip, &sip->registrar, method, url);
534 auth = g_strdup_printf("Authorization: %s", buf);
535 g_free(buf);
536 gaim_debug(GAIM_DEBUG_MISC, "simple", "header %s", auth);
537 }
538
539 if(sip->proxy.type && strcmp(method,"REGISTER")) {
540 buf = auth_header(sip, &sip->proxy, method, url);
541 auth = g_strdup_printf("Proxy-Authorization: %s", buf);
542 g_free(buf);
543 gaim_debug(GAIM_DEBUG_MISC, "simple", "header %s", auth);
544 }
545
546 buf = g_strdup_printf("%s %s SIP/2.0\r\n"
547 "Via: SIP/2.0/%s %s:%d;branch=%s\r\n"
548 "From: <sip:%s@%s>;tag=%s\r\n"
549 "To: <%s>%s%s\r\n"
550 "Max-Forwards: 10\r\n"
551 "CSeq: %d %s\r\n"
552 "User-Agent: Gaim SIP/SIMPLE Plugin\r\n"
553 "Call-ID: %s\r\n"
554 "%s%s"
555 "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s",
556 method,
557 url,
558 sip->udp ? "UDP" : "TCP",
559 get_my_ip(),
560 sip->listenport,
561 branch,
562 sip->username,
563 sip->servername,
564 dialog ? dialog->ourtag : gentag(),
565 to,
566 dialog ? ";tag=" : "",
567 dialog ? dialog->theirtag : "",
568 ++sip->cseq,
569 method,
570 callid,
571 auth,
572 addh,
573 strlen(body),
574 body);
575 g_free(branch);
576 g_free(callid);
577
578 /* add to ongoing transactions */
579
580 transactions_add_buf(sip, buf, tc);
581
582 sendout_pkt(gc,buf);
583
584 g_free(buf);
585 }
586
587 static void do_register_exp(struct simple_account_data *sip, int expire) {
588 char *uri = g_strdup_printf("sip:%s",sip->servername);
589 char *to = g_strdup_printf("sip:%s@%s",sip->username,sip->servername);
590 char *contact = g_strdup_printf("Contact: <sip:%s@%s:%d;transport=%s>;methods=\"MESSAGE, SUBSCRIBE, NOTIFY\"\r\nExpires: %d\r\n", sip->username, get_my_ip(), sip->listenport, sip->udp ? "udp" : "tcp", expire);
591
592 sip->registerstatus = 1;
593
594 if(expire) {
595 sip->reregister = time(NULL) + expire - 50;
596 } else {
597 sip->reregister = time(NULL) + 600;
598 }
599 send_sip_request(sip->gc,"REGISTER",uri,to, contact, "", NULL, process_register_response);
600 g_free(contact);
601 g_free(uri);
602 g_free(to);
603 }
604
605 static void do_register(struct simple_account_data *sip) {
606 do_register_exp(sip, sip->registerexpire);
607 }
608
609 static gchar *parse_from(gchar *hdr) {
610 gchar *from = hdr;
611 gchar *tmp;
612
613 if(!from) return NULL;
614 gaim_debug_info("simple", "parsing address out of %s\n",from);
615 tmp = strchr(from, '<');
616
617 /* i hate the different SIP UA behaviours... */
618 if(tmp) { /* sip address in <...> */
619 from = tmp+1;
620 tmp = strchr(from,'>');
621 if(tmp) {
622 from = g_strndup(from,tmp-from);
623 } else {
624 gaim_debug_info("simple", "found < without > in From\n");
625 return NULL;
626 }
627 } else {
628 tmp = strchr(from, ';');
629 if(tmp) {
630 from = g_strndup(from,tmp-from);
631 } else {
632 from = g_strdup(from);
633 }
634 }
635 gaim_debug_info("simple", "got %s\n",from);
636 return from;
637 }
638
639 static gboolean process_subscribe_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
640 gchar *to = parse_from(sipmsg_find_header(tc->msg,"To")); /* cant be NULL since it is our own msg */
641
642 if(msg->response==200 || msg->response==202) {
643 return TRUE;
644 }
645
646 /* we can not subscribe -> user is offline (TODO unknown status?) */
647
648 gaim_prpl_got_user_status(sip->account, to, "offline", NULL);
649 g_free(to);
650 return TRUE;
651 }
652
653 static void simple_subscribe(struct simple_account_data *sip, struct simple_buddy *buddy) {
654 gchar *contact = "Expires: 300\r\nAccept: application/pidf+xml\r\nEvent: presence\r\n";
655 gchar *to;
656 if(strstr(buddy->name,"sip:")) to = g_strdup(buddy->name);
657 else to = g_strdup_printf("sip:%s",buddy->name);
658 contact = g_strdup_printf("%sContact: <%s@%s>\r\n", contact, sip->username, sip->servername);
659 /* subscribe to buddy presence
660 * we dont need to know the status so we do not need a callback */
661
662 send_sip_request(sip->gc, "SUBSCRIBE",to, to, contact, "", NULL, process_subscribe_response);
663
664 g_free(to);
665 g_free(contact);
666
667 /* resubscribe before subscription expires */
668 /* add some jitter */
669 buddy->resubscribe = time(NULL)+250+(rand()%50);
670 }
671
672 static void simple_buddy_resub(char *name, struct simple_buddy *buddy, struct simple_account_data *sip) {
673 time_t curtime = time(NULL);
674 gaim_debug_info("simple","buddy resub\n");
675 if(buddy->resubscribe < curtime) {
676 gaim_debug(GAIM_DEBUG_MISC, "simple", "simple_buddy_resub %s\n",name);
677 simple_subscribe(sip, buddy);
678 }
679 }
680
681 static gboolean resend_timeout(struct simple_account_data *sip) {
682 GSList *tmp = sip->transactions;
683 time_t currtime = time(NULL);
684 while(tmp) {
685 struct transaction *trans = tmp->data;
686 tmp = tmp->next;
687 gaim_debug_info("simple", "have open transaction age: %d\n", currtime- trans->time);
688 if((currtime - trans->time > 5) && trans->retries >= 1) {
689 /* TODO 408 */
690 } else {
691 if((currtime - trans->time > 2) && trans->retries == 0) {
692 trans->retries++;
693 sendout_sipmsg(sip, trans->msg);
694 }
695 }
696 }
697 return TRUE;
698 }
699
700 static gboolean subscribe_timeout(struct simple_account_data *sip) {
701 GSList *tmp;
702 time_t curtime = time(NULL);
703 /* register again if first registration expires */
704 if(sip->reregister < curtime) {
705 do_register(sip);
706 }
707 /* check for every subscription if we need to resubscribe */
708 g_hash_table_foreach(sip->buddies, (GHFunc)simple_buddy_resub, (gpointer)sip);
709
710 /* remove a timed out suscriber */
711
712 tmp = sip->watcher;
713 while(tmp) {
714 struct simple_watcher *watcher = tmp->data;
715 if(watcher->expire < curtime) {
716 watcher_remove(sip, watcher->name);
717 tmp = sip->watcher;
718 }
719 if(tmp) tmp = tmp->next;
720 }
721
722 return TRUE;
723 }
724
725 static void simple_send_message(struct simple_account_data *sip, char *to, char *msg, char *type) {
726 gchar *hdr;
727 if(type) {
728 hdr = g_strdup_printf("Content-Type: %s\r\n",type);
729 } else {
730 hdr = g_strdup("Content-Type: text/plain\r\n");
731 }
732 send_sip_request(sip->gc, "MESSAGE", to, to, hdr, msg, NULL, NULL);
733 g_free(hdr);
734 }
735
736 static int simple_im_send(GaimConnection *gc, const char *who, const char *what, GaimMessageFlags flags) {
737 struct simple_account_data *sip = gc->proto_data;
738 char *to = g_strdup(who);
739 char *text = gaim_unescape_html(what);
740 simple_send_message(sip, to, text, NULL);
741 g_free(to);
742 g_free(text);
743 return 1;
744 }
745
746 static void process_incoming_message(struct simple_account_data *sip, struct sipmsg *msg) {
747 gchar *from;
748 gchar *contenttype;
749 gboolean found = FALSE;
750
751 from = parse_from(sipmsg_find_header(msg, "From"));
752
753 if(!from) return;
754
755 gaim_debug(GAIM_DEBUG_MISC, "simple", "got message from %s: %s\n", from, msg->body);
756
757 contenttype = sipmsg_find_header(msg, "Content-Type");
758 if(!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) {
759 serv_got_im(sip->gc, from, msg->body, 0, time(NULL));
760 send_sip_response(sip->gc, msg, 200, "OK", NULL);
761 found = TRUE;
762 }
763 if(!strncmp(contenttype, "application/im-iscomposing+xml",30)) {
764 xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen);
765 xmlnode *state;
766 gchar *statedata;
767
768 if(!isc) {
769 gaim_debug_info("simple","process_incoming_message: can not parse iscomposing\n");
770 return;
771 }
772
773 state = xmlnode_get_child(isc, "state");
774
775 if(!state) {
776 gaim_debug_info("simple","process_incoming_message: no state found\n");
777 return;
778 }
779
780 statedata = xmlnode_get_data(state);
781 if(statedata) {
782 if(strstr(statedata,"active")) serv_got_typing(sip->gc, from, 0, GAIM_TYPING);
783 else serv_got_typing_stopped(sip->gc, from);
784 }
785 xmlnode_free(isc);
786 send_sip_response(sip->gc, msg, 200, "OK", NULL);
787 found = TRUE;
788 }
789 if(!found) {
790 gaim_debug_info("simple", "got unknown mime-type");
791 send_sip_response(sip->gc, msg, 415, "Unsupported media type", NULL);
792 }
793 g_free(from);
794 }
795
796
797 gboolean process_register_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
798 gchar *tmp;
799 gaim_debug(GAIM_DEBUG_MISC, "simple", "in process register response response: %d\n", msg->response);
800 switch (msg->response) {
801 case 200:
802 if(sip->registerstatus<3) { /* registered */
803 if(gaim_account_get_bool(sip->account, "dopublish", TRUE)) {
804 send_publish(sip);
805 }
806 }
807 sip->registerstatus=3;
808 gaim_connection_set_state(sip->gc, GAIM_CONNECTED);
809
810 /* get buddies from blist */
811 simple_get_buddies(sip->gc);
812
813 subscribe_timeout(sip);
814 break;
815 case 401:
816 if(sip->registerstatus!=2) {
817 gaim_debug_info("simple","REGISTER retries %d\n",sip->registrar.retries);
818 if(sip->registrar.retries>3) {
819 gaim_connection_error(sip->gc,"Wrong Password");
820 return TRUE;
821 }
822 tmp = sipmsg_find_header(msg, "WWW-Authenticate");
823 fill_auth(sip, tmp, &sip->registrar);
824 sip->registerstatus=2;
825 do_register(sip);
826 }
827 break;
828 }
829 return TRUE;
830 }
831
832 static void process_incoming_notify(struct simple_account_data *sip, struct sipmsg *msg) {
833 gchar *from;
834 gchar *fromhdr;
835 gchar *tmp2;
836 xmlnode *pidf;
837 xmlnode *basicstatus;
838 gboolean isonline = FALSE;
839
840 fromhdr = sipmsg_find_header(msg,"From");
841 from = parse_from(fromhdr);
842 if(!from) return;
843
844 pidf = xmlnode_from_str(msg->body, msg->bodylen);
845
846 if(!pidf) {
847 gaim_debug_info("simple","process_incoming_notify: no parseable pidf\n");
848 return;
849 }
850
851 basicstatus = xmlnode_get_child(xmlnode_get_child(xmlnode_get_child(pidf,"tuple"),"status"), "basic");
852
853 if(!basicstatus) {
854 gaim_debug_info("simple","process_incoming_notify: no basic found\n");
855 return;
856 }
857
858 tmp2 = xmlnode_get_data(basicstatus);
859
860 if(!tmp2) {
861 gaim_debug_info("simple","process_incoming_notify: no basic data found\n");
862 return;
863 }
864
865 if(strstr(tmp2, "open")) {
866 isonline = TRUE;
867 }
868
869 if(isonline) gaim_prpl_got_user_status(sip->account, from, "available", NULL);
870 else gaim_prpl_got_user_status(sip->account, from, "offline", NULL);
871
872 xmlnode_free(pidf);
873
874 g_free(from);
875 send_sip_response(sip->gc, msg, 200, "OK", NULL);
876 }
877
878 static int simple_typing(GaimConnection *gc, const char *name, int typing) {
879 struct simple_account_data *sip = gc->proto_data;
880
881 gchar *xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
882 "<isComposing xmlns=\"urn:ietf:params:xml:ns:im-iscomposing\"\n"
883 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
884 "xsi:schemaLocation=\"urn:ietf:params:xml:ns:im-composing iscomposing.xsd\">\n"
885 "<state>%s</state>\n"
886 "<contenttype>text/plain</contenttype>\n"
887 "<refresh>60</refresh>\n"
888 "</isComposing>";
889 gchar *recv = g_strdup(name);
890 if(typing == GAIM_TYPING) {
891 gchar *msg = g_strdup_printf(xml, "active");
892 simple_send_message(sip, recv, msg, "application/im-iscomposing+xml");
893 g_free(msg);
894 } else {
895 gchar *msg = g_strdup_printf(xml, "idle");
896 simple_send_message(sip, recv, msg, "application/im-iscomposing+xml");
897 g_free(msg);
898 }
899 g_free(recv);
900 return 1;
901 }
902
903 static gchar *find_tag(gchar *hdr) {
904 gchar *tmp = strstr(hdr, ";tag=");
905 gchar *tmp2;
906 if(!tmp) return NULL;
907 tmp += 5;
908 if((tmp2 = strchr(tmp, ';'))) {
909 tmp2[0] = '\0';
910 tmp = g_strdup(tmp);
911 tmp2[0] = ';';
912 return tmp;
913 }
914 return g_strdup(tmp);
915 }
916
917 static gchar* gen_pidf(struct simple_account_data *sip) {
918 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
919 "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\"\n"
920 "xmlns:im=\"urn:ietf:params:xml:ns:pidf:im\"\n"
921 "entity=\"sip:%s@%s\">\n"
922 "<tuple id=\"bs35r9f\">\n"
923 "<status>\n"
924 "<basic>open</basic>\n"
925 "<im:im>%s</im:im>\n"
926 "</status>\n"
927 "</tuple>\n"
928 "</presence>",
929 sip->username,
930 sip->servername,
931 sip->status);
932 return doc;
933 }
934
935 static void send_notify(struct simple_account_data *sip, struct simple_watcher *watcher) {
936 gchar *doc = gen_pidf(sip);
937 send_sip_request(sip->gc, "NOTIFY", watcher->name, watcher->name, "Event: presence\r\nContent-Type: application/pidf+xml\r\n", doc, &watcher->dialog, NULL);
938 g_free(doc);
939 }
940
941 static gboolean process_publish_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) {
942 if(msg->response != 200 && msg->response != 408) {
943 /* never send again */
944 sip->republish = -1;
945 }
946 return TRUE;
947 }
948
949 static void send_publish(struct simple_account_data *sip) {
950 gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername);
951 gchar *doc = gen_pidf(sip);
952 send_sip_request(sip->gc, "PUBLISH", uri, uri, "Expires: 600\r\nEvent: presence\r\nContent-Type: application/pidf+xml\r\n", doc, NULL, process_publish_response);
953 sip->republish = time(NULL) + 500;
954 g_free(doc);
955 }
956
957 static void process_incoming_subscribe(struct simple_account_data *sip, struct sipmsg *msg) {
958 gchar *from = parse_from(sipmsg_find_header(msg, "From"));
959 gchar *theirtag = find_tag(sipmsg_find_header(msg, "From"));
960 gchar *ourtag = find_tag(sipmsg_find_header(msg, "To"));
961 gboolean tagadded = FALSE;
962 gchar *callid = sipmsg_find_header(msg, "Call-ID");
963 gchar *expire = sipmsg_find_header(msg, "Expire");
964 gchar *tmp;
965 struct simple_watcher *watcher = watcher_find(sip, from);
966 if(!ourtag) {
967 tagadded = TRUE;
968 ourtag = gentag();
969 }
970 if(!watcher) { /* new subscription */
971 if(!gaim_privacy_check(sip->account, from)) {
972 send_sip_response(sip->gc, msg, 202, "Ok", NULL);
973 goto privend;
974 }
975 watcher = watcher_create(sip, from, callid, ourtag, theirtag);
976 }
977 if(tagadded) {
978 gchar *to = g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg, "To"), ourtag);
979 sipmsg_remove_header(msg, "To");
980 sipmsg_add_header(msg, "To", to);
981 }
982 if(expire)
983 watcher->expire = time(NULL) + strtol(expire, NULL, 10);
984 else
985 watcher->expire = time(NULL) + 600;
986 sipmsg_remove_header(msg, "Contact");
987 tmp = g_strdup_printf("<%s@%s>",sip->username, sip->servername);
988 sipmsg_add_header(msg, "Contact", tmp);
989 gaim_debug_info("simple","got subscribe: name %s ourtag %s theirtag %s callid %s\n", watcher->name, watcher->dialog.ourtag, watcher->dialog.theirtag, watcher->dialog.callid);
990 send_sip_response(sip->gc, msg, 200, "Ok", NULL);
991 g_free(tmp);
992 send_notify(sip, watcher);
993 privend:
994 g_free(from);
995 g_free(theirtag);
996 g_free(ourtag);
997 g_free(callid);
998 g_free(expire);
999 }
1000
1001 static void process_input_message(struct simple_account_data *sip, struct sipmsg *msg) {
1002 gboolean found = FALSE;
1003 if(msg->response == 0) { /* request */
1004 if(!strcmp(msg->method, "MESSAGE")) {
1005 process_incoming_message(sip, msg);
1006 found = TRUE;
1007 } else if(!strcmp(msg->method, "NOTIFY")) {
1008 process_incoming_notify(sip, msg);
1009 found = TRUE;
1010 } else if(!strcmp(msg->method, "SUBSCRIBE")) {
1011 process_incoming_subscribe(sip, msg);
1012 found = TRUE;
1013 } else {
1014 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL);
1015 }
1016 } else { /* response */
1017 struct transaction *trans = transactions_find(sip, msg);
1018 if(trans) {
1019 if(msg->response == 407) {
1020 gchar *resend, *auth, *ptmp;
1021
1022 if(sip->proxy.retries>3) return;
1023 sip->proxy.retries++;
1024 /* do proxy authentication */
1025
1026 ptmp = sipmsg_find_header(msg, "Proxy-Authenticate");
1027
1028 fill_auth(sip, ptmp, &sip->proxy);
1029 auth = auth_header(sip, &sip->proxy, trans->msg->method, trans->msg->target);
1030 sipmsg_remove_header(msg, "Proxy-Authorization");
1031 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth);
1032 g_free(auth);
1033 resend = sipmsg_to_string(trans->msg);
1034 /* resend request */
1035 sendout_pkt(sip->gc, resend);
1036 g_free(resend);
1037 } else {
1038 if(msg->response == 100) {
1039 /* ignore provisional response */
1040 gaim_debug_info("simple","got trying response\n");
1041 } else {
1042 sip->proxy.retries = 0;
1043 if(msg->response == 401) sip->registrar.retries++;
1044 else sip->registrar.retries = 0;
1045 if(trans->callback) {
1046 /* call the callback to process response*/
1047 (trans->callback)(sip, msg, trans);
1048 }
1049 transactions_remove(sip, trans);
1050 }
1051 }
1052 found = TRUE;
1053 } else {
1054 gaim_debug(GAIM_DEBUG_MISC, "simple", "received response to unknown transaction");
1055 }
1056 }
1057 if(!found) {
1058 gaim_debug(GAIM_DEBUG_MISC, "simple", "received a unknown sip message with method %s and response %d\n",msg->method, msg->response);
1059 }
1060 }
1061
1062 static void process_input(struct simple_account_data *sip, struct sip_connection *conn)
1063 {
1064 char *cur;
1065 char *dummy;
1066 struct sipmsg *msg;
1067 int restlen;
1068 cur = conn->inbuf;
1069
1070 /* according to the RFC remove CRLF at the beginning */
1071 while(*cur == '\r' || *cur == '\n') {
1072 cur++;
1073 }
1074 if(cur != conn->inbuf) {
1075 memmove(conn->inbuf, cur, conn->inbufused-(cur-conn->inbuf));
1076 conn->inbufused=strlen(conn->inbuf);
1077 }
1078
1079 /* Received a full Header? */
1080 if((cur = strstr(conn->inbuf, "\r\n\r\n"))!=NULL) {
1081 time_t currtime = time(NULL);
1082 cur += 2;
1083 cur[0] = '\0';
1084 gaim_debug_info("simple","\n\nreceived - %s\n######\n%s\n#######\n\n",ctime(&currtime), conn->inbuf);
1085 msg = sipmsg_parse_header(conn->inbuf);
1086 cur[0] = '\r';
1087 cur += 2;
1088 restlen = conn->inbufused - (cur-conn->inbuf);
1089 if(restlen>=msg->bodylen) {
1090 dummy = g_malloc(msg->bodylen+1);
1091 memcpy(dummy, cur, msg->bodylen);
1092 dummy[msg->bodylen]='\0';
1093 msg->body = dummy;
1094 cur+=msg->bodylen;
1095 memmove(conn->inbuf, cur, conn->inbuflen);
1096 conn->inbufused=strlen(conn->inbuf);
1097 } else {
1098 sipmsg_free(msg);
1099 return;
1100 }
1101 gaim_debug(GAIM_DEBUG_MISC, "simple", "in process response response: %d\n", msg->response);
1102 process_input_message(sip,msg);
1103 } else {
1104 gaim_debug(GAIM_DEBUG_MISC, "simple", "received a incomplete sip msg: %s\n", conn->inbuf);
1105 }
1106 }
1107
1108 static void simple_udp_process(gpointer data, gint source, GaimInputCondition con) {
1109 GaimConnection *gc = data;
1110 struct simple_account_data *sip = gc->proto_data;
1111 struct sipmsg *msg;
1112 int len;
1113 time_t currtime;
1114
1115 static char buffer[65536];
1116 if((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) {
1117 buffer[len] = '\0';
1118 gaim_debug_info("simple","\n\nreceived - %s\n######\n%s\n#######\n\n",ctime(&currtime), buffer);
1119 msg = sipmsg_parse_msg(buffer);
1120 if(msg) process_input_message(sip, msg);
1121 }
1122 }
1123
1124 static void simple_input_cb(gpointer data, gint source, GaimInputCondition cond)
1125 {
1126 GaimConnection *gc = data;
1127 struct simple_account_data *sip = gc->proto_data;
1128 int len;
1129 struct sip_connection *conn = connection_find(sip, source);
1130 if(!conn) {
1131 gaim_debug_error("simple", "Connection not found!\n");
1132 return;
1133 }
1134
1135 if (conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) {
1136 conn->inbuflen += SIMPLE_BUF_INC;
1137 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen);
1138 }
1139
1140 if ((len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1)) <= 0) {
1141 gaim_debug_info("simple","simple_input_cb: read error\n");
1142 connection_remove(sip, source);
1143 if(sip->fd == source) sip->fd = -1;
1144 return;
1145 }
1146 if(len == 0) {
1147 /* connection was closed */
1148 connection_remove(sip, source);
1149 if(sip->fd == source) sip->fd = -1;
1150 }
1151
1152 conn->inbufused += len;
1153 conn->inbuf[conn->inbufused]='\0';
1154
1155 process_input(sip, conn);
1156 }
1157
1158 /* Callback for new connections on incoming TCP port */
1159 static void simple_newconn_cb(gpointer data, gint source, GaimInputCondition cond) {
1160 GaimConnection *gc = data;
1161 struct simple_account_data *sip = gc->proto_data;
1162 struct sip_connection *conn;
1163
1164 int newfd = accept(source, NULL, NULL);
1165
1166 conn = connection_create(sip, newfd);
1167
1168 conn->inputhandler = gaim_input_add(newfd, GAIM_INPUT_READ, simple_input_cb, gc);
1169 }
1170
1171 static void login_cb(gpointer data, gint source, GaimInputCondition cond) {
1172 GaimConnection *gc = data;
1173 struct simple_account_data *sip = gc->proto_data;
1174 struct sip_connection *conn;
1175
1176 if( source < 0 ) {
1177 gaim_connection_error(gc,"Could not connect");
1178 return;
1179 }
1180
1181 sip->fd = source;
1182
1183 conn = connection_create(sip, source);
1184
1185 sip->registertimeout = gaim_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
1186
1187 do_register(sip);
1188
1189 conn->inputhandler = gaim_input_add(sip->fd, GAIM_INPUT_READ, simple_input_cb, gc);
1190 }
1191
1192 static guint simple_ht_hash_nick(const char *nick) {
1193 char *lc = g_utf8_strdown(nick, -1);
1194 guint bucket = g_str_hash(lc);
1195 g_free(lc);
1196
1197 return bucket;
1198 }
1199
1200 static gboolean simple_ht_equals_nick(const char *nick1, const char *nick2) {
1201 return (gaim_utf8_strcasecmp(nick1, nick2) == 0);
1202 }
1203
1204 static void simple_udp_host_resolved_listen_cb(int listenfd, gpointer data) {
1205 struct simple_account_data *sip = (struct simple_account_data*) data;
1206
1207 if(listenfd == -1) {
1208 gaim_connection_error(sip->gc, _("Could not create listen socket"));
1209 return;
1210 }
1211
1212 sip->fd = listenfd;
1213
1214 sip->listenport = gaim_network_get_port_from_fd(sip->fd);
1215 sip->listenfd = sip->fd;
1216
1217 sip->listenpa = gaim_input_add(sip->fd, GAIM_INPUT_READ, simple_udp_process, sip->gc);
1218
1219 sip->resendtimeout = gaim_timeout_add(2500, (GSourceFunc) resend_timeout, sip);
1220 sip->registertimeout = gaim_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip);
1221 do_register(sip);
1222 }
1223
1224 static void simple_udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) {
1225 struct simple_account_data *sip = (struct simple_account_data*) data;
1226 int addr_size;
1227
1228 if (!hosts || !hosts->data) {
1229 gaim_connection_error(sip->gc, _("Couldn't resolve host"));
1230 return;
1231 }
1232
1233 addr_size = GPOINTER_TO_INT(hosts->data);
1234 hosts = g_slist_remove(hosts, hosts->data);
1235 memcpy(&(sip->serveraddr), hosts->data, addr_size);
1236 g_free(hosts->data);
1237 hosts = g_slist_remove(hosts, hosts->data);
1238 while(hosts) {
1239 hosts = g_slist_remove(hosts, hosts->data);
1240 g_free(hosts->data);
1241 hosts = g_slist_remove(hosts, hosts->data);
1242 }
1243
1244 /* create socket for incoming connections */
1245 if(!gaim_network_listen_range(5060, 5160, SOCK_DGRAM,
1246 simple_udp_host_resolved_listen_cb, sip)) {
1247 gaim_connection_error(sip->gc, _("Could not create listen socket"));
1248 return;
1249 }
1250 }
1251
1252 static void
1253 simple_tcp_connect_listen_cb(int listenfd, gpointer data) {
1254 struct simple_account_data *sip = (struct simple_account_data*) data;
1255 int error = 0;
1256
1257 sip->listenfd = listenfd;
1258 if(sip->listenfd == -1) {
1259 gaim_connection_error(sip->gc, _("Could not create listen socket"));
1260 return;
1261 }
1262
1263 gaim_debug_info("simple", "listenfd: %d\n", sip->listenfd);
1264 sip->listenport = gaim_network_get_port_from_fd(sip->listenfd);
1265 sip->listenpa = gaim_input_add(sip->listenfd, GAIM_INPUT_READ,
1266 simple_newconn_cb, sip->gc);
1267 gaim_debug_info("simple","connecting to %s port %d\n",
1268 sip->realhostname, sip->realport);
1269 /* open tcp connection to the server */
1270 error = gaim_proxy_connect(sip->account, sip->realhostname,
1271 sip->realport, login_cb, sip->gc);
1272 if(error) {
1273 gaim_connection_error(sip->gc, _("Couldn't create socket"));
1274 }
1275 }
1276
1277 static void srvresolved(GaimSrvResponse *resp, int results, gpointer data) {
1278 struct simple_account_data *sip = (struct simple_account_data*) data;
1279
1280 gchar *hostname;
1281 int port = gaim_account_get_int(sip->account, "port", 0);
1282
1283
1284 /* find the host to connect to */
1285 if(results) {
1286 hostname = g_strdup(resp->hostname);
1287 if(!port)
1288 port = resp->port;
1289 g_free(resp);
1290 } else {
1291 if(!gaim_account_get_bool(sip->account, "useproxy", FALSE)) {
1292 hostname = g_strdup(sip->servername);
1293 } else {
1294 hostname = g_strdup(gaim_account_get_string(sip->account, "proxy", sip->servername));
1295 }
1296 }
1297
1298 sip->realhostname = hostname;
1299 sip->realport = port;
1300 if(!sip->realport) sip->realport = 5060;
1301 /* TCP case */
1302 if(! sip->udp) {
1303 /* create socket for incoming connections */
1304 if(!gaim_network_listen_range(5060, 5160, SOCK_STREAM,
1305 simple_tcp_connect_listen_cb, sip)) {
1306 gaim_connection_error(sip->gc, _("Could not create listen socket"));
1307 return;
1308 }
1309 } else { /* UDP */
1310 gaim_debug_info("simple", "using udp with server %s and port %d\n", hostname, port);
1311
1312 gaim_gethostbyname_async(hostname, port, simple_udp_host_resolved, sip);
1313 }
1314 }
1315
1316 static void simple_login(GaimAccount *account)
1317 {
1318 GaimConnection *gc;
1319 struct simple_account_data *sip;
1320 gchar **userserver;
1321 gchar *hosttoconnect;
1322
1323 const char *username = gaim_account_get_username(account);
1324
1325 gc = gaim_account_get_connection(account);
1326 gc->proto_data = sip = g_new0(struct simple_account_data,1);
1327 sip->gc=gc;
1328 sip->account = account;
1329 sip->registerexpire = 900;
1330 sip->udp = gaim_account_get_bool(account, "udp", FALSE);
1331 if (strpbrk(username, " \t\v\r\n") != NULL) {
1332 gaim_connection_error(gc, _("SIP usernames may not contain whitespaces or @ symbols"));
1333 return;
1334 }
1335
1336 userserver = g_strsplit(username, "@", 2);
1337 gaim_connection_set_display_name(gc,userserver[0]);
1338 sip->username = g_strdup(userserver[0]);
1339 sip->servername = g_strdup(userserver[1]);
1340 sip->password = g_strdup(gaim_connection_get_password(gc));
1341 g_strfreev(userserver);
1342
1343 sip->buddies = g_hash_table_new((GHashFunc)simple_ht_hash_nick, (GEqualFunc)simple_ht_equals_nick);
1344
1345 gaim_connection_update_progress(gc, _("Connecting"), 1, 2);
1346
1347 /* TODO: Set the status correctly. */
1348 sip->status = g_strdup("available");
1349
1350 if(!gaim_account_get_bool(account, "useproxy", FALSE)) {
1351 hosttoconnect = g_strdup(sip->servername);
1352 } else {
1353 hosttoconnect = g_strdup(gaim_account_get_string(account, "proxy", sip->servername));
1354 }
1355
1356 /* TCP case */
1357 if(! sip->udp) {
1358 gaim_srv_resolve("sip","tcp",hosttoconnect,srvresolved, sip);
1359 } else { /* UDP */
1360 gaim_srv_resolve("sip","udp",hosttoconnect,srvresolved, sip);
1361 }
1362 g_free(hosttoconnect);
1363 }
1364
1365 static void simple_close(GaimConnection *gc)
1366 {
1367 struct simple_account_data *sip = gc->proto_data;
1368
1369 /* unregister */
1370 do_register_exp(sip, 0);
1371 connection_free_all(sip);
1372 if(sip) {
1373 g_free(sip->servername);
1374 g_free(sip->username);
1375 g_free(sip->password);
1376 g_free(sip->registrar.nonce);
1377 g_free(sip->registrar.realm);
1378 g_free(sip->proxy.nonce);
1379 g_free(sip->proxy.realm);
1380 g_free(sip->sendlater);
1381 g_free(sip->realhostname);
1382 if(sip->listenpa) gaim_input_remove(sip->listenpa);
1383 if(sip->resendtimeout) gaim_timeout_remove(sip->resendtimeout);
1384 if(sip->registertimeout) gaim_timeout_remove(sip->registertimeout);
1385 sip->servername = sip->username = sip->password = sip->registrar.nonce = sip->registrar.realm = sip->proxy.nonce = sip->proxy.realm = sip->sendlater = sip->realhostname = NULL;
1386 }
1387 g_free(gc->proto_data);
1388 gc->proto_data = NULL;
1389 }
1390
1391 /* not needed since privacy is checked for every subscribe */
1392 static void dummy_add_deny(GaimConnection *gc, const char *name) {
1393 }
1394
1395 static void dummy_permit_deny(GaimConnection *gc) {
1396 }
1397
1398 static GaimPluginProtocolInfo prpl_info =
1399 {
1400 0,
1401 NULL, /* user_splits */
1402 NULL, /* protocol_options */
1403 NO_BUDDY_ICONS, /* icon_spec */
1404 simple_list_icon, /* list_icon */
1405 NULL, /* list_emblems */
1406 NULL, /* status_text */
1407 NULL, /* tooltip_text */
1408 simple_status_types, /* away_states */
1409 NULL, /* blist_node_menu */
1410 NULL, /* chat_info */
1411 NULL, /* chat_info_defaults */
1412 simple_login, /* login */
1413 simple_close, /* close */
1414 simple_im_send, /* send_im */
1415 NULL, /* set_info */
1416 simple_typing, /* send_typing */
1417 NULL, /* get_info */
1418 simple_set_status, /* set_status */
1419 NULL, /* set_idle */
1420 NULL, /* change_passwd */
1421 simple_add_buddy, /* add_buddy */
1422 NULL, /* add_buddies */
1423 simple_remove_buddy, /* remove_buddy */
1424 NULL, /* remove_buddies */
1425 dummy_add_deny, /* add_permit */
1426 dummy_add_deny, /* add_deny */
1427 dummy_add_deny, /* rem_permit */
1428 dummy_add_deny, /* rem_deny */
1429 dummy_permit_deny, /* set_permit_deny */
1430 NULL, /* join_chat */
1431 NULL, /* reject_chat */
1432 NULL, /* get_chat_name */
1433 NULL, /* chat_invite */
1434 NULL, /* chat_leave */
1435 NULL, /* chat_whisper */
1436 NULL, /* chat_send */
1437 simple_keep_alive, /* keepalive */
1438 NULL, /* register_user */
1439 NULL, /* get_cb_info */
1440 NULL, /* get_cb_away */
1441 NULL, /* alias_buddy */
1442 NULL, /* group_buddy */
1443 NULL, /* rename_group */
1444 NULL, /* buddy_free */
1445 NULL, /* convo_closed */
1446 NULL, /* normalize */
1447 NULL, /* set_buddy_icon */
1448 NULL, /* remove_group */
1449 NULL, /* get_cb_real_name */
1450 NULL, /* set_chat_topic */
1451 NULL, /* find_blist_chat */
1452 NULL, /* roomlist_get_list */
1453 NULL, /* roomlist_cancel */
1454 NULL, /* roomlist_expand_category */
1455 NULL, /* can_receive_file */
1456 NULL, /* send_file */
1457 NULL, /* new_xfer */
1458 NULL, /* offline_message */
1459 NULL, /* whiteboard_prpl_ops */
1460 NULL, /* media_prpl_ops */
1461 };
1462
1463
1464 static GaimPluginInfo info =
1465 {
1466 GAIM_PLUGIN_MAGIC,
1467 GAIM_MAJOR_VERSION,
1468 GAIM_MINOR_VERSION,
1469 GAIM_PLUGIN_PROTOCOL, /**< type */
1470 NULL, /**< ui_requirement */
1471 0, /**< flags */
1472 NULL, /**< dependencies */
1473 GAIM_PRIORITY_DEFAULT, /**< priority */
1474
1475 "prpl-simple", /**< id */
1476 "SIMPLE", /**< name */
1477 VERSION, /**< version */
1478 N_("SIP/SIMPLE Protocol Plugin"), /** summary */
1479 N_("The SIP/SIMPLE Protocol Plugin"), /** description */
1480 "Thomas Butter <butter@uni-mannheim.de>", /**< author */
1481 GAIM_WEBSITE, /**< homepage */
1482
1483 NULL, /**< load */
1484 NULL, /**< unload */
1485 NULL, /**< destroy */
1486
1487 NULL, /**< ui_info */
1488 &prpl_info, /**< extra_info */
1489 NULL,
1490 NULL
1491 };
1492
1493 static void _init_plugin(GaimPlugin *plugin)
1494 {
1495 GaimAccountUserSplit *split;
1496 GaimAccountOption *option;
1497
1498 split = gaim_account_user_split_new(_("Server"), "", '@');
1499 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
1500
1501 option = gaim_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "dopublish", TRUE);
1502 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1503
1504 option = gaim_account_option_int_new(_("Connect port"), "port", 0);
1505 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1506
1507
1508 option = gaim_account_option_bool_new(_("Use UDP"), "udp", FALSE);
1509 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1510 option = gaim_account_option_bool_new(_("Use proxy"), "useproxy", FALSE);
1511 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1512 option = gaim_account_option_string_new(_("Proxy"), "proxy", "");
1513 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1514 }
1515
1516 GAIM_INIT_PLUGIN(simple, _init_plugin, info);

mercurial