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