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