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