Updated.
[doldaconnect.git] / clients / gtk2 / main.c
CommitLineData
d3372da9 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19
a745644c 20/* This file is a complete and total mess, mostly because of my
21 * inability to structure GUI programs properly. Looking at it too
22 * closely may cause ocular hemorrhaging. */
23
b5010caa 24#include <stdlib.h>
d3372da9 25#include <stdio.h>
26#include <unistd.h>
27#include <string.h>
d3372da9 28#include <stdarg.h>
29#include <gtk/gtk.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32#include <gdk/gdkkeysyms.h>
33#include <doldaconnect/uilib.h>
34#include <doldaconnect/uimisc.h>
35#include <doldaconnect/utils.h>
36#include <errno.h>
37#include <regex.h>
38#include <signal.h>
39#include <time.h>
8d9ec191 40#include <sys/time.h>
d3372da9 41#include <pwd.h>
42#include <locale.h>
43#include <libintl.h>
44#include <assert.h>
45
a745644c 46/* "Programming with libxml2 is like the thrilling embrace of an
47 * exotic strangler."
48 * --Me */
49#include <libxml/parser.h>
50#include <libxml/tree.h>
51
d3372da9 52#ifdef HAVE_CONFIG_H
53#include <config.h>
54#endif
55#include "progressbar.h"
56
8d9ec191 57#define TRHISTSIZE 10
a745644c 58#define PHO_INIT 0
59#define PHO_DATA 1
60#define PHO_EOF 2
61#define PHO_FINI 3
8d9ec191 62
63struct trdata
64{
65 size_t poshist[TRHISTSIZE];
66 double timehist[TRHISTSIZE];
67 int hc;
68};
d3372da9 69
70struct fndata
71{
72 GtkTextBuffer *textbuf;
73};
74
75struct srchsize
76{
77 int size;
78 int num;
79 int slots;
80 double resptime;
81 GtkTreeRowReference *ref;
82};
83
84struct knownspeed
85{
86 char *userid;
87 int speed, seq;
88 time_t fetched;
89};
90
91GtkWidget *inpdialog;
a745644c 92GtkListStore *fnmodel, *ulmodel, *dlmodel, *reslist;
93int (*pubhubhandler)(int, char *, size_t *) = NULL;
d3372da9 94GtkTreeStore *srchmodel;
95GtkTreeModelFilter *srchmodelfilter;
96GtkTextTagTable *chattags;
97int dcfd = -1, gdkread = -1, gdkwrite = -1;
98int pubhubfd = -1, pubhubtag = -1, filterpubhub = 0;
99int curchat = -1;
100regex_t pubhubfilter;
101pid_t pubhubproc = 0;
102char *pubhubaddr = NULL;
103char *connectas = NULL;
104char *dcserver = NULL;
ef0f20e9 105int autoconn = 1;
d3372da9 106int srchautoupdate = 0;
107int cursrch = -1, nextsrch = -1;
108time_t srcheta;
109struct srchsize *srchsizes = NULL;
110struct knownspeed *knownspeeds = NULL;
4c27a5f2 111int numsizes = 0, numspeeds = 0, ksqueryseq = -1, ksquerytag = -1, lsrestag = -1;
d3372da9 112
113gboolean initdeath(GtkWidget *, gpointer);
114void cb_main_connmenu_activate(GtkWidget *widget, gpointer data);
115void cb_main_dconnmenu_activate(GtkWidget *widget, gpointer data);
116void cb_main_prefmenu_activate(GtkWidget *widget, gpointer data);
4c27a5f2 117void cb_main_lsres_activate(GtkWidget *widget, gpointer data);
d3372da9 118void cb_main_sdmenu_activate(GtkWidget *widget, gpointer data);
119void cb_inpdialog_entry_activate(GtkWidget *widget, gpointer data);
120void cb_main_fnaddr_activate(GtkWidget *widget, gpointer data);
121void cb_main_pubhubfilter_activate(GtkWidget *widget, gpointer data);
122void cb_main_dcnctbtn_clicked(GtkWidget *widget, gpointer data);
123void cb_main_phublist_cchange(GtkWidget *widget, gpointer data);
124void cb_main_phublist_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data);
125void cb_main_chatnodes_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data);
126void cb_main_srchres_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data);
127void cb_main_chatstr_activate(GtkWidget *widget, gpointer data);
128void cb_main_simplesrch_changed(GtkWidget *widget, gpointer data);
129void cb_main_realsrch_changed(GtkWidget *widget, gpointer data);
130void cb_main_srchbtn_clicked(GtkWidget *widget, gpointer data);
131void cb_main_srchcanbtn_clicked(GtkWidget *widget, gpointer data);
ab9f4164 132gboolean cb_main_trlist_keypress(GtkWidget *widget, GdkEventKey *event, gpointer data);
d3372da9 133void cb_main_filternoslots_toggled(GtkToggleButton *widget, gpointer data);
ab9f4164 134void cb_main_srhash_activate(GtkWidget *widget, gpointer data);
415daf6c 135void cb_main_srcopy_activate(GtkWidget *widget, gpointer data);
ab9f4164 136void cb_main_trhash_activate(GtkWidget *widget, gpointer data);
415daf6c 137void cb_main_trcopy_activate(GtkWidget *widget, gpointer data);
419385f3 138void cb_main_trcancel_activate(GtkWidget *widget, gpointer data);
ab9f4164 139gboolean cb_main_srpopup(GtkWidget *widget, GdkEventButton *event, gpointer data);
140gboolean cb_main_trpopup(GtkWidget *widget, GdkEventButton *event, gpointer data);
4c27a5f2 141void cb_reslist_reload_clicked(GtkWidget *widget, gpointer data);
142void cb_reslist_delete_clicked(GtkWidget *widget, gpointer data);
143void cb_reslist_search_clicked(GtkWidget *widget, gpointer data);
144void cb_reslist_list_cchange(GtkWidget *widget, gpointer data);
145void cb_reslist_list_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data);
146gboolean cb_reslist_list_keypress(GtkWidget *widget, GdkEventKey *event, gpointer data);
d3372da9 147void dcfdcallback(gpointer data, gint source, GdkInputCondition condition);
148void srchstatupdate(void);
149void transnicebytefunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
cac656ef 150void transnicebytefunc2(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
8d9ec191 151void transspeedinfo(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
d3372da9 152void transerrorinfo(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
153void percentagefunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
154void hidezerofunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
155void speedtimefunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
156
157#define DCCHARSET "windows-1252"
158
159#define _(text) gettext(text)
160
161#include "mainwnd.gtk"
162#include "inpdialog.gtk"
163#include "pref.gtk"
4c27a5f2 164#include "reslist.gtk"
d3372da9 165
166void updatewrite(void)
167{
168 if(dcfd < 0)
169 return;
170 if(dc_wantwrite())
171 {
172 if(gdkwrite == -1)
173 gdkwrite = gdk_input_add(dcfd, GDK_INPUT_WRITE, dcfdcallback, NULL);
174 } else {
175 if(gdkwrite != -1)
176 {
177 gdk_input_remove(gdkwrite);
178 gdkwrite = -1;
179 }
180 }
181}
182
8d9ec191 183double ntime(void)
184{
185 struct timeval tv;
186
187 gettimeofday(&tv, NULL);
188 return((double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0));
189}
190
d3372da9 191void fndestroycb(struct dc_fnetnode *fn)
192{
193 struct fndata *data;
194 GtkTextBuffer *textbuf;
195
196 data = fn->udata;
197 g_object_unref(data->textbuf);
198 free(data);
199 if(curchat == fn->id)
200 {
201 textbuf = gtk_text_buffer_new(chattags);
202 gtk_text_view_set_buffer(GTK_TEXT_VIEW(main_chatview), textbuf);
203 g_object_unref(textbuf);
204 }
205}
206
207void addfndata(struct dc_fnetnode *fn)
208{
209 struct fndata *data;
210
211 if(fn->udata != NULL)
212 return;
213 fn->destroycb = fndestroycb;
214 data = smalloc(sizeof(*data));
215 data->textbuf = gtk_text_buffer_new(chattags);
216 fn->udata = data;
217}
218
8d9ec191 219void trdestroycb(struct dc_transfer *tr)
220{
221 free(tr->udata);
222}
223
224void addtrdata(struct dc_transfer *tr)
225{
226 struct trdata *data;
227
228 if(tr->udata != NULL)
229 return;
230 tr->destroycb = trdestroycb;
231 data = smalloc(sizeof(*data));
232 memset(data, 0, sizeof(*data));
233 tr->udata = data;
234}
235
236void updatetrdata(struct dc_transfer *tr)
237{
238 int i;
239 struct trdata *data;
240
241 data = tr->udata;
242 if(data->hc < TRHISTSIZE)
243 {
244 data->poshist[data->hc] = tr->curpos;
245 data->timehist[data->hc] = ntime();
246 data->hc++;
247 } else {
248 for(i = 0; i < TRHISTSIZE - 1; i++)
249 {
250 data->poshist[i] = data->poshist[i + 1];
251 data->timehist[i] = data->timehist[i + 1];
252 }
253 data->poshist[i] = tr->curpos;
254 data->timehist[i] = ntime();
255 }
256}
257
d3372da9 258char *getfnstatestock(int state)
259{
260 if(state == DC_FNN_STATE_SYN)
261 return("gtk-jump-to");
262 if(state == DC_FNN_STATE_HS)
263 return("gtk-execute");
264 if(state == DC_FNN_STATE_EST)
265 return("gtk-yes");
266 if(state == DC_FNN_STATE_DEAD)
267 return("gtk-cancel");
268 return(NULL);
269}
270
271void updatehublist(void)
272{
273 int done;
274 struct dc_fnetnode *fn;
275 GtkTreeIter iter;
276 int id;
277 char *buf;
278 char *name;
279 int state, numusers;
280
281 for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
282 fn->found = 0;
283 done = 0;
284 while(!done)
285 {
286 done = 1;
287 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fnmodel), &iter))
288 {
289 do
290 {
291 gtk_tree_model_get(GTK_TREE_MODEL(fnmodel), &iter, 0, &id, -1);
292 if((fn = dc_findfnetnode(id)) == NULL)
293 {
294 /* I can't seem to get a sensible reply fromp
295 * gtk_list_store, so I'm just doing this
296 * instead. */
297 gtk_list_store_remove(fnmodel, &iter);
298 done = 0;
299 break;
300 } else {
301 gtk_tree_model_get(GTK_TREE_MODEL(fnmodel), &iter, 1, &name, 2, &state, 3, &numusers, -1);
302 if(fn->name == NULL)
303 buf = _("Unknown");
304 else
305 buf = icswcstombs(fn->name, "UTF-8", NULL);
306 if(strcmp(buf, name))
307 gtk_list_store_set(fnmodel, &iter, 1, buf, -1);
308 if(state != fn->state)
309 {
310 gtk_list_store_set(fnmodel, &iter, 2, fn->state, -1);
311 gtk_list_store_set(fnmodel, &iter, 4, getfnstatestock(fn->state), -1);
312 }
313 if(numusers != fn->numusers)
314 gtk_list_store_set(fnmodel, &iter, 3, fn->numusers, -1);
315 g_free(name);
316 fn->found = 1;
317 }
318 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(fnmodel), &iter));
319 }
320 }
321 for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
322 {
323 if(!fn->found)
324 {
325 if(fn->name == NULL)
326 buf = _("Unknown");
327 else
328 buf = icswcstombs(fn->name, "UTF-8", NULL);
329 gtk_list_store_append(fnmodel, &iter);
330 gtk_list_store_set(fnmodel, &iter, 0, fn->id, 1, buf, 2, fn->state, 3, fn->numusers, 4, getfnstatestock(fn->state), -1);
331 addfndata(fn);
332 }
333 }
334}
335
cac656ef 336char *bytes2si(long long bytes)
8d9ec191 337{
338 int i;
339 double b;
340 char *sd;
341 static char ret[64];
342
343 b = bytes;
cac656ef 344 for(i = 0; (b >= 1024) && (i < 4); i++)
8d9ec191 345 b /= 1024;
346 if(i == 0)
347 sd = "B";
348 else if(i == 1)
349 sd = "kiB";
350 else if(i == 2)
351 sd = "MiB";
352 else if(i == 3)
353 sd = "GiB";
354 else
355 sd = "TiB";
356 snprintf(ret, 64, "%.1f %s", b, sd);
357 return(ret);
358}
359
d3372da9 360void percentagefunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
361{
362 int colnum;
363 float val;
364 char buf[64];
365
a2f77326 366 colnum = GPOINTER_TO_INT(data);
d3372da9 367 gtk_tree_model_get(model, iter, colnum, &val, -1);
368 snprintf(buf, 64, "%.2f%%", (double)(val * 100.0));
369 g_object_set(rend, "text", buf, NULL);
370}
371
372void transnicebytefunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
373{
374 int colnum, val;
375 char buf[64];
376
a2f77326 377 colnum = GPOINTER_TO_INT(data);
d3372da9 378 gtk_tree_model_get(model, iter, colnum, &val, -1);
379 if(val >= 0)
380 snprintf(buf, 64, "%'i", val);
381 else
382 strcpy(buf, _("Unknown"));
383 g_object_set(rend, "text", buf, NULL);
384}
385
cac656ef 386void transnicebytefunc2(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
387{
388 int colnum;
389 long long val;
390 char buf[64];
391
a2f77326 392 colnum = GPOINTER_TO_INT(data);
cac656ef 393 gtk_tree_model_get(model, iter, colnum, &val, -1);
394 if(val >= 0)
395 strcpy(buf, bytes2si(val));
396 else
397 strcpy(buf, _("Unknown"));
398 g_object_set(rend, "text", buf, NULL);
399}
400
d3372da9 401void hidezerofunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
402{
403 int colnum, val;
404 char buf[64];
405
a2f77326 406 colnum = GPOINTER_TO_INT(data);
d3372da9 407 gtk_tree_model_get(model, iter, colnum, &val, -1);
408 if(val > 0)
409 snprintf(buf, 64, "%i", val);
410 else
411 strcpy(buf, "");
412 g_object_set(rend, "text", buf, NULL);
413}
414
415void speedtimefunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
416{
417 int speed, size, time;
418 char buf[64];
419
420 gtk_tree_model_get(model, iter, 4, &size, 8, &speed, -1);
421 if(speed > 0)
422 {
423 time = (size / speed) / 60;
424 if(time < 1)
425 snprintf(buf, 64, "%'i (<00:01)", speed);
426 else
427 snprintf(buf, 64, "%'i (%02i:%02i)", speed, time / 60, time % 60);
428 } else if(speed == 0) {
429 strcpy(buf, "0");
430 } else {
431 strcpy(buf, _("Unknown"));
432 }
433 g_object_set(rend, "text", buf, NULL);
434}
435
8d9ec191 436void transspeedinfo(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
437{
438 int id;
439 struct dc_transfer *tr;
440 struct trdata *d;
441 char buf[64];
442 int speed;
443
444 gtk_tree_model_get(model, iter, 0, &id, -1);
445 if((tr = dc_findtransfer(id)) != NULL)
446 {
447 d = tr->udata;
448 if((tr->state != DC_TRNS_MAIN) || (d == NULL))
449 {
450 buf[0] = 0;
451 } else if(d->hc < 2) {
452 strcpy(buf, "...");
453 } else {
454 speed = (((double)(d->poshist[d->hc - 1] - d->poshist[0])) / (d->timehist[d->hc - 1] - d->timehist[0]));
455 snprintf(buf, 64, "%s/s", bytes2si(speed));
456 }
457 } else {
458 buf[0] = 0;
459 }
460 g_object_set(rend, "text", buf, NULL);
461}
462
d3372da9 463void transerrorinfo(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
464{
465 int error;
466 time_t errortime;
467 char finbuf[64], tbuf[64], *errstr;
468
469 gtk_tree_model_get(model, iter, 10, &error, 11, &errortime, -1);
470 if(error != DC_TRNSE_NOERROR)
471 {
472 if(error == DC_TRNSE_NOTFOUND)
473 errstr = _("Not found");
474 else if(error == DC_TRNSE_NOSLOTS)
475 errstr = _("No slots");
476 strftime(tbuf, 64, _("%H:%M:%S"), localtime(&errortime));
477 snprintf(finbuf, 64, _("%s (reported at %s)"), errstr, tbuf);
478 } else {
479 *finbuf = 0;
480 }
481 g_object_set(rend, "text", finbuf, NULL);
482}
483
484char *gettrstatestock(int state)
485{
486 if(state == DC_TRNS_WAITING)
487 return("gtk-jump-to");
488 if(state == DC_TRNS_HS)
489 return("gtk-execute");
490 if(state == DC_TRNS_MAIN)
491 return("gtk-network");
492 if(state == DC_TRNS_DONE)
493 return("gtk-yes");
494 return(NULL);
495}
496
8d9ec191 497gint updatetransfers(gpointer data)
498{
499 struct dc_transfer *tr;
500 struct trdata *d;
501 double now;
502
503 now = ntime();
504 for(tr = dc_transfers; tr != NULL; tr = tr->next)
505 {
506 if((d = tr->udata) != NULL)
507 {
508 if((d->hc > 0) && ((now - d->timehist[d->hc - 1]) > 2))
509 updatetrdata(tr);
510 }
511 }
512 return(TRUE);
513}
514
d3372da9 515void updatetransferlists(void)
516{
517 int i;
518 int done;
519 struct dc_transfer *transfer;
520 GtkTreeIter iter;
521 int id;
522 char *buf;
ab9f4164 523 char *peerid, *peernick, *path, *hash;
d3372da9 524 int state, dir, size, curpos, error;
525 time_t errortime;
526 GtkListStore *stores[3];
527
528 for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
529 transfer->found = 0;
530 stores[DC_TRNSD_UNKNOWN] = NULL;
531 stores[DC_TRNSD_UP] = ulmodel;
532 stores[DC_TRNSD_DOWN] = dlmodel;
533 for(i = 0; i < 3; i++)
534 {
535 if(stores[i] == NULL)
536 continue;
537 done = 0;
538 while(!done)
539 {
540 done = 1;
541 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(stores[i]), &iter))
542 {
543 do
544 {
545 gtk_tree_model_get(GTK_TREE_MODEL(stores[i]), &iter, 0, &id, 1, &dir, -1);
546 if(((transfer = dc_findtransfer(id)) == NULL) || (transfer->dir != dir))
547 {
548 gtk_list_store_remove(stores[i], &iter);
549 done = 0;
550 break;
551 } else {
552 transfer->found = 1;
ab9f4164 553 gtk_tree_model_get(GTK_TREE_MODEL(stores[i]), &iter, 2, &state, 3, &peerid, 4, &peernick, 5, &path, 6, &size, 7, &curpos, 10, &error, 11, &errortime, 12, &hash, -1);
d3372da9 554 if(state != transfer->state)
555 gtk_list_store_set(stores[i], &iter, 2, transfer->state, 8, gettrstatestock(transfer->state), -1);
556 if(size != transfer->size)
557 gtk_list_store_set(stores[i], &iter, 6, transfer->size, -1);
558 if(curpos != transfer->curpos)
8d9ec191 559 {
d3372da9 560 gtk_list_store_set(stores[i], &iter, 7, transfer->curpos, -1);
8d9ec191 561 if(transfer->udata != NULL)
562 updatetrdata(transfer);
563 }
d3372da9 564 if(error != transfer->error)
565 gtk_list_store_set(stores[i], &iter, 10, transfer->error, -1);
566 if(errortime != transfer->errortime)
567 gtk_list_store_set(stores[i], &iter, 11, transfer->errortime, -1);
568 if((transfer->size > 0) && (transfer->curpos > 0))
569 gtk_list_store_set(stores[i], &iter, 9, (float)transfer->curpos / (float)transfer->size, -1);
570 buf = icswcstombs(transfer->peerid, "UTF-8", NULL);
571 if(strcmp(buf, peerid))
572 gtk_list_store_set(stores[i], &iter, 3, buf, -1);
573 buf = icswcstombs(((transfer->peernick == NULL) || (transfer->peernick[0] == L'\0'))?transfer->peerid:transfer->peernick, "UTF-8", NULL);
574 if(strcmp(buf, peernick))
575 gtk_list_store_set(stores[i], &iter, 4, buf, -1);
576 buf = (transfer->path == NULL)?_("Unknown"):icswcstombs(transfer->path, "UTF-8", NULL);
577 if(strcmp(buf, path))
578 gtk_list_store_set(stores[i], &iter, 5, buf, -1);
ab9f4164 579 buf = (transfer->hash == NULL)?"":icswcstombs(transfer->hash, "UTF-8", NULL);
580 if(strcmp(buf, path))
581 gtk_list_store_set(stores[i], &iter, 12, buf, -1);
582 g_free(hash);
d3372da9 583 g_free(peerid);
584 g_free(peernick);
585 g_free(path);
586 }
587 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(stores[i]), &iter));
588 }
589 }
590 }
591 for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
592 {
593 if(!transfer->found)
594 {
595 if(stores[transfer->dir] != NULL)
596 {
597 peerid = icwcstombs(transfer->peerid, "UTF-8");
598 peernick = icwcstombs(((transfer->peernick == NULL) || (transfer->peernick[0] == L'\0'))?transfer->peerid:transfer->peernick, "UTF-8");
599 path = (transfer->path == NULL)?_("Unknown"):icwcstombs(transfer->path, "UTF-8");
ab9f4164 600 hash = (transfer->hash == NULL)?"":icwcstombs(transfer->hash, "UTF-8");
d3372da9 601 gtk_list_store_append(stores[transfer->dir], &iter);
602 gtk_list_store_set(stores[transfer->dir], &iter,
603 0, transfer->id,
604 1, transfer->dir,
605 2, transfer->state,
606 3, peerid,
607 4, peernick,
608 5, path,
609 6, transfer->size,
610 7, transfer->curpos,
611 8, gettrstatestock(transfer->state),
612 9, 0.0,
613 10, transfer->error,
614 11, transfer->errortime,
ab9f4164 615 12, hash,
d3372da9 616 -1);
617 free(peerid);
618 free(peernick);
619 if(transfer->path != NULL)
620 free(path);
ab9f4164 621 if(transfer->hash != NULL)
622 free(hash);
8d9ec191 623 addtrdata(transfer);
d3372da9 624 }
625 }
626 }
627}
628
629void updatesbar(char *msg)
630{
631 gtk_statusbar_pop(GTK_STATUSBAR(main_statusbar), 0);
632 gtk_statusbar_push(GTK_STATUSBAR(main_statusbar), 0, msg);
633}
634
635void freesrchsizes(void)
636{
637 int i;
638
639 for(i = 0; i < numsizes; i++)
640 {
641 if(srchsizes[i].ref != NULL)
642 gtk_tree_row_reference_free(srchsizes[i].ref);
643 }
644 if(srchsizes != NULL)
645 free(srchsizes);
646 srchsizes = NULL;
647 numsizes = 0;
648}
649
650void dcdisconnected(void)
651{
652 if(gdkread != -1)
653 {
654 gdk_input_remove(gdkread);
655 gdkread = -1;
656 }
657 dcfd = -1;
658 updatehublist();
659 updatetransferlists();
660 cursrch = nextsrch = -1;
661 gtk_tree_store_clear(srchmodel);
662 freesrchsizes();
663 gtk_widget_set_sensitive(main_connmenu, TRUE);
664 gtk_widget_set_sensitive(main_dconnmenu, FALSE);
665 gtk_widget_set_sensitive(main_simplesrch, TRUE);
666 gtk_widget_set_sensitive(main_realsrch, TRUE);
667 gtk_widget_set_sensitive(main_srchbtn, TRUE);
668 gtk_widget_set_sensitive(main_srchcanbtn, FALSE);
669 updatesbar(_("Disconnected"));
670}
671
672char *inputbox(char *title, char *prompt, char *def, int echo)
673{
674 int resp;
675 GtkWidget *swnd;
676 char *buf;
677
678 inpdialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_wnd), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
679 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(inpdialog)->vbox), swnd = create_inpdialog_wnd(), TRUE, TRUE, 0);
680 gtk_widget_show(swnd);
681 if(!echo)
682 gtk_entry_set_visibility(GTK_ENTRY(inpdialog_entry), FALSE);
683 gtk_label_set_text(GTK_LABEL(inpdialog_prompt), prompt);
684 gtk_entry_set_text(GTK_ENTRY(inpdialog_entry), def);
685 resp = gtk_dialog_run(GTK_DIALOG(inpdialog));
686 if(!echo)
687 gtk_entry_set_visibility(GTK_ENTRY(inpdialog_entry), TRUE);
688 if(resp == GTK_RESPONSE_ACCEPT)
689 buf = strdup(gtk_entry_get_text(GTK_ENTRY(inpdialog_entry)));
690 else
691 buf = NULL;
692 gtk_widget_destroy(inpdialog);
693 updatewrite();
694 return(buf);
695}
696
697int msgbox(int type, int buttons, char *format, ...)
a2f77326 698#if defined(__GNUC__)
699 __attribute__ ((format (printf, 3, 4)))
700#endif
701;
702
703int msgbox(int type, int buttons, char *format, ...)
d3372da9 704{
705 GtkWidget *swnd;
706 va_list args;
707 char *buf;
708 int resp;
709
710 va_start(args, format);
711 buf = vsprintf2(format, args);
712 va_end(args);
713 swnd = gtk_message_dialog_new(GTK_WINDOW(main_wnd), GTK_DIALOG_MODAL, type, buttons, "%s", buf);
714 resp = gtk_dialog_run(GTK_DIALOG(swnd));
715 gtk_widget_destroy(swnd);
716 free(buf);
717 return(resp);
718}
719
720void readconfigfile(void)
721{
722 FILE *cfgfile;
723 char *homedir, *buf, *p;
724 int w, h;
725
726 if((homedir = getenv("HOME")) == NULL)
727 {
728 fprintf(stderr, "warning: could not find home directory!\n");
729 return;
730 }
731 buf = sprintf2("%s/.dolconrc", homedir);
732 if((cfgfile = fopen(buf, "r")) == NULL)
733 {
734 if(errno != ENOENT)
735 perror(buf);
736 free(buf);
737 return;
738 }
739 free(buf);
740 buf = smalloc(1024);
741 while(fgets(buf, 1024, cfgfile) != NULL)
742 {
743 if(strlen(buf) < 1)
744 continue;
745 p = buf + strlen(buf);
746 if(p[-1] == '\n')
747 *(--p) = 0;
748 if((p = strchr(buf, ':')) == NULL)
749 continue;
750 *(p++) = 0;
751 while((*p == ' ') || (*p == '\t'))
752 p++;
753 if(!strcmp(buf, "wnd-width"))
754 {
755 w = atoi(p);
756 } else if(!strcmp(buf, "wnd-height")) {
757 h = atoi(p);
758 } else if(!strcmp(buf, "pane1-pos")) {
759 gtk_paned_set_position(GTK_PANED(main_pane1), atoi(p));
760 } else if(!strcmp(buf, "pane2-pos")) {
761 gtk_paned_set_position(GTK_PANED(main_pane2), atoi(p));
762 } else if(!strcmp(buf, "pane3-pos")) {
763 gtk_paned_set_position(GTK_PANED(main_pane3), atoi(p));
764 } else if(!strcmp(buf, "pubhubaddr")) {
765 free(pubhubaddr);
766 pubhubaddr = sstrdup(p);
767 } else if(!strcmp(buf, "dcserver")) {
768 free(dcserver);
769 dcserver = sstrdup(p);
770 } else if(!strcmp(buf, "advexpanded")) {
771 gtk_expander_set_expanded(GTK_EXPANDER(main_advexp), atoi(p));
772 } else if(!strcmp(buf, "connectas")) {
773 free(connectas);
774 connectas = sstrdup(p);
775 } else if(!strcmp(buf, "autoconn")) {
776 autoconn = atoi(p);
777 } else if(!strcmp(buf, "filternoslots")) {
778 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(main_filternoslots), atoi(p));
779 }
780 }
781 free(buf);
782 fclose(cfgfile);
783/*
784 if(w != 1589)
785 abort();
786*/
787 gtk_window_resize(GTK_WINDOW(main_wnd), w, h);
788}
789
790void updateconfigfile(void)
791{
792 FILE *cfgfile;
793 char *homedir, *buf;
794 int w, h;
795
796 if((homedir = getenv("HOME")) == NULL)
797 {
798 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
799 return;
800 }
801 buf = sprintf2("%s/.dolconrc", homedir);
802 if((cfgfile = fopen(buf, "w")) == NULL)
803 {
804 free(buf);
805 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not open configuration file for writing: %s"), strerror(errno));
806 return;
807 }
808 free(buf);
809 gtk_window_get_size(GTK_WINDOW(main_wnd), &w, &h);
810 fprintf(cfgfile, "wnd-width: %i\n", w);
811 fprintf(cfgfile, "wnd-height: %i\n", h);
812 fprintf(cfgfile, "pane1-pos: %i\n", gtk_paned_get_position(GTK_PANED(main_pane1)));
813 fprintf(cfgfile, "pane2-pos: %i\n", gtk_paned_get_position(GTK_PANED(main_pane2)));
814 fprintf(cfgfile, "pane3-pos: %i\n", gtk_paned_get_position(GTK_PANED(main_pane3)));
815 fprintf(cfgfile, "pubhubaddr: %s\n", pubhubaddr);
816 fprintf(cfgfile, "dcserver: %s\n", dcserver);
817 fprintf(cfgfile, "advexpanded: %i\n", gtk_expander_get_expanded(GTK_EXPANDER(main_advexp)));
818 fprintf(cfgfile, "connectas: %s\n", connectas);
819 fprintf(cfgfile, "autoconn: %i\n", autoconn);
820 fprintf(cfgfile, "filternoslots: %i\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(main_filternoslots)));
821 fclose(cfgfile);
822}
823
824gboolean initdeath(GtkWidget *widget, gpointer data)
825{
826 updateconfigfile();
827 gtk_main_quit();
828 return(TRUE);
829}
830
831void cb_inpdialog_entry_activate(GtkWidget *widget, gpointer data)
832{
833 gtk_dialog_response(GTK_DIALOG(inpdialog), GTK_RESPONSE_ACCEPT);
834}
835
836int loginconv(int type, wchar_t *prompt, char **resp, void *data)
837{
838 int ret;
839 char *buf;
840
841 ret = 0;
842 buf = icwcstombs(prompt, "UTF-8");
843 switch(type)
844 {
845 case DC_LOGIN_CONV_NOECHO:
846 if((*resp = inputbox(_("Login"), buf, "", 0)) == NULL)
847 ret = 1;
848 break;
849 case DC_LOGIN_CONV_ECHO:
850 if((*resp = inputbox(_("Login"), buf, "", 1)) == NULL)
851 ret = 1;
852 break;
853 case DC_LOGIN_CONV_INFO:
854 msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "%s", buf);
855 break;
856 case DC_LOGIN_CONV_ERROR:
857 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", buf);
858 break;
859 }
860 free(buf);
861 updatewrite();
862 return(ret);
863}
864
865void getfnlistcallback(int resp, void *data)
866{
867 updatehublist();
868}
869
870void gettrlistcallback(int resp, void *data)
871{
872 updatetransferlists();
873}
874
875void logincallback(int err, wchar_t *reason, void *data)
876{
877 switch(err)
878 {
879 case DC_LOGIN_ERR_SUCCESS:
880 dc_queuecmd(NULL, NULL, L"notify", L"all", L"on", NULL);
881 dc_getfnlistasync(getfnlistcallback, NULL);
882 dc_gettrlistasync(gettrlistcallback, NULL);
883 updatesbar("Authenticated");
884 break;
885 case DC_LOGIN_ERR_NOLOGIN:
886 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not negotiate an acceptable authentication mechanism"));
887 dc_disconnect();
888 dcdisconnected();
889 break;
890 case DC_LOGIN_ERR_SERVER:
891 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The server has encountered an error"));
892 dc_disconnect();
893 dcdisconnected();
894 break;
895 case DC_LOGIN_ERR_USER:
896 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal client error"));
897 dc_disconnect();
898 dcdisconnected();
899 break;
900 case DC_LOGIN_ERR_CONV:
901 dc_disconnect();
902 dcdisconnected();
903 break;
904 case DC_LOGIN_ERR_AUTHFAIL:
905 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Login attempt failed!"));
906 dc_disconnect();
907 dcdisconnected();
908 break;
909 }
910 updatewrite();
911}
912
913GtkTreeIter *ref2iter(GtkTreeRowReference *ref)
914{
915 static GtkTreeIter iter;
916 GtkTreePath *path;
917
918 assert((path = gtk_tree_row_reference_get_path(ref)) != NULL);
919 assert(gtk_tree_model_get_iter(GTK_TREE_MODEL(srchmodel), &iter, path));
920 gtk_tree_path_free(path);
921 return(&iter);
922}
923
924GtkTreeRowReference *iter2ref(GtkTreeIter *iter)
925{
926 GtkTreePath *path;
927 GtkTreeRowReference *ref;
928
929 assert((path = gtk_tree_model_get_path(GTK_TREE_MODEL(srchmodel), iter)) != NULL);
930 assert((ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(srchmodel), path)) != NULL);
931 gtk_tree_path_free(path);
932 return(ref);
933}
934
935struct srchsize *finddiscsize(void)
936{
937 int i;
938 GtkTreeIter iter;
939
940 for(i = 0; i < numsizes; i++)
941 {
942 if(srchsizes[i].size == -1)
943 return(&srchsizes[i]);
944 }
945 srchsizes = srealloc(srchsizes, sizeof(*srchsizes) * ++numsizes);
946 srchsizes[i].size = -1;
947 srchsizes[i].num = 1;
948 srchsizes[i].slots = 0;
949 srchsizes[i].resptime = 0.0;
950 gtk_tree_store_append(srchmodel, &iter, NULL);
951 gtk_tree_store_set(srchmodel, &iter, 3, _("Discrete sizes"), 7, 1, -1);
952 srchsizes[i].ref = iter2ref(&iter);
953 return(&srchsizes[i]);
954}
955
956struct knownspeed *findksentbyname(char *userid)
957{
958 int i;
959
960 for(i = 0; i < numspeeds; i++)
961 {
962 if(!strcmp(knownspeeds[i].userid, userid))
963 return(&knownspeeds[i]);
964 }
965 return(NULL);
966}
967
968struct knownspeed *findksentbyseq(int seq)
969{
970 int i;
971
972 for(i = 0; i < numspeeds; i++)
973 {
974 if(knownspeeds[i].seq == seq)
975 return(&knownspeeds[i]);
976 }
977 return(NULL);
978}
979
980gboolean ksupdaterow(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
981{
982 struct knownspeed *ks;
983 char *userid;
984
985 gtk_tree_model_get(GTK_TREE_MODEL(model), iter, 1, &userid, -1);
986 if(userid == NULL)
987 return(FALSE);
988 ks = findksentbyname(userid);
989 if(ks == NULL)
990 {
991 knownspeeds = srealloc(knownspeeds, (numspeeds + 1) * sizeof(*knownspeeds));
992 ks = &knownspeeds[numspeeds];
993 numspeeds++;
994 ks->userid = sstrdup(userid);
995 ks->speed = -1;
996 ks->seq = -2;
997 ksqueryseq = -2;
998 }
999 g_free(userid);
1000 if(ks->speed != -1)
1001 gtk_tree_store_set(GTK_TREE_STORE(model), iter, 8, ks->speed, -1);
1002 return(FALSE);
1003}
1004
1005gint ksupdatecb(gpointer data)
1006{
1007 int i, oldnum;
1008 time_t now;
1009 wchar_t **users, *buf;
1010 size_t userssize, usersdata;
1011
1012 if(ksquerytag != -1)
1013 return(TRUE);
1014 now = time(NULL);
1015 oldnum = numspeeds;
1016 for(i = 0; i < numspeeds;)
1017 {
1018 if(now - knownspeeds[i].fetched > 60)
1019 {
1020 free(knownspeeds[i].userid);
1021 memmove(&knownspeeds[i], &knownspeeds[i + 1], (--numspeeds - i) * sizeof(*knownspeeds));
1022 } else {
1023 i++;
1024 }
1025 }
1026 if(oldnum != numspeeds)
65123ca2 1027 {
1028 if(numspeeds == 0)
1029 {
1030 free(knownspeeds);
1031 knownspeeds = NULL;
1032 } else {
1033 knownspeeds = srealloc(knownspeeds, numspeeds * sizeof(*knownspeeds));
1034 }
1035 }
d3372da9 1036 gtk_tree_model_foreach(GTK_TREE_MODEL(srchmodel), ksupdaterow, NULL);
1037 if(ksqueryseq == -2)
1038 {
1039 users = NULL;
1040 userssize = usersdata = 0;
1041 ksqueryseq = 0;
1042 for(i = 0; i < numspeeds; i++)
1043 {
1044 if(knownspeeds[i].seq == -2)
1045 {
1046 assert((buf = icmbstowcs(knownspeeds[i].userid, "UTF-8")) != NULL);
1047 knownspeeds[i].seq = ksqueryseq++;
1048 addtobuf(users, buf);
1049 }
1050 }
1051 addtobuf(users, NULL);
0931eb36 1052 ksquerytag = dc_queuecmd(NULL, NULL, L"filtercmd", L"userspeeda", L"%a", users, NULL);
d3372da9 1053 dc_freewcsarr(users);
1054 }
1055 return(TRUE);
1056}
1057
1058void handleresps(void)
1059{
1060 int i;
1061 struct dc_response *resp;
1062 struct dc_intresp *ires;
1063 struct dc_fnetnode *fn;
1064 struct fndata *fndata;
1065 GtkTextIter iter;
1066 GtkTreeIter titer, piter;
1067 char *buf, *p;
1068 int tosbuf;
1069 struct srchsize *ss;
1070 struct knownspeed *ks;
1071
1072 while((resp = dc_getresp()) != NULL)
1073 {
1074 if(!wcscmp(resp->cmdname, L".connect"))
1075 {
5e1e52f1 1076 if(resp->code != 201)
d3372da9 1077 {
5e1e52f1 1078 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The server refused the connection"));
1079 dc_disconnect();
1080 dcdisconnected();
1081 } else if(dc_checkprotocol(resp, DC_LATEST)) {
1082 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Server protocol revision mismatch"));
1083 dc_disconnect();
1084 dcdisconnected();
1085 } else {
d3372da9 1086 tosbuf = 0x10; /* Minimum cost */
1087 setsockopt(dcfd, SOL_IP, IP_TOS, &tosbuf, sizeof(tosbuf));
1088 updatesbar(_("Connected"));
1089 dc_loginasync(connectas, 1, loginconv, logincallback, NULL);
d3372da9 1090 }
1091 } else if(!wcscmp(resp->cmdname, L".notify")) {
1092 dc_uimisc_handlenotify(resp);
1093 switch(resp->code)
1094 {
1095 case 600:
1096 if((ires = dc_interpret(resp)) != NULL)
1097 {
1098 if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1099 {
1100 fndata = fn->udata;
1101 gtk_text_buffer_get_end_iter(fndata->textbuf, &iter);
1102 if((buf = icwcstombs(ires->argv[3].val.str, "UTF-8")) != NULL)
1103 {
1104 gtk_text_buffer_insert_with_tags_by_name(fndata->textbuf, &iter, "<", -1, "sender", NULL);
1105 gtk_text_buffer_insert_with_tags_by_name(fndata->textbuf, &iter, buf, -1, "sender", NULL);
1106 gtk_text_buffer_insert_with_tags_by_name(fndata->textbuf, &iter, ">", -1, "sender", NULL);
1107 gtk_text_buffer_insert(fndata->textbuf, &iter, " ", -1);
1108 free(buf);
1109 }
1110 if((buf = icwcstombs(ires->argv[4].val.str, "UTF-8")) != NULL)
1111 {
1112 gtk_text_buffer_insert(fndata->textbuf, &iter, buf, -1);
1113 gtk_text_buffer_insert(fndata->textbuf, &iter, "\n", -1);
1114 free(buf);
1115 if(curchat == fn->id)
1116 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(main_chatview), &iter, 0, 0, 0, 0);
1117 }
1118 }
1119 dc_freeires(ires);
1120 }
1121 break;
1122 case 601:
1123 case 602:
1124 case 603:
1125 case 604:
1126 case 605:
1127 updatehublist();
1128 break;
1129 case 610:
1130 case 611:
1131 case 612:
1132 case 613:
1133 case 614:
1134 case 615:
1135 case 616:
1136 case 617:
ab9f4164 1137 case 618:
d3372da9 1138 updatetransferlists();
1139 break;
1140 case 620:
1141 if((ires = dc_interpret(resp)) != NULL)
1142 {
1143 if(ires->argv[0].val.num == nextsrch)
1144 srcheta = time(NULL) + ires->argv[0].val.num;
1145 dc_freeires(ires);
1146 }
1147 break;
1148 case 621:
1149 if((ires = dc_interpret(resp)) != NULL)
1150 {
1151 if(ires->argv[0].val.num == nextsrch)
1152 {
1153 if(cursrch != -1)
0931eb36 1154 dc_queuecmd(NULL, NULL, L"cansrch", L"%i", cursrch, NULL);
d3372da9 1155 cursrch = nextsrch;
1156 nextsrch = -1;
1157 gtk_widget_set_sensitive(main_realsrch, TRUE);
1158 gtk_widget_set_sensitive(main_simplesrch, TRUE);
1159 gtk_widget_set_sensitive(main_srchbtn, TRUE);
1160 gtk_widget_set_sensitive(main_srchcanbtn, FALSE);
1161 srchstatupdate();
1162 gtk_entry_set_text(GTK_ENTRY(main_realsrch), "");
1163 gtk_entry_set_text(GTK_ENTRY(main_simplesrch), "");
1164 gtk_tree_store_clear(srchmodel);
1165 freesrchsizes();
1166 }
1167 dc_freeires(ires);
1168 }
1169 break;
1170 case 622:
1171 if((ires = dc_interpret(resp)) != NULL)
1172 {
1173 if(ires->argv[0].val.num == cursrch)
1174 {
1175 for(i = 0; i < numsizes; i++)
1176 {
1177 if(srchsizes[i].size == ires->argv[4].val.num)
1178 break;
1179 }
1180 if(i == numsizes)
1181 {
1182 srchsizes = srealloc(srchsizes, sizeof(*srchsizes) * ++numsizes);
1183 srchsizes[i].size = ires->argv[4].val.num;
1184 srchsizes[i].num = 1;
1185 srchsizes[i].slots = ires->argv[5].val.num;
1186 srchsizes[i].resptime = ires->argv[7].val.flnum;
1187 ss = finddiscsize();
1188 ss->slots += ires->argv[5].val.num;
1189 if((ss->resptime == 0.0) || (ss->resptime > ires->argv[7].val.flnum))
1190 ss->resptime = ires->argv[7].val.flnum;
1191 piter = *ref2iter(ss->ref);
1192 gtk_tree_store_set(srchmodel, &piter, 5, ss->slots, 6, ss->resptime, -1);
1193 gtk_tree_store_append(srchmodel, &titer, &piter);
1194 srchsizes[i].ref = iter2ref(&titer);
1195 } else if(srchsizes[i].num == 1) {
ab9f4164 1196 char *filename, *peername, *fnetname, *hash;
d3372da9 1197 int slots, speed;
1198 double resptime;
1199
ab9f4164 1200 gtk_tree_model_get(GTK_TREE_MODEL(srchmodel), ref2iter(srchsizes[i].ref), 0, &fnetname, 1, &peername, 3, &filename, 5, &slots, 6, &resptime, 8, &speed, 9, &hash, -1);
d3372da9 1201 gtk_tree_store_remove(srchmodel, ref2iter(srchsizes[i].ref));
1202 gtk_tree_row_reference_free(srchsizes[i].ref);
1203 ss = finddiscsize();
1204 ss->slots -= slots;
1205 gtk_tree_store_set(srchmodel, ref2iter(ss->ref), 5, ss->slots, -1);
1206 gtk_tree_store_append(srchmodel, &piter, NULL);
1207 srchsizes[i].slots = ires->argv[5].val.num + slots;
1208 srchsizes[i].resptime = (ires->argv[7].val.flnum < resptime)?ires->argv[7].val.flnum:resptime;
1209 srchsizes[i].num = 2;
1210 srchsizes[i].ref = iter2ref(&piter);
1211 gtk_tree_store_set(srchmodel, &piter, 4, srchsizes[i].size, 5, srchsizes[i].slots, 6, srchsizes[i].resptime, 7, 2, -1);
1212 if((buf = icwcstombs(ires->argv[1].val.str, "UTF-8")) != NULL)
1213 {
1214 p = buf;
ab9f4164 1215 /* XXX: Too NMDC-specific! */
d3372da9 1216 if(strrchr(p, '\\') != NULL)
1217 p = strrchr(p, '\\') + 1;
1218 gtk_tree_store_set(srchmodel, &piter, 3, p, -1);
1219 free(buf);
1220 }
1221 gtk_tree_store_append(srchmodel, &titer, &piter);
ab9f4164 1222 gtk_tree_store_set(srchmodel, &titer, 0, fnetname, 1, peername, 2, peername, 3, filename, 4, srchsizes[i].size, 5, slots, 6, resptime, 8, speed, 9, hash, -1);
1223 g_free(filename); g_free(peername); g_free(fnetname); g_free(hash);
d3372da9 1224 gtk_tree_store_append(srchmodel, &titer, &piter);
1225 } else {
1226 srchsizes[i].num++;
1227 srchsizes[i].slots += ires->argv[5].val.num;
1228 if(ires->argv[7].val.flnum < srchsizes[i].resptime)
1229 srchsizes[i].resptime = ires->argv[7].val.flnum;
1230 piter = *ref2iter(srchsizes[i].ref);
1231 gtk_tree_store_set(srchmodel, &piter, 5, srchsizes[i].slots, 6, srchsizes[i].resptime, 7, srchsizes[i].num, -1);
1232 gtk_tree_store_append(srchmodel, &titer, &piter);
1233 }
1234 if((buf = icwcstombs(ires->argv[1].val.str, "UTF-8")) != NULL)
1235 {
1236 gtk_tree_store_set(srchmodel, &titer, 3, buf, -1);
1237 free(buf);
1238 }
1239 if((buf = icwcstombs(ires->argv[2].val.str, "UTF-8")) != NULL)
1240 {
1241 gtk_tree_store_set(srchmodel, &titer, 0, buf, -1);
1242 free(buf);
1243 }
1244 if((buf = icwcstombs(ires->argv[3].val.str, "UTF-8")) != NULL)
1245 {
1246 gtk_tree_store_set(srchmodel, &titer, 1, buf, -1);
1247 gtk_tree_store_set(srchmodel, &titer, 2, buf, -1);
1248 free(buf);
1249 }
ab9f4164 1250 if((buf = icwcstombs(ires->argv[8].val.str, "UTF-8")) != NULL)
1251 {
1252 gtk_tree_store_set(srchmodel, &titer, 9, buf, -1);
1253 free(buf);
1254 }
d3372da9 1255 gtk_tree_store_set(srchmodel, &titer, 4, ires->argv[4].val.num, 5, ires->argv[5].val.num, 6, ires->argv[7].val.flnum, 8, -1, -1);
1256 }
1257 dc_freeires(ires);
1258 }
1259 break;
1260 default:
1261 break;
1262 }
1263 } else if(!wcscmp(resp->cmdname, L"filtercmd")) {
1264 if((ksquerytag >= 0) && (ksquerytag == resp->tag))
1265 {
1266 for(i = 0; i < resp->numlines; i++)
1267 {
1268 assert((ks = findksentbyseq(i)) != NULL);
1269 ks->speed = wcstol(resp->rlines[i].argv[1], NULL, 10);
1270 ks->seq = -1;
1271 ks->fetched = time(NULL);
1272 }
1273 ksquerytag = -1;
1274 ksupdatecb(NULL);
4c27a5f2 1275 } else if((lsrestag >= 0) && (lsrestag == resp->tag)) {
1276 for(i = 0; i < resp->numlines; i++)
1277 {
1278 if(!wcsncmp(resp->rlines[i].argv[1], L"id:", 3))
1279 {
1280 gtk_list_store_append(reslist, &titer);
1281 gtk_list_store_set(reslist, &titer, 0, icswcstombs(resp->rlines[i].argv[1] + 3, "UTF-8", NULL), -1);
1282 } else if(!wcsncmp(resp->rlines[i].argv[1], L"size:", 5)) {
1283 gtk_list_store_set(reslist, &titer, 1, wcstol(resp->rlines[i].argv[1] + 5, NULL, 10), -1);
1284 } else if(!wcsncmp(resp->rlines[i].argv[1], L"prog:", 5)) {
1285 gtk_list_store_set(reslist, &titer, 2, wcstol(resp->rlines[i].argv[1] + 5, NULL, 10), -1);
1286 } else if(!wcsncmp(resp->rlines[i].argv[1], L"name:", 5)) {
1287 gtk_list_store_set(reslist, &titer, 3, icswcstombs(resp->rlines[i].argv[1] + 5, "UTF-8", NULL), -1);
1288 } else if(!wcsncmp(resp->rlines[i].argv[1], L"lock:", 5)) {
1289 if(!wcscmp(resp->rlines[i].argv[1] + 5, L"yes"))
1290 gtk_list_store_set(reslist, &titer, 4, TRUE, -1);
1291 else
1292 gtk_list_store_set(reslist, &titer, 4, FALSE, -1);
1293 } else if(!wcsncmp(resp->rlines[i].argv[1], L"hash:", 5)) {
1294 gtk_list_store_set(reslist, &titer, 5, icswcstombs(resp->rlines[i].argv[1] + 5, "UTF-8", NULL), -1);
1295 }
1296 }
1297 lsrestag = -1;
1298 gtk_widget_set_sensitive(reslist_reload, TRUE);
d3372da9 1299 }
1300 }
1301 dc_freeresp(resp);
1302 }
1303 updatewrite();
1304}
1305
1306void dcfdcallback(gpointer data, gint source, GdkInputCondition condition)
1307{
1308 int errnobak;
1309
1310 if(((condition & GDK_INPUT_READ) && dc_handleread()) || ((condition & GDK_INPUT_WRITE) && dc_handlewrite()))
1311 {
1312 errnobak = errno;
1313 dcdisconnected();
1314 if(errnobak == 0)
1315 {
1316 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The server has closed the connection"));
1317 } else {
1318 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The connection to the server failed:\n\n%s"), strerror(errnobak));
1319 }
1320 return;
1321 }
1322 handleresps();
1323}
1324
1325void cb_main_dconnmenu_activate(GtkWidget *widget, gpointer data)
1326{
1327 if(dcfd < 0)
1328 return;
1329 dc_disconnect();
1330 dcdisconnected();
1331}
1332
1333void cb_main_prefmenu_activate(GtkWidget *widget, gpointer data)
1334{
1335 GtkWidget *dialog, *swnd;
1336 int resp;
1337
1338 dialog = gtk_dialog_new_with_buttons(_("Preferences"), GTK_WINDOW(main_wnd), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
1339 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), swnd = create_pref_wnd(), TRUE, TRUE, 0);
1340 gtk_entry_set_text(GTK_ENTRY(pref_pubhuburl), pubhubaddr);
1341 gtk_entry_set_text(GTK_ENTRY(pref_connectas), connectas);
1342 gtk_entry_set_text(GTK_ENTRY(pref_dcserver), dcserver);
1343 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pref_autoconn), autoconn);
1344 gtk_widget_show(swnd);
1345 resp = gtk_dialog_run(GTK_DIALOG(dialog));
1346 if(resp == GTK_RESPONSE_ACCEPT)
1347 {
1348 free(pubhubaddr);
1349 pubhubaddr = sstrdup(gtk_entry_get_text(GTK_ENTRY(pref_pubhuburl)));
1350 free(connectas);
1351 connectas = sstrdup(gtk_entry_get_text(GTK_ENTRY(pref_connectas)));
1352 free(dcserver);
1353 dcserver = sstrdup(gtk_entry_get_text(GTK_ENTRY(pref_dcserver)));
1354 autoconn = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_autoconn));
1355 }
1356 gtk_widget_destroy(dialog);
1357}
1358
4c27a5f2 1359void cb_main_lsres_activate(GtkWidget *widget, gpointer data)
1360{
1361 gtk_list_store_clear(reslist);
1362 gtk_widget_set_sensitive(reslist_delete, FALSE);
1363 gtk_widget_set_sensitive(reslist_search, FALSE);
1364 gtk_widget_show(reslist_wnd);
1365 if(lsrestag == -1)
1366 {
1367 lsrestag = dc_queuecmd(NULL, NULL, L"filtercmd", L"lsres", NULL);
1368 gtk_widget_set_sensitive(reslist_reload, FALSE);
1369 }
1370}
1371
d3372da9 1372void dcconnect(char *host)
1373{
65bf229b 1374 dcfd = dc_connect(host);
d3372da9 1375 if(dcfd < 0)
1376 {
1377 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect:\n\n%s"), strerror(errno));
1378 return;
1379 }
1380 gdkread = gdk_input_add(dcfd, GDK_INPUT_READ, dcfdcallback, NULL);
1381 updatewrite();
1382 gtk_widget_set_sensitive(main_connmenu, FALSE);
1383 gtk_widget_set_sensitive(main_dconnmenu, TRUE);
1384 updatesbar(_("Connecting..."));
1385}
1386
1387void cb_main_connmenu_activate(GtkWidget *widget, gpointer data)
1388{
1389 char *buf;
1390
1391 if(dcfd >= 0)
1392 return;
1393 if((buf = inputbox(_("Connect"), _("Server address:"), dcserver, 1)) == NULL)
1394 return;
1395 dcconnect(buf);
1396 free(buf);
1397}
1398
1399void cb_main_sdmenu_activate(GtkWidget *widget, gpointer data)
1400{
1401 int tag;
1402 struct dc_response *resp;
1403
1404 if(dcfd < 0)
1405 {
1406 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1407 return;
1408 }
1409 tag = dc_queuecmd(NULL, NULL, L"shutdown", NULL);
1410 if((resp = dc_gettaggedrespsync(tag)) != NULL)
1411 {
1412 if(resp->code == 502)
1413 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1414 dc_freeresp(resp);
1415 }
1416 handleresps();
1417}
1418
1419void cb_main_fnaddr_activate(GtkWidget *widget, gpointer data)
1420{
1421 int tag;
d3372da9 1422 struct dc_response *resp;
0783994d 1423 wchar_t **toks;
d3372da9 1424
1425 if(dcfd < 0)
1426 {
1427 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1428 return;
1429 }
0783994d 1430 toks = dc_lexsexpr(icsmbstowcs((char *)gtk_entry_get_text(GTK_ENTRY(main_fnaddr)), "UTF-8", NULL));
1431 if(*toks == NULL)
d3372da9 1432 {
0783994d 1433 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Illegal address entered"));
1434 return;
d3372da9 1435 }
0783994d 1436 if(wcschr(toks[0], L':') == NULL)
1437 {
1438 toks[0] = srealloc(toks[0], (wcslen(toks[0]) + 5) * sizeof(wchar_t));
1439 wcscat(toks[0], L":411");
1440 }
0931eb36 1441 tag = dc_queuecmd(NULL, NULL, L"cnct", L"dc", L"%a", toks, NULL);
0783994d 1442 dc_freewcsarr(toks);
d3372da9 1443 if((resp = dc_gettaggedrespsync(tag)) != NULL)
1444 {
1445 if(resp->code == 502)
1446 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1447 if(resp->code == 509)
1448 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("The server could not parse that address"));
eaf3d948 1449 if(resp->code == 515)
1450 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("There are too many hubs connected"));
d3372da9 1451 dc_freeresp(resp);
1452 }
1453 gtk_entry_set_text(GTK_ENTRY(main_fnaddr), "");
1454 handleresps();
1455}
1456
eba4327a 1457void setpubhubmodel(GtkTreeModel *model, int sortcol, int numcols, int *cols, char **names)
d3372da9 1458{
a745644c 1459 GtkTreeViewColumn *col;
cac656ef 1460 GtkCellRenderer *rnd;
a745644c 1461 GtkTreeModel *sortmodel;
eba4327a 1462 int i;
a745644c 1463
1464 while((col = gtk_tree_view_get_column(GTK_TREE_VIEW(main_phublist), 0)) != NULL)
1465 gtk_tree_view_remove_column(GTK_TREE_VIEW(main_phublist), col);
eba4327a 1466 for(i = 0; i < numcols; i++) {
cac656ef 1467 if(gtk_tree_model_get_column_type(model, cols[i]) == G_TYPE_INT64)
1468 {
1469 col = gtk_tree_view_column_new();
1470 gtk_tree_view_column_set_title(col, names[i]);
1471 rnd = gtk_cell_renderer_text_new();
1472 gtk_tree_view_column_pack_start(col, rnd, TRUE);
a2f77326 1473 gtk_tree_view_column_set_cell_data_func(col, rnd, transnicebytefunc2, GINT_TO_POINTER(cols[i]), NULL);
cac656ef 1474 } else {
1475 col = gtk_tree_view_column_new_with_attributes(names[i], gtk_cell_renderer_text_new(), "text", cols[i], NULL);
1476 }
eba4327a 1477 gtk_tree_view_column_set_sort_column_id(col, cols[i]);
a745644c 1478 gtk_tree_view_column_set_resizable(col, TRUE);
1479 gtk_tree_view_append_column(GTK_TREE_VIEW(main_phublist), col);
1480 }
a745644c 1481 sortmodel = gtk_tree_model_sort_new_with_model(model);
1482 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortmodel), sortcol, GTK_SORT_DESCENDING);
1483 gtk_tree_view_set_model(GTK_TREE_VIEW(main_phublist), sortmodel);
1484 g_object_unref(sortmodel);
1485}
1486
eba4327a 1487xmlNodePtr findnode(xmlNodePtr node, char *name)
1488{
1489 for(; node != NULL; node = node->next)
1490 {
1491 if(!strcmp((char *)node->name, name))
1492 break;
1493 }
1494 return(node);
1495}
1496
1497int pubhubxmlhandler(int op, char *buf, size_t *len)
1498{
1499 static xmlParserCtxtPtr ctxt = NULL;
1500 int i, match;
1501 xmlNodePtr dr, r, cr, c, n;
1502 int numcols, *cols, sortcol;
1503 GType type, *types;
1504 char **names, *name, *stype, *attr;
1505 GtkListStore *model;
1506 GtkTreeIter iter;
1507
1508 numcols = 0;
1509 names = NULL;
1510 types = NULL;
1511 switch(op)
1512 {
1513 case PHO_INIT:
1514 break;
1515 case PHO_DATA:
1516 if(ctxt == NULL) {
1517 ctxt = xmlCreatePushParserCtxt(NULL, NULL, buf, *len, pubhubaddr);
1518 *len = 0;
1519 if(ctxt == NULL)
1520 return(1);
1521 } else {
1522 xmlParseChunk(ctxt, buf, *len, 0);
1523 *len = 0;
1524 }
1525 break;
1526 case PHO_EOF:
031b283f 1527 if(ctxt == NULL)
1528 {
1529 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("A hub list could not be read from %s"), pubhubaddr);
1530 break;
1531 }
eba4327a 1532 xmlParseChunk(ctxt, NULL, 0, 1);
1533 if(!ctxt->wellFormed)
1534 {
1535 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The hub list at %s is not valid"), pubhubaddr);
1536 break;
1537 }
1538 dr = r = cr = NULL;
1539 dr = xmlDocGetRootElement(ctxt->myDoc);
1540 if(dr != NULL)
1541 r = findnode(dr->children, "Hubs");
1542 if(r != NULL)
1543 cr = findnode(r->children, "Columns");
1544 if(cr == NULL)
1545 {
1546 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("The hub list at %s cannot be understood"), pubhubaddr);
1547 break;
1548 }
1549 for(c = findnode(cr->children, "Column"); c != NULL; c = findnode(c->next, "Column"))
1550 {
1551 name = (char *)xmlGetProp(c, (xmlChar *)"Name");
1552 stype = (char *)xmlGetProp(c, (xmlChar *)"Type");
1553 type = G_TYPE_INVALID;
1554 if(stype != NULL)
1555 {
1556 if(!strcmp(stype, "string"))
1557 type = G_TYPE_STRING;
1558 else if(!strcmp(stype, "int"))
1559 type = G_TYPE_INT;
1560 else if(!strcmp(stype, "bytes"))
1561 type = G_TYPE_INT64;
1562 }
1563 if((name != NULL) && (type != G_TYPE_INVALID))
1564 {
1565 names = srealloc(names, (numcols + 1) * sizeof(*names));
1566 types = srealloc(types, (numcols + 1) * sizeof(*names));
1567 names[numcols] = sstrdup(name);
1568 types[numcols] = type;
1569 numcols++;
1570 }
1571 if(name != NULL)
1572 xmlFree(name);
1573 if(stype != NULL)
1574 xmlFree(stype);
1575 }
1576 if(numcols == 0)
1577 {
1578 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("The hub list at %s did not contain any columns"), pubhubaddr);
1579 break;
1580 }
1581 for(i = 0; i < numcols; i++)
1582 {
1583 if(!strcmp(names[i], "Address"))
1584 {
1585 name = names[0];
1586 names[0] = names[i];
1587 names[i] = name;
1588 type = types[0];
1589 types[0] = types[i];
1590 types[i] = type;
1591 break;
1592 }
1593 }
1594 if(i == numcols)
1595 {
a2f77326 1596 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("The hub list at %s did not contain the address to any hubs"), pubhubaddr);
eba4327a 1597 break;
1598 }
1599 model = gtk_list_store_newv(numcols, types);
1600 for(n = findnode(r->children, "Hub"); n != NULL; n = findnode(n->next, "Hub"))
1601 {
1602 if(!xmlHasProp(n, (xmlChar *)"Address") || !xmlHasProp(n, (xmlChar *)"Name"))
1603 continue;
1604 if(filterpubhub)
1605 {
1606 match = 0;
1607 attr = (char *)xmlGetProp(n, (xmlChar *)"Name");
1608 if(!regexec(&pubhubfilter, attr, 0, NULL, 0))
1609 match = 1;
1610 xmlFree(attr);
1611 if((attr = (char *)xmlGetProp(n, (xmlChar *)"Description")) != NULL)
1612 {
1613 if(!regexec(&pubhubfilter, attr, 0, NULL, 0))
1614 match = 1;
1615 xmlFree(attr);
1616 }
1617 if(!match)
1618 continue;
1619 }
1620 gtk_list_store_append(model, &iter);
1621 for(i = 0; i < numcols; i++)
1622 {
1623 attr = (char *)xmlGetProp(n, (xmlChar *)names[i]);
1624 if(attr != NULL)
1625 {
1626 if(types[i] == G_TYPE_STRING)
1627 gtk_list_store_set(model, &iter, i, attr, -1);
1628 else if(types[i] == G_TYPE_INT)
1629 gtk_list_store_set(model, &iter, i, atoi(attr), -1);
1630 else if(types[i] == G_TYPE_INT64)
1631 gtk_list_store_set(model, &iter, i, strtoll(attr, NULL, 0), -1);
1632 xmlFree(attr);
1633 }
1634 }
1635 }
1636 cols = smalloc((numcols - 1) * sizeof(*cols));
1637 for(i = 1; i < numcols; i++)
1638 cols[i - 1] = i;
1639 sortcol = 0;
1640 for(i = 0; i < numcols; i++)
1641 {
1642 if(!strcmp(names[i], "Users"))
1643 sortcol = i;
1644 }
1645 setpubhubmodel(GTK_TREE_MODEL(model), sortcol, numcols - 1, cols, names + 1);
1646 free(cols);
dd30d71b 1647 g_object_unref(model);
eba4327a 1648 break;
1649 case PHO_FINI:
1650 if(ctxt != NULL)
1651 {
1652 if(ctxt->myDoc != NULL)
1653 xmlFreeDoc(ctxt->myDoc);
1654 xmlFreeParserCtxt(ctxt);
1655 ctxt = NULL;
1656 }
1657 break;
1658 }
1659 if(numcols != 0)
1660 {
1661 for(i = 0; i < numcols; i++)
1662 free(names[i]);
1663 free(names);
1664 free(types);
1665 }
1666 return(0);
1667}
1668
a745644c 1669int pubhuboldhandler(int op, char *buf, size_t *len)
1670{
1671 static GtkListStore *model = NULL;
1672 int i;
d3372da9 1673 char *p, *p2;
d3372da9 1674 wchar_t *wbuf;
eba4327a 1675 char *fields[4], *names[3];
1676 int cols[3];
d3372da9 1677 GtkTreeIter iter;
d3372da9 1678
a745644c 1679 switch(op)
d3372da9 1680 {
a745644c 1681 case PHO_INIT:
1682 model = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
1683 break;
1684 case PHO_DATA:
1685 while((p = memchr(buf, '\n', *len)) != NULL)
d3372da9 1686 {
a745644c 1687 *(p++) = 0;
1688 for(i = 0, p2 = buf; i < 4; i++) {
d3372da9 1689 fields[i] = p2;
1690 if((p2 = strchr(p2, '|')) == NULL)
1691 break;
1692 *(p2++) = 0;
1693 }
a745644c 1694 if(i == 4) {
1695 for(i = 0; i < 4; i++) {
1696 if((wbuf = icsmbstowcs(fields[i], DCCHARSET, NULL)) == NULL) {
efea2eaf 1697 fields[i] = sstrdup(_("(Invalid character)"));
1698 } else {
1699 if((fields[i] = icwcstombs(wbuf, "UTF-8")) == NULL)
1700 break;
d3372da9 1701 }
d3372da9 1702 }
a745644c 1703 if(i == 4) {
1704 if(!filterpubhub || !regexec(&pubhubfilter, fields[0], 0, NULL, 0) || !regexec(&pubhubfilter, fields[2], 0, NULL, 0)) {
1705 gtk_list_store_append(model, &iter);
1706 gtk_list_store_set(model, &iter, 0, fields[1], 1, fields[0], 2, fields[2], 3, atoi(fields[3]), -1);
1707 }
d3372da9 1708 }
1709 for(i--; i >= 0; i--)
1710 free(fields[i]);
1711 }
a745644c 1712 memmove(buf, p, *len -= p - buf);
1713 }
1714 break;
1715 case PHO_EOF:
eba4327a 1716 cols[0] = 3; names[0] = _("# users");
1717 cols[1] = 1; names[1] = _("Name");
1718 cols[2] = 2; names[2] = _("Description");
1719 setpubhubmodel(GTK_TREE_MODEL(model), 3, 3, cols, names);
a745644c 1720 break;
1721 case PHO_FINI:
1722 if(model != NULL)
1723 g_object_unref(model);
1724 model = NULL;
1725 break;
1726 }
1727 return(0);
1728}
1729
1730void pubhubfdcallback(gpointer data, gint source, GdkInputCondition condition)
1731{
1732 static char buf[65536];
1733 static size_t bufpos = 0;
1734 int ret, reset;
1735
1736 if(!(condition & GDK_INPUT_READ))
1737 return;
1738 if(bufpos == sizeof(buf))
1739 bufpos = 0;
1740 ret = read(pubhubfd, buf + bufpos, sizeof(buf) - bufpos);
1741 reset = 0;
1742 if(ret <= 0)
1743 {
1744 if(ret < 0)
1745 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not read from public hub listing process: %s"), strerror(errno));
1746 else
eba4327a 1747 pubhubhandler(PHO_EOF, NULL, NULL);
a745644c 1748 reset = 1;
1749 } else {
1750 bufpos += ret;
1751 if(pubhubhandler(PHO_DATA, buf, &bufpos))
1752 reset = 1;
1753 }
1754 if(reset)
1755 {
1756 pubhubhandler(PHO_FINI, NULL, NULL);
1757 pubhubhandler = NULL;
1758 gdk_input_remove(pubhubtag);
1759 close(pubhubfd);
1760 kill(pubhubproc, SIGINT);
1761 pubhubfd = pubhubtag = -1;
1762 pubhubproc = 0;
1763 bufpos = 0;
1764 if(filterpubhub)
1765 {
1766 regfree(&pubhubfilter);
1767 filterpubhub = 0;
d3372da9 1768 }
d3372da9 1769 }
d3372da9 1770}
1771
1772void cb_main_pubhubfilter_activate(GtkWidget *widget, gpointer data)
1773{
1774 int pipe1[2], pipe2[2];
1775 int len, err;
a745644c 1776 const char *buf, *p;
d3372da9 1777 char errbuf[1024];
1778
1779 if(pubhubtag >= 0)
1780 gdk_input_remove(pubhubtag);
1781 if(pubhubfd >= 0)
1782 close(pubhubfd);
1783 if(pubhubproc > 0)
1784 kill(pubhubproc, SIGINT);
a745644c 1785 if(pubhubhandler != NULL)
1786 {
1787 pubhubhandler(PHO_FINI, NULL, NULL);
1788 pubhubhandler = NULL;
1789 }
d3372da9 1790 if(filterpubhub)
1791 {
1792 regfree(&pubhubfilter);
1793 filterpubhub = 0;
1794 }
1795 buf = gtk_entry_get_text(GTK_ENTRY(main_pubhubfilter));
1796 if(*buf)
1797 {
1798 if((err = regcomp(&pubhubfilter, buf, REG_EXTENDED | REG_ICASE | REG_NOSUB)) != 0)
1799 {
1800 regerror(err, &pubhubfilter, errbuf, sizeof(errbuf));
1801 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not compile regex: %s", errbuf);
1802 regfree(&pubhubfilter);
1803 filterpubhub = 0;
1804 return;
1805 }
1806 filterpubhub = 1;
1807 }
d3372da9 1808 pipe(pipe1);
1809 if((pubhubproc = fork()) == 0)
1810 {
1811 dup2(pipe1[1], 1);
1812 close(pipe1[0]);
1813 close(pipe1[1]);
1814 execlp("wget", "wget", "-qO", "-", pubhubaddr, NULL);
1815 perror("wget");
1816 exit(127);
1817 }
1818 close(pipe1[1]);
1819 pubhubfd = pipe1[0];
1820 len = strlen(pubhubaddr);
a745644c 1821 p = pubhubaddr + len;
1822 if((len > 4) && !strncmp(p - 4, ".bz2", 4))
d3372da9 1823 {
a745644c 1824 p -= 4;
1825 len -= 4;
d3372da9 1826 pipe(pipe2);
1827 if(fork() == 0)
1828 {
1829 dup2(pipe1[0], 0);
1830 dup2(pipe2[1], 1);
1831 close(pipe1[0]);
1832 close(pipe2[0]);
1833 close(pipe2[1]);
1834 execlp("bzcat", "bzcat", NULL);
1835 perror("bzcat");
1836 exit(127);
1837 }
1838 close(pipe1[0]);
1839 close(pipe2[1]);
1840 pubhubfd = pipe2[0];
1841 }
a745644c 1842 if((len > 4) && !strncmp(p - 4, ".xml", 4))
1843 {
1844 p -= 4;
1845 len -= 4;
eba4327a 1846 pubhubhandler = pubhubxmlhandler;
a745644c 1847 } else {
1848 pubhubhandler = pubhuboldhandler;
1849 }
1850 pubhubhandler(PHO_INIT, NULL, NULL);
d3372da9 1851 pubhubtag = gdk_input_add(pubhubfd, GDK_INPUT_READ, pubhubfdcallback, NULL);
1852}
1853
1854void cb_main_dcnctbtn_clicked(GtkWidget *widget, gpointer data)
1855{
1856 GtkTreeIter iter;
1857 int tag, id;
1858 struct dc_response *resp;
1859
1860 if(dcfd < 0)
1861 {
1862 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1863 return;
1864 }
1865 if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(main_fnetnodes)), NULL, &iter))
1866 {
1867 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("No hub selected"));
1868 return;
1869 }
1870 gtk_tree_model_get(GTK_TREE_MODEL(fnmodel), &iter, 0, &id, -1);
0931eb36 1871 tag = dc_queuecmd(NULL, NULL, L"dcnct", L"%i", id, NULL);
d3372da9 1872 if((resp = dc_gettaggedrespsync(tag)) != NULL)
1873 {
1874 if(resp->code == 502)
1875 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1876 dc_freeresp(resp);
1877 }
1878 handleresps();
1879}
1880
1881void cb_main_phublist_cchange(GtkWidget *widget, gpointer data)
1882{
1883 GtkTreeIter iter;
1884 GtkTreeModel *model;
1885 char *addr;
1886
1887 if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(main_phublist)), &model, &iter))
1888 return;
a745644c 1889 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &addr, -1);
d3372da9 1890 gtk_entry_set_text(GTK_ENTRY(main_fnaddr), addr);
1891 g_free(addr);
1892}
1893
1894void cb_main_phublist_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data)
1895{
1896 int tag;
1897 struct dc_response *resp;
1898 GtkTreeIter iter;
1899 GtkTreeModel *model;
1900 char *buf;
1901
1902 if(dcfd < 0)
1903 {
1904 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1905 return;
1906 }
1907 model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
1908 if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
1909 return;
a745644c 1910 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &buf, -1);
d3372da9 1911 if(strchr(buf, ':') == NULL)
1912 {
1913 buf = g_realloc(buf, strlen(buf) + 5);
1914 strcat(buf, ":411");
1915 }
0931eb36 1916 tag = dc_queuecmd(NULL, NULL, L"cnct", L"dc", L"%s", buf, NULL);
d3372da9 1917 g_free(buf);
1918 gtk_entry_set_text(GTK_ENTRY(main_fnaddr), "");
1919 if((resp = dc_gettaggedrespsync(tag)) != NULL)
1920 {
1921 if(resp->code == 502)
1922 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1923 if(resp->code == 509)
1924 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("The server could not parse that address"));
eaf3d948 1925 if(resp->code == 515)
1926 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("There are too many hubs connected"));
d3372da9 1927 dc_freeresp(resp);
1928 }
1929 handleresps();
1930}
1931
1932void cb_main_chatnodes_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer uudata)
1933{
1934 GtkTreeIter iter;
1935 int id;
1936 struct dc_fnetnode *fn;
1937 struct fndata *data;
1938
1939 if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(fnmodel), &iter, path))
1940 return;
1941 gtk_tree_model_get(GTK_TREE_MODEL(fnmodel), &iter, 0, &id, -1);
1942 if((fn = dc_findfnetnode(id)) == NULL)
1943 return;
1944 data = fn->udata;
1945 curchat = id;
1946 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fnmodel), &iter))
1947 {
1948 do
1949 {
1950 gtk_tree_model_get(GTK_TREE_MODEL(fnmodel), &iter, 0, &id, -1);
1951 if(id == curchat)
1952 gtk_list_store_set(fnmodel, &iter, 5, "gtk-apply", -1);
1953 else
1954 gtk_list_store_set(fnmodel, &iter, 5, NULL, -1);
1955 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(fnmodel), &iter));
1956 }
1957 gtk_text_view_set_buffer(GTK_TEXT_VIEW(main_chatview), GTK_TEXT_BUFFER(data->textbuf));
1958}
1959
1960void cb_main_chatstr_activate(GtkWidget *widget, gpointer data)
1961{
1962 int tag;
1963 const char *buf;
1964 struct dc_response *resp;
1965
1966 if(dcfd < 0)
1967 {
1968 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1969 return;
1970 }
1971 if(curchat < 0)
1972 {
1973 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("No hub selected"));
1974 return;
1975 }
1976 buf = gtk_entry_get_text(GTK_ENTRY(main_chatstr));
0931eb36 1977 tag = dc_queuecmd(NULL, NULL, L"sendchat", L"%i", curchat, L"1", L"", L"%s", buf, NULL);
d3372da9 1978 if((resp = dc_gettaggedrespsync(tag)) != NULL)
1979 {
1980 if(resp->code == 502)
1981 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1982 else if(resp->code == 504)
1983 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("This hub could not support all the types of characters in your chat message"));
1984 else if(resp->code == 513)
1985 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("This hub does not support chatting"));
1986 else if(resp->code != 200)
1987 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to chat (%i)"), resp->code);
1988 dc_freeresp(resp);
1989 }
1990 gtk_entry_set_text(GTK_ENTRY(main_chatstr), "");
1991 handleresps();
1992}
1993
1994void updatesrchfld(const char *simple)
1995{
1996 char *buf, *s;
1997 char *p, *p2;
1998 size_t bufsize, bufdata;
1999
2000 s = sstrdup(simple);
2001 buf = NULL;
2002 bufsize = bufdata = 0;
2003 p = s;
2004 do
2005 {
2006 p2 = strchr(p, ' ');
2007 if(p2 != NULL)
2008 *(p2++) = 0;
2009 if(*p)
2010 {
2011 if(bufdata > 0)
2012 bufcat(buf, " & ", 3);
2013 bufcat(buf, "N~", 2);
2014 for(; *p; p++)
2015 {
ab9f4164 2016 if(strchr("[]()$^.*?+\\|\"!", *p) != NULL)
d3372da9 2017 addtobuf(buf, '\\');
2018 addtobuf(buf, *p);
2019 }
2020 }
2021 p = p2;
2022 } while(p2 != NULL);
2023 addtobuf(buf, 0);
2024 gtk_entry_set_text(GTK_ENTRY(main_realsrch), buf);
2025 free(buf);
2026 free(s);
2027}
2028
2029void cb_main_simplesrch_changed(GtkWidget *widget, gpointer data)
2030{
2031 if(srchautoupdate)
2032 return;
2033 srchautoupdate = 1;
2034 updatesrchfld(gtk_entry_get_text(GTK_ENTRY(main_simplesrch)));
2035 srchautoupdate = 0;
2036}
2037
2038void cb_main_realsrch_changed(GtkWidget *widget, gpointer data)
2039{
2040 if(srchautoupdate)
2041 return;
2042 srchautoupdate = 1;
2043 gtk_entry_set_text(GTK_ENTRY(main_simplesrch), "");
2044 srchautoupdate = 0;
2045}
2046
2047void cb_main_srchbtn_clicked(GtkWidget *widget, gpointer data)
2048{
2049 wchar_t **toks;
2050 int tag;
2051 struct dc_response *resp;
2052 struct dc_intresp *ires;
2053
2054 if(dcfd < 0)
2055 {
2056 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
2057 return;
2058 }
2059 if(nextsrch != -1) /* Impossible case, but oh well... */
2060 return;
2061 toks = dc_lexsexpr(icsmbstowcs((char *)gtk_entry_get_text(GTK_ENTRY(main_realsrch)), "UTF-8", NULL));
2062 if(*toks == NULL)
2063 {
2064 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Please enter a search expression before searching"));
2065 return;
2066 }
0931eb36 2067 tag = dc_queuecmd(NULL, NULL, L"search", L"all", L"%a", toks, NULL);
d3372da9 2068 dc_freewcsarr(toks);
2069 if((resp = dc_gettaggedrespsync(tag)) != NULL)
2070 {
2071 if(resp->code == 501)
2072 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not find any hubs to search on"));
2073 else if(resp->code == 502)
2074 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
2075 else if(resp->code == 509)
2076 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("The server could not parse your search expression"));
2077 else if(resp->code != 200)
2078 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to search (%i)"), resp->code);
2079 if(resp->code == 200)
2080 {
2081 if((ires = dc_interpret(resp)) != NULL)
2082 {
2083 nextsrch = ires->argv[0].val.num;
2084 srcheta = time(NULL) + ires->argv[1].val.num;
2085 dc_freeires(ires);
2086 }
2087 gtk_widget_set_sensitive(main_realsrch, FALSE);
2088 gtk_widget_set_sensitive(main_simplesrch, FALSE);
2089 gtk_widget_set_sensitive(main_srchbtn, FALSE);
2090 gtk_widget_set_sensitive(main_srchcanbtn, TRUE);
2091 srchstatupdate();
2092 }
2093 dc_freeresp(resp);
2094 }
2095 handleresps();
2096}
2097
2098void cb_main_srchcanbtn_clicked(GtkWidget *widget, gpointer data)
2099{
2100 if(nextsrch == -1)
2101 return;
0931eb36 2102 dc_queuecmd(NULL, NULL, L"cansrch", L"%i", nextsrch, NULL);
d3372da9 2103 nextsrch = -1;
2104 gtk_widget_set_sensitive(main_realsrch, TRUE);
2105 gtk_widget_set_sensitive(main_simplesrch, TRUE);
2106 gtk_widget_set_sensitive(main_srchbtn, TRUE);
2107 gtk_widget_set_sensitive(main_srchcanbtn, FALSE);
2108 srchstatupdate();
2109}
2110
ab9f4164 2111gboolean cb_main_trlist_keypress(GtkWidget *widget, GdkEventKey *event, gpointer data)
d3372da9 2112{
2113 int id, tag;
2114 GtkTreeSelection *sel;
2115 GtkTreeModel *model;
2116 GtkTreeIter iter;
2117 struct dc_response *resp;
2118
2119 if((event->type == GDK_KEY_PRESS) && (event->keyval == GDK_Delete))
2120 {
2121 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
2122 if(gtk_tree_selection_get_selected(sel, &model, &iter))
2123 {
2124 gtk_tree_model_get(model, &iter, 0, &id, -1);
0931eb36 2125 tag = dc_queuecmd(NULL, NULL, L"cancel", L"%i", id, NULL);
d3372da9 2126 if((resp = dc_gettaggedrespsync(tag)) != NULL)
2127 {
2128 if(resp->code == 502)
2129 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
2130 else if(resp->code != 200)
2131 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to cancel (%i)"), resp->code);
2132 dc_freeresp(resp);
2133 }
2134 handleresps();
2135 }
ab9f4164 2136 return(TRUE);
d3372da9 2137 }
ab9f4164 2138 return(FALSE);
d3372da9 2139}
2140
2141void cb_main_srchres_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data)
2142{
2143 int tag;
2144 struct dc_response *resp;
2145 GtkTreeIter iter;
2146 GtkTreeModel *model;
2147 int size, num;
419385f3 2148 char *tfnet, *tpeerid, *tfilename, *thash, *arg;
2149 wchar_t *fnet, *peerid, *filename, *hash;
d3372da9 2150
2151 if(dcfd < 0)
2152 {
2153 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
2154 return;
2155 }
2156 model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
2157 if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
2158 return;
2159 gtk_tree_model_get(model, &iter, 7, &num, -1);
2160 if(num > 0)
2161 return;
419385f3 2162 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &tfnet, 1, &tpeerid, 3, &tfilename, 4, &size, 9, &thash, -1);
d3372da9 2163 fnet = icmbstowcs(tfnet, "UTF-8");
2164 peerid = icmbstowcs(tpeerid, "UTF-8");
2165 filename = icmbstowcs(tfilename, "UTF-8");
419385f3 2166 hash = (thash == NULL)?NULL:icmbstowcs(thash, "UTF-8");
d3372da9 2167 if((fnet == NULL) || (peerid == NULL) || (filename == NULL))
2168 {
2169 if(fnet != NULL)
2170 free(fnet);
2171 if(peerid != NULL)
2172 free(peerid);
2173 if(filename != NULL)
2174 free(filename);
419385f3 2175 if(hash != NULL)
2176 free(hash);
d3372da9 2177 g_free(tfnet);
2178 g_free(tpeerid);
2179 g_free(tfilename);
419385f3 2180 if(thash != NULL)
2181 g_free(thash);
d3372da9 2182 return;
2183 }
2184 g_free(tfnet);
2185 g_free(tpeerid);
2186 g_free(tfilename);
2187 arg = (char *)gtk_entry_get_text(GTK_ENTRY(main_dlarg));
2188 if(*arg)
0931eb36 2189 tag = dc_queuecmd(NULL, NULL, L"download", fnet, L"%ls", peerid, L"%ls", filename, L"%i", size, L"hash", L"%ls", (hash == NULL)?L"":hash, L"user", L"%s", arg, NULL);
d3372da9 2190 else
0931eb36 2191 tag = dc_queuecmd(NULL, NULL, L"download", fnet, L"%ls", peerid, L"%ls", filename, L"%i", size, L"hash", L"%ls", (hash == NULL)?L"":hash, NULL);
d3372da9 2192 free(fnet);
2193 free(peerid);
2194 free(filename);
419385f3 2195 if(hash != NULL)
2196 free(hash);
d3372da9 2197 if((resp = dc_gettaggedrespsync(tag)) != NULL)
2198 {
2199 if(resp->code == 502)
2200 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
2201 if(resp->code != 200)
2202 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to queue the download (%i)"), resp->code);
2203 dc_freeresp(resp);
2204 }
2205 handleresps();
2206}
2207
2208gboolean srchfilterfunc(GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
2209{
2210 int slots;
2211 int filteratall;
2212
2213 filteratall = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(main_filternoslots));
2214 if(!filteratall)
2215 return(TRUE);
2216 gtk_tree_model_get(model, iter, 5, &slots, -1);
2217 if(slots < 1)
2218 return(FALSE);
2219 return(TRUE);
2220}
2221
2222void cb_main_filternoslots_toggled(GtkToggleButton *widget, gpointer data)
2223{
2224 gtk_tree_model_filter_refilter(srchmodelfilter);
2225}
2226
ab9f4164 2227void cb_main_srhash_activate(GtkWidget *widget, gpointer data)
2228{
2229 GtkTreeSelection *sel;
2230 GtkTreeModel *model;
2231 GtkTreeIter iter;
2232 char *hash, *buf;
2233
2234 if(nextsrch != -1)
2235 return;
2236 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_srchres));
2237 if(gtk_tree_selection_get_selected(sel, &model, &iter))
2238 {
2239 gtk_tree_model_get(model, &iter, 9, &hash, -1);
2240 buf = sprintf2("H=%s", hash);
2241 gtk_entry_set_text(GTK_ENTRY(main_realsrch), buf);
2242 g_free(hash);
2243 free(buf);
2244 cb_main_srchbtn_clicked(widget, NULL);
2245 } else {
2246 return;
2247 }
2248}
2249
415daf6c 2250void cb_main_srcopy_activate(GtkWidget *widget, gpointer data)
2251{
2252 GtkClipboard *cb;
2253 GtkTreeSelection *sel;
2254 GtkTreeModel *model;
2255 GtkTreeIter iter;
2256 char *hash;
2257
2258 if(nextsrch != -1)
2259 return;
2260 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_srchres));
2261 if(!gtk_tree_selection_get_selected(sel, &model, &iter))
2262 return;
2263 gtk_tree_model_get(model, &iter, 9, &hash, -1);
2264 cb = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
2265 gtk_clipboard_set_text(cb, hash, -1);
2266 g_free(hash);
2267}
2268
ab9f4164 2269void cb_main_trhash_activate(GtkWidget *widget, gpointer data)
2270{
2271 GtkTreeSelection *sel;
2272 GtkTreeModel *model;
2273 GtkTreeIter iter;
2274 char *hash, *buf;
2275
2276 if(nextsrch != -1)
2277 return;
2278 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_downloads));
2279 if(gtk_tree_selection_get_selected(sel, &model, &iter))
2280 {
2281 gtk_tree_model_get(model, &iter, 12, &hash, -1);
2282 buf = sprintf2("H=%s", hash);
2283 gtk_entry_set_text(GTK_ENTRY(main_realsrch), buf);
2284 g_free(hash);
2285 free(buf);
2286 cb_main_srchbtn_clicked(widget, NULL);
2287 } else {
2288 return;
2289 }
2290}
2291
415daf6c 2292void cb_main_trcopy_activate(GtkWidget *widget, gpointer data)
2293{
2294 GtkClipboard *cb;
2295 GtkTreeSelection *sel;
2296 GtkTreeModel *model;
2297 GtkTreeIter iter;
2298 char *hash;
2299
2300 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_downloads));
2301 if(!gtk_tree_selection_get_selected(sel, &model, &iter))
2302 return;
2303 gtk_tree_model_get(model, &iter, 12, &hash, -1);
2304 cb = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
2305 gtk_clipboard_set_text(cb, hash, -1);
2306 g_free(hash);
2307}
2308
419385f3 2309void cb_main_trcancel_activate(GtkWidget *widget, gpointer data)
2310{
2311 GtkTreeSelection *sel;
2312 GtkTreeModel *model;
2313 GtkTreeIter iter;
2314 int id, tag;
2315 struct dc_response *resp;
2316
2317 if(nextsrch != -1)
2318 return;
2319 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_downloads));
2320 if(gtk_tree_selection_get_selected(sel, &model, &iter))
2321 {
2322 gtk_tree_model_get(model, &iter, 0, &id, -1);
0931eb36 2323 tag = dc_queuecmd(NULL, NULL, L"cancel", L"%i", id, NULL);
419385f3 2324 if((resp = dc_gettaggedrespsync(tag)) != NULL)
2325 {
2326 if(resp->code == 502)
2327 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
2328 else if(resp->code != 200)
2329 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to cancel (%i)"), resp->code);
2330 dc_freeresp(resp);
2331 }
2332 handleresps();
2333 } else {
2334 return;
2335 }
2336}
2337
ab9f4164 2338/* XXX: This is quite a hack, since the calling convention is
2339 * different for the popup-menu sig and the button-press-event sig. It
2340 * most certainly works, but I don't know how portable it is. */
2341gboolean cb_main_srpopup(GtkWidget *widget, GdkEventButton *event, gpointer data)
2342{
2343 GtkTreeSelection *sel;
2344 GtkTreeModel *model;
2345 GtkTreeIter iter;
2346 char *hash;
2347
2348 if((event != NULL) && (event->button != 3))
2349 return(FALSE);
2350 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
2351 if(gtk_tree_selection_get_selected(sel, &model, &iter))
2352 {
2353 gtk_tree_model_get(model, &iter, 9, &hash, -1);
4c27a5f2 2354 if((hash == NULL) || (*hash == 0))
415daf6c 2355 {
ab9f4164 2356 gtk_widget_set_sensitive(main_srhash, FALSE);
415daf6c 2357 gtk_widget_set_sensitive(main_srcopy, FALSE);
2358 } else {
4c27a5f2 2359 if(nextsrch == -1)
2360 gtk_widget_set_sensitive(main_srhash, TRUE);
2361 else
2362 gtk_widget_set_sensitive(main_srhash, FALSE);
415daf6c 2363 gtk_widget_set_sensitive(main_srcopy, TRUE);
2364 }
ab9f4164 2365 g_free(hash);
2366 } else {
2367 return(FALSE);
2368 }
2369 if(event == NULL)
2370 gtk_menu_popup(GTK_MENU(main_srpopup), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
2371 else
2372 gtk_menu_popup(GTK_MENU(main_srpopup), NULL, NULL, NULL, NULL, event->button, event->time);
2373 return(FALSE);
2374}
2375
2376/* The above hack note goes for this one too. */
2377gboolean cb_main_trpopup(GtkWidget *widget, GdkEventButton *event, gpointer data)
2378{
2379 GtkTreeSelection *sel;
2380 GtkTreeModel *model;
2381 GtkTreeIter iter;
2382 char *hash;
2383
2384 if((event != NULL) && (event->button != 3))
2385 return(FALSE);
2386 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
2387 if(gtk_tree_selection_get_selected(sel, &model, &iter))
2388 {
2389 gtk_tree_model_get(model, &iter, 12, &hash, -1);
4c27a5f2 2390 if((hash == NULL) || (*hash == 0))
415daf6c 2391 {
ab9f4164 2392 gtk_widget_set_sensitive(main_trhash, FALSE);
415daf6c 2393 gtk_widget_set_sensitive(main_trcopy, FALSE);
2394 } else {
4c27a5f2 2395 if(nextsrch == -1)
2396 gtk_widget_set_sensitive(main_trhash, TRUE);
2397 else
2398 gtk_widget_set_sensitive(main_trhash, FALSE);
415daf6c 2399 gtk_widget_set_sensitive(main_trcopy, TRUE);
2400 }
ab9f4164 2401 g_free(hash);
2402 } else {
2403 return(FALSE);
2404 }
2405 if(event == NULL)
2406 gtk_menu_popup(GTK_MENU(main_trpopup), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
2407 else
2408 gtk_menu_popup(GTK_MENU(main_trpopup), NULL, NULL, NULL, NULL, event->button, event->time);
2409 return(FALSE);
2410}
2411
4c27a5f2 2412void cb_reslist_reload_clicked(GtkWidget *widget, gpointer data)
2413{
2414 if(lsrestag != -1)
2415 return;
2416 gtk_widget_set_sensitive(reslist_delete, FALSE);
2417 gtk_widget_set_sensitive(reslist_search, FALSE);
2418 gtk_list_store_clear(reslist);
2419 lsrestag = dc_queuecmd(NULL, NULL, L"filtercmd", L"lsres", NULL);
2420 gtk_widget_set_sensitive(reslist_reload, FALSE);
2421}
2422
2423int rmres(char *id)
2424{
2425 int tag, ret;
2426 struct dc_response *resp;
2427
2428 ret = -1;
0931eb36 2429 tag = dc_queuecmd(NULL, NULL, L"filtercmd", L"rmres", L"%s", id, NULL);
4c27a5f2 2430 if((resp = dc_gettaggedrespsync(tag)) != NULL)
2431 {
2432 if(resp->numlines > 0)
2433 {
2434 if(!wcscmp(resp->rlines[0].argv[1], L"ok"))
2435 ret = 0;
2436 else if(!wcsncmp(resp->rlines[0].argv[1], L"err:", 4))
2437 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred (%ls)"), resp->rlines[0].argv[1] + 4);
2438 }
2439 dc_freeresp(resp);
2440 }
2441 handleresps();
2442 return(ret);
2443}
2444
2445void cb_reslist_delete_clicked(GtkWidget *widget, gpointer data)
2446{
2447 GtkTreeIter iter;
2448 GtkTreeModel *model;
2449 char *id;
2450 gboolean locked;
2451
2452 if(nextsrch != -1)
2453 return;
2454 if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(reslist_list)), &model, &iter))
2455 return;
2456 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &id, 4, &locked, -1);
2457 if(locked)
2458 {
2459 g_free(id);
2460 return;
2461 }
2462 if(!rmres(id))
2463 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
2464 g_free(id);
2465}
2466
2467void cb_reslist_search_clicked(GtkWidget *widget, gpointer data)
2468{
2469 GtkTreeIter iter;
2470 GtkTreeModel *model;
2471 char *hash, *buf;
2472
2473 if(nextsrch != -1)
2474 return;
2475 if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(reslist_list)), &model, &iter))
2476 return;
2477 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 5, &hash, -1);
2478 buf = sprintf2("H=%s", hash);
2479 gtk_entry_set_text(GTK_ENTRY(main_realsrch), buf);
2480 free(buf);
2481 g_free(hash);
2482 cb_main_srchbtn_clicked(widget, NULL);
2483}
2484
2485void cb_reslist_list_cchange(GtkWidget *widget, gpointer data)
2486{
2487 GtkTreeIter iter;
2488 GtkTreeModel *model;
2489 gboolean locked;
2490 char *hash;
2491
2492 if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(reslist_list)), &model, &iter))
2493 {
2494 gtk_widget_set_sensitive(reslist_delete, FALSE);
2495 gtk_widget_set_sensitive(reslist_search, FALSE);
2496 return;
2497 }
2498 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 4, &locked, 5, &hash, -1);
2499 gtk_widget_set_sensitive(reslist_delete, !locked);
2500 gtk_widget_set_sensitive(reslist_search, hash && *hash);
2501 g_free(hash);
2502}
2503
2504void cb_reslist_list_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data)
2505{
2506 GtkTreeIter iter;
2507 GtkTreeModel *model;
2508 char *hash, *buf;
2509
2510 model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
2511 if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
2512 return;
2513 if(nextsrch != -1)
2514 return;
2515 gtk_tree_model_get(model, &iter, 5, &hash, -1);
2516 buf = sprintf2("H=%s", hash);
2517 gtk_entry_set_text(GTK_ENTRY(main_realsrch), buf);
2518 free(buf);
2519 g_free(hash);
2520 cb_main_srchbtn_clicked(widget, NULL);
2521}
2522
2523gboolean cb_reslist_list_keypress(GtkWidget *widget, GdkEventKey *event, gpointer data)
2524{
2525 GtkTreeSelection *sel;
2526 GtkTreeModel *model;
2527 GtkTreeIter iter;
2528 char *id;
2529 gboolean locked;
2530
2531 if((event->type == GDK_KEY_PRESS) && (event->keyval == GDK_Delete))
2532 {
2533 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
2534 if(gtk_tree_selection_get_selected(sel, &model, &iter))
2535 {
2536 gtk_tree_model_get(model, &iter, 0, &id, 4, &locked, -1);
2537 if(!locked)
2538 {
2539 if(!rmres(id))
2540 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
2541 }
2542 g_free(id);
2543 }
2544 return(TRUE);
2545 }
2546 return(FALSE);
2547}
2548
d3372da9 2549void srchstatupdate(void)
2550{
2551 char buf[1024];
2552
2553 if(nextsrch == -1)
2554 {
2555 snprintf(buf, 1024, _("Ready to search"));
2556 } else {
2557 snprintf(buf, 1024, _("Search scheduled and will be submitted in %i seconds"), (int)(srcheta - time(NULL)));
2558 }
2559 if(strcmp(gtk_label_get_text(GTK_LABEL(main_srchstatus)), buf))
2560 gtk_label_set_text(GTK_LABEL(main_srchstatus), buf);
2561}
2562
2563gint srchstatupdatecb(gpointer data)
2564{
2565 srchstatupdate();
2566 return(TRUE);
2567}
2568
2569void initchattags(void)
2570{
2571 GtkTextTag *tag;
2572
2573 chattags = gtk_text_tag_table_new();
2574 tag = gtk_text_tag_new("sender");
2575 g_object_set(tag, "foreground", "blue", NULL);
2576 gtk_text_tag_table_add(chattags, tag);
2577}
2578
2579int main(int argc, char **argv)
2580{
2581 GtkWidget *wnd;
2582 PangoFontDescription *monospacefont;
2583 GtkTreeModel *sortmodel;
2584 struct passwd *pwent;
2585
2586 setlocale(LC_ALL, "");
2587 bindtextdomain(PACKAGE, LOCALEDIR);
2588 textdomain(PACKAGE);
2589 gtk_init(&argc, &argv);
2590 dc_init();
2591 signal(SIGCHLD, SIG_IGN);
9ebbbe20 2592 pubhubaddr = sstrdup("http://www.hublist.org/PublicHubList.xml.bz2");
65bf229b 2593 dcserver = sstrdup("");
d3372da9 2594 if((pwent = getpwuid(getuid())) == NULL)
2595 {
2596 fprintf(stderr, "could not get your passwd data");
2597 exit(1);
2598 }
2599 connectas = sstrdup(pwent->pw_name);
2600 wnd = create_main_wnd();
4c27a5f2 2601 create_reslist_wnd();
2602 gtk_window_resize(GTK_WINDOW(reslist_wnd), 600, 400);
d3372da9 2603 initchattags();
2604
2605 fnmodel = gtk_list_store_new(6, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
2606 gtk_tree_view_set_model(GTK_TREE_VIEW(main_fnetnodes), GTK_TREE_MODEL(fnmodel));
2607 gtk_tree_view_set_model(GTK_TREE_VIEW(main_chatnodes), GTK_TREE_MODEL(fnmodel));
4c27a5f2 2608
2609 reslist = gtk_list_store_new(6, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING);
2610 gtk_tree_view_set_model(GTK_TREE_VIEW(reslist_list), GTK_TREE_MODEL(reslist));
2611
ab9f4164 2612 dlmodel = gtk_list_store_new(13, G_TYPE_INT, /* id */
d3372da9 2613 G_TYPE_INT, /* dir */
2614 G_TYPE_INT, /* state */
2615 G_TYPE_STRING, /* peerid */
2616 G_TYPE_STRING, /* peernick */
2617 G_TYPE_STRING, /* path */
2618 G_TYPE_INT, /* size */
2619 G_TYPE_INT, /* curpos */
2620 G_TYPE_STRING, /* stock */
2621 G_TYPE_FLOAT, /* percentage */
2622 G_TYPE_INT, /* error */
ab9f4164 2623 G_TYPE_INT, /* errortime */
2624 G_TYPE_STRING); /* hash */
d3372da9 2625 gtk_tree_view_set_model(GTK_TREE_VIEW(main_downloads), GTK_TREE_MODEL(dlmodel));
2626
ab9f4164 2627 ulmodel = gtk_list_store_new(13, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING);
d3372da9 2628 gtk_tree_view_set_model(GTK_TREE_VIEW(main_uploads), GTK_TREE_MODEL(ulmodel));
2629
ab9f4164 2630 srchmodel = gtk_tree_store_new(10, G_TYPE_STRING, /* fnetname */
2631 G_TYPE_STRING, /* peerid */
2632 G_TYPE_STRING, /* peername */
2633 G_TYPE_STRING, /* filename */
2634 G_TYPE_INT, /* size */
2635 G_TYPE_INT, /* slots */
2636 G_TYPE_DOUBLE, /* resptime */
2637 G_TYPE_INT, /* sizenum */
2638 G_TYPE_INT, /* speed */
2639 G_TYPE_STRING); /* hash */
d3372da9 2640 srchmodelfilter = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new(GTK_TREE_MODEL(srchmodel), NULL));
2641 gtk_tree_model_filter_set_visible_func(srchmodelfilter, srchfilterfunc, NULL, NULL);
2642 sortmodel = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(srchmodelfilter));
2643 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortmodel), 4, GTK_SORT_DESCENDING);
2644 gtk_tree_view_set_model(GTK_TREE_VIEW(main_srchres), GTK_TREE_MODEL(sortmodel));
2645 g_object_unref(sortmodel);
2646
2647 monospacefont = pango_font_description_from_string("Monospace 10");
2648 gtk_widget_modify_font(main_chatview, monospacefont);
2649 pango_font_description_free(monospacefont);
2650 readconfigfile();
2651 updatesbar(_("Disconnected"));
2652 gtk_widget_show(wnd);
2653 if(autoconn)
2654 dcconnect(dcserver);
2655 g_timeout_add(500, srchstatupdatecb, NULL);
2656 g_timeout_add(5000, ksupdatecb, NULL);
8d9ec191 2657 g_timeout_add(1000, updatetransfers, NULL);
d3372da9 2658 gtk_main();
2659 return(0);
2660}