Bugfixed the socket debugging.
[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 #include <stdint.h>
45
46 #ifdef HAVE_CONFIG_H
47 #include <config.h>
48 #endif
49 #include "dolcon.h"
50 #include "hublist.h"
51
52 #define TRHISTSIZE 10
53
54 struct trdata
55 {
56     dc_lnum_t poshist[TRHISTSIZE];
57     double timehist[TRHISTSIZE];
58     int hc;
59 };
60
61 struct fndata
62 {
63     GtkTextBuffer *textbuf;
64 };
65
66 struct srchsize
67 {
68     gint64 size;
69     int num;
70     int slots;
71     double resptime;
72     GtkTreeRowReference *ref;
73 };
74
75 struct knownspeed
76 {
77     char *userid;
78     int speed, seq;
79     time_t fetched;
80 };
81
82 GtkWidget *inpdialog;
83 GtkListStore *fnmodel, *ulmodel, *dlmodel, *reslist;
84 GtkTreeStore *srchmodel;
85 GtkTreeModelFilter *srchmodelfilter;
86 GtkTextTagTable *chattags;
87 int dcfd = -1, gdkread = -1, gdkwrite = -1;
88 int curchat = -1;
89 char *pubhubaddr = NULL;
90 char *connectas = NULL;
91 char *dcserver = NULL;
92 int autoconn = 1;
93 int srchautoupdate = 0;
94 int cursrch = -1, nextsrch = -1;
95 time_t srcheta;
96 struct srchsize *srchsizes = NULL;
97 struct knownspeed *knownspeeds = NULL;
98 int numsizes = 0, numspeeds = 0, ksqueryseq = -1, ksquerytag = -1, lsrestag = -1;
99
100 void dcfdcallback(gpointer data, gint source, GdkInputCondition condition);
101 void srchstatupdate(void);
102
103 #include "mainwnd.gtkh"
104 #include "inpdialog.gtkh"
105 #include "pref.gtkh"
106 #include "reslist.gtkh"
107
108 void updatewrite(void)
109 {
110     if(dcfd < 0)
111         return;
112     if(dc_wantwrite())
113     {
114         if(gdkwrite == -1)
115             gdkwrite = gdk_input_add(dcfd, GDK_INPUT_WRITE, dcfdcallback, NULL);
116     } else {
117         if(gdkwrite != -1)
118         {
119             gdk_input_remove(gdkwrite);
120             gdkwrite = -1;
121         }
122     }
123 }
124
125 double ntime(void)
126 {
127     struct timeval tv;
128     
129     gettimeofday(&tv, NULL);
130     return((double)tv.tv_sec + ((double)tv.tv_usec / 1000000.0));
131 }
132
133 void fndestroycb(struct dc_fnetnode *fn)
134 {
135     struct fndata *data;
136     GtkTextBuffer *textbuf;
137     
138     data = fn->udata;
139     g_object_unref(data->textbuf);
140     free(data);
141     if(curchat == fn->id)
142     {
143         textbuf = gtk_text_buffer_new(chattags);
144         gtk_text_view_set_buffer(GTK_TEXT_VIEW(main_chatview), textbuf);
145         g_object_unref(textbuf);
146     }
147 }
148
149 void addfndata(struct dc_fnetnode *fn)
150 {
151     struct fndata *data;
152     
153     if(fn->udata != NULL)
154         return;
155     fn->destroycb = fndestroycb;
156     data = smalloc(sizeof(*data));
157     data->textbuf = gtk_text_buffer_new(chattags);
158     fn->udata = data;
159 }
160
161 void trdestroycb(struct dc_transfer *tr)
162 {
163     free(tr->udata);
164 }
165
166 void addtrdata(struct dc_transfer *tr)
167 {
168     struct trdata *data;
169     
170     if(tr->udata != NULL)
171         return;
172     tr->destroycb = trdestroycb;
173     data = smalloc(sizeof(*data));
174     memset(data, 0, sizeof(*data));
175     tr->udata = data;
176 }
177
178 void updatetrdata(struct dc_transfer *tr)
179 {
180     int i;
181     struct trdata *data;
182     
183     data = tr->udata;
184     if(data->hc < TRHISTSIZE)
185     {
186         data->poshist[data->hc] = tr->curpos;
187         data->timehist[data->hc] = ntime();
188         data->hc++;
189     } else {
190         for(i = 0; i < TRHISTSIZE - 1; i++)
191         {
192             data->poshist[i] = data->poshist[i + 1];
193             data->timehist[i] = data->timehist[i + 1];
194         }
195         data->poshist[i] = tr->curpos;
196         data->timehist[i] = ntime();
197     }
198 }
199
200 char *getfnstatestock(int state)
201 {
202     if(state == DC_FNN_STATE_SYN)
203         return("gtk-jump-to");
204     if(state == DC_FNN_STATE_HS)
205         return("gtk-execute");
206     if(state == DC_FNN_STATE_EST)
207         return("gtk-yes");
208     if(state == DC_FNN_STATE_DEAD)
209         return("gtk-cancel");
210     return(NULL);
211 }
212
213 void updatehublist(void)
214 {
215     int done;
216     struct dc_fnetnode *fn;
217     GtkTreeIter iter;
218     int id;
219     char *buf;
220     char *name;
221     int state, numusers;
222     
223     for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
224         fn->found = 0;
225     done = 0;
226     while(!done)
227     {
228         done = 1;
229         if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fnmodel), &iter))
230         {
231             do
232             {
233                 gtk_tree_model_get(GTK_TREE_MODEL(fnmodel), &iter, 0, &id, -1);
234                 if((fn = dc_findfnetnode(id)) == NULL)
235                 {
236                     /* I can't seem to get a sensible reply fromp
237                      * gtk_list_store, so I'm just doing this
238                      * instead. */
239                     gtk_list_store_remove(fnmodel, &iter);
240                     done = 0;
241                     break;
242                 } else {
243                     gtk_tree_model_get(GTK_TREE_MODEL(fnmodel), &iter, 1, &name, 2, &state, 3, &numusers, -1);
244                     if(fn->name == NULL)
245                         buf = _("Unknown");
246                     else
247                         buf = icswcstombs(fn->name, "UTF-8", NULL);
248                     if(strcmp(buf, name))
249                         gtk_list_store_set(fnmodel, &iter, 1, buf, -1);
250                     if(state != fn->state)
251                     {
252                         gtk_list_store_set(fnmodel, &iter, 2, fn->state, -1);
253                         gtk_list_store_set(fnmodel, &iter, 4, getfnstatestock(fn->state), -1);
254                     }
255                     if(numusers != fn->numusers)
256                         gtk_list_store_set(fnmodel, &iter, 3, fn->numusers, -1);
257                     g_free(name);
258                     fn->found = 1;
259                 }
260             } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(fnmodel), &iter));
261         }
262     }
263     for(fn = dc_fnetnodes; fn != NULL; fn = fn->next)
264     {
265         if(!fn->found)
266         {
267             if(fn->name == NULL)
268                 buf = _("Unknown");
269             else
270                 buf = icswcstombs(fn->name, "UTF-8", NULL);
271             gtk_list_store_append(fnmodel, &iter);
272             gtk_list_store_set(fnmodel, &iter, 0, fn->id, 1, buf, 2, fn->state, 3, fn->numusers, 4, getfnstatestock(fn->state), -1);
273             addfndata(fn);
274         }
275     }
276 }
277
278 char *bytes2si(long long bytes)
279 {
280     int i;
281     double b;
282     static char ret[64];
283     static char pfx[] = {'k', 'M', 'G', 'T'};
284     
285     b = bytes;
286     for(i = 0; (b >= 1024) && (i < sizeof(pfx)); i++)
287         b /= 1024;
288     if(i == 0)
289         snprintf(ret, 64, "%.1f B", b);
290     else
291         snprintf(ret, 64, "%.1f %ciB", b, pfx[i - 1]);
292     return(ret);
293 }
294
295 void progressfunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
296 {
297     int totalc, curc;
298     gint64 total, cur;
299     char buf[64];
300     
301     totalc = (GPOINTER_TO_INT(data) & 0xff00) >> 8;
302     curc = GPOINTER_TO_INT(data) & 0xff;
303     gtk_tree_model_get(model, iter, totalc, &total, curc, &cur, -1);
304     if(total < 1)
305         g_object_set(rend, "value", GINT_TO_POINTER(0), NULL);
306     else
307         g_object_set(rend, "value", GINT_TO_POINTER((int)(((double)cur / (double)total) * 100)), NULL);
308     if(cur < 0) {
309         g_object_set(rend, "text", "", NULL);
310     } else {
311         snprintf(buf, 64, "%'ji", (intmax_t)cur);
312         g_object_set(rend, "text", buf, NULL);
313     }
314 }
315
316 void percentagefunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
317 {
318     int colnum;
319     float val;
320     char buf[64];
321     
322     colnum = GPOINTER_TO_INT(data);
323     gtk_tree_model_get(model, iter, colnum, &val, -1);
324     snprintf(buf, 64, "%.2f%%", (double)(val * 100.0));
325     g_object_set(rend, "text", buf, NULL);
326 }
327
328 void transnicebytefunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
329 {
330     int colnum;
331     gint64 val;
332     char buf[64];
333     
334     colnum = GPOINTER_TO_INT(data);
335     gtk_tree_model_get(model, iter, colnum, &val, -1);
336     if(val >= 0)
337         snprintf(buf, 64, "%'ji", (intmax_t)val);
338     else
339         strcpy(buf, _("Unknown"));
340     g_object_set(rend, "text", buf, NULL);
341 }
342
343 void transnicebytefunc2(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
344 {
345     int colnum;
346     gint64 val;
347     char buf[64];
348     
349     colnum = GPOINTER_TO_INT(data);
350     gtk_tree_model_get(model, iter, colnum, &val, -1);
351     if(val >= 0)
352         strcpy(buf, bytes2si(val));
353     else
354         strcpy(buf, _("Unknown"));
355     g_object_set(rend, "text", buf, NULL);
356 }
357
358 void hidezerofunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
359 {
360     int colnum, val;
361     char buf[64];
362     
363     colnum = GPOINTER_TO_INT(data);
364     gtk_tree_model_get(model, iter, colnum, &val, -1);
365     if(val > 0)
366         snprintf(buf, 64, "%i", val);
367     else
368         strcpy(buf, "");
369     g_object_set(rend, "text", buf, NULL);
370 }
371
372 void speedtimefunc(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
373 {
374     int speed, time;
375     gint64 size;
376     char buf[64];
377     
378     gtk_tree_model_get(model, iter, 4, &size, 8, &speed, -1);
379     if(speed > 0)
380     {
381         time = (size / speed) / 60;
382         if(time < 1)
383             snprintf(buf, 64, "%'i (<00:01)", speed);
384         else
385             snprintf(buf, 64, "%'i (%02i:%02i)", speed, time / 60, time % 60);
386     } else if(speed == 0) {
387         strcpy(buf, "0");
388     } else {
389         strcpy(buf, _("Unknown"));
390     }
391     g_object_set(rend, "text", buf, NULL);
392 }
393
394 void transspeedinfo(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
395 {
396     int id;
397     struct dc_transfer *tr;
398     struct trdata *d;
399     char buf[64];
400     int speed;
401     
402     gtk_tree_model_get(model, iter, 0, &id, -1);
403     if((tr = dc_findtransfer(id)) != NULL)
404     {
405         d = tr->udata;
406         if((tr->state != DC_TRNS_MAIN) || (d == NULL))
407         {
408             buf[0] = 0;
409         } else if(d->hc < 2) {
410             strcpy(buf, "...");
411         } else {
412             speed = (((double)(d->poshist[d->hc - 1] - d->poshist[0])) / (d->timehist[d->hc - 1] - d->timehist[0]));
413             snprintf(buf, 64, "%s/s", bytes2si(speed));
414         }
415     } else {
416         buf[0] = 0;
417     }
418     g_object_set(rend, "text", buf, NULL);
419 }
420
421 void transerrorinfo(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
422 {
423     int error;
424     time_t errortime;
425     char finbuf[64], tbuf[64], *errstr;
426     
427     gtk_tree_model_get(model, iter, 10, &error, 11, &errortime, -1);
428     if(error != DC_TRNSE_NOERROR)
429     {
430         if(error == DC_TRNSE_NOTFOUND)
431             errstr = _("Not found");
432         else if(error == DC_TRNSE_NOSLOTS)
433             errstr = _("No slots");
434         strftime(tbuf, 64, _("%H:%M:%S"), localtime(&errortime));
435         snprintf(finbuf, 64, _("%s (reported at %s)"), errstr, tbuf);
436     } else {
437         *finbuf = 0;
438     }
439     g_object_set(rend, "text", finbuf, NULL);
440 }
441
442 char *gettrstatestock(int state)
443 {
444     if(state == DC_TRNS_WAITING)
445         return("gtk-jump-to");
446     if(state == DC_TRNS_HS)
447         return("gtk-execute");
448     if(state == DC_TRNS_MAIN)
449         return("gtk-network");
450     if(state == DC_TRNS_DONE)
451         return("gtk-yes");
452     return(NULL);
453 }
454
455 gint updatetransfers(gpointer data)
456 {
457     struct dc_transfer *tr;
458     struct trdata *d;
459     double now;
460     
461     now = ntime();
462     for(tr = dc_transfers; tr != NULL; tr = tr->next)
463     {
464         if((d = tr->udata) != NULL)
465         {
466             if((d->hc > 0) && ((now - d->timehist[d->hc - 1]) > 2))
467                 updatetrdata(tr);
468         }
469     }
470     return(TRUE);
471 }
472
473 void updatetransferlists(void)
474 {
475     int i;
476     int done;
477     struct dc_transfer *transfer;
478     GtkTreeIter iter;
479     int id;
480     char *buf;
481     char *peerid, *peernick, *path, *hash;
482     int state, dir, error;
483     gint64 size, curpos;
484     time_t errortime;
485     GtkListStore *stores[3];
486     
487     for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
488         transfer->found = 0;
489     stores[DC_TRNSD_UNKNOWN] = NULL;
490     stores[DC_TRNSD_UP] = ulmodel;
491     stores[DC_TRNSD_DOWN] = dlmodel;
492     for(i = 0; i < 3; i++)
493     {
494         if(stores[i] == NULL)
495             continue;
496         done = 0;
497         while(!done)
498         {
499             done = 1;
500             if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(stores[i]), &iter))
501             {
502                 do
503                 {
504                     gtk_tree_model_get(GTK_TREE_MODEL(stores[i]), &iter, 0, &id, 1, &dir, -1);
505                     if(((transfer = dc_findtransfer(id)) == NULL) || (transfer->dir != dir))
506                     {
507                         gtk_list_store_remove(stores[i], &iter);
508                         done = 0;
509                         break;
510                     } else {
511                         transfer->found = 1;
512                         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);
513                         if(state != transfer->state)
514                             gtk_list_store_set(stores[i], &iter, 2, transfer->state, 8, gettrstatestock(transfer->state), -1);
515                         if(size != transfer->size)
516                             gtk_list_store_set(stores[i], &iter, 6, (gint64)transfer->size, -1);
517                         if(curpos != transfer->curpos)
518                         {
519                             gtk_list_store_set(stores[i], &iter, 7, (gint64)transfer->curpos, -1);
520                             if(transfer->udata != NULL)
521                                 updatetrdata(transfer);
522                         }
523                         if(error != transfer->error)
524                             gtk_list_store_set(stores[i], &iter, 10, transfer->error, -1);
525                         if(errortime != transfer->errortime)
526                             gtk_list_store_set(stores[i], &iter, 11, transfer->errortime, -1);
527                         if((transfer->size > 0) && (transfer->curpos > 0))
528                             gtk_list_store_set(stores[i], &iter, 9, (float)transfer->curpos / (float)transfer->size, -1);
529                         buf = icswcstombs(transfer->peerid, "UTF-8", NULL);
530                         if(strcmp(buf, peerid))
531                             gtk_list_store_set(stores[i], &iter, 3, buf, -1);
532                         buf = icswcstombs(((transfer->peernick == NULL) || (transfer->peernick[0] == L'\0'))?transfer->peerid:transfer->peernick, "UTF-8", NULL);
533                         if(strcmp(buf, peernick))
534                             gtk_list_store_set(stores[i], &iter, 4, buf, -1);
535                         buf = (transfer->path == NULL)?_("Unknown"):icswcstombs(transfer->path, "UTF-8", NULL);
536                         if(strcmp(buf, path))
537                             gtk_list_store_set(stores[i], &iter, 5, buf, -1);
538                         buf = (transfer->hash == NULL)?"":icswcstombs(transfer->hash, "UTF-8", NULL);
539                         if(strcmp(buf, path))
540                             gtk_list_store_set(stores[i], &iter, 12, buf, -1);
541                         g_free(hash);
542                         g_free(peerid);
543                         g_free(peernick);
544                         g_free(path);
545                     }
546                 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(stores[i]), &iter));
547             }
548         }
549     }
550     for(transfer = dc_transfers; transfer != NULL; transfer = transfer->next)
551     {
552         if(!transfer->found)
553         {
554             if(stores[transfer->dir] != NULL)
555             {
556                 peerid = icwcstombs(transfer->peerid, "UTF-8");
557                 peernick = icwcstombs(((transfer->peernick == NULL) || (transfer->peernick[0] == L'\0'))?transfer->peerid:transfer->peernick, "UTF-8");
558                 path = (transfer->path == NULL)?_("Unknown"):icwcstombs(transfer->path, "UTF-8");
559                 hash = (transfer->hash == NULL)?"":icwcstombs(transfer->hash, "UTF-8");
560                 gtk_list_store_append(stores[transfer->dir], &iter);
561                 gtk_list_store_set(stores[transfer->dir], &iter,
562                                    0, transfer->id,
563                                    1, transfer->dir,
564                                    2, transfer->state,
565                                    3, peerid,
566                                    4, peernick,
567                                    5, path,
568                                    6, (gint64)transfer->size,
569                                    7, (gint64)transfer->curpos,
570                                    8, gettrstatestock(transfer->state),
571                                    9, 0.0,
572                                    10, transfer->error,
573                                    11, transfer->errortime,
574                                    12, hash,
575                                    -1);
576                 free(peerid);
577                 free(peernick);
578                 if(transfer->path != NULL)
579                     free(path);
580                 if(transfer->hash != NULL)
581                     free(hash);
582                 addtrdata(transfer);
583             }
584         }
585     }
586 }
587
588 void updatesbar(char *msg)
589 {
590     gtk_statusbar_pop(GTK_STATUSBAR(main_statusbar), 0);
591     gtk_statusbar_push(GTK_STATUSBAR(main_statusbar), 0, msg);
592 }
593
594 void freesrchsizes(void)
595 {
596     int i;
597     
598     for(i = 0; i < numsizes; i++)
599     {
600         if(srchsizes[i].ref != NULL)
601             gtk_tree_row_reference_free(srchsizes[i].ref);
602     }
603     if(srchsizes != NULL)
604         free(srchsizes);
605     srchsizes = NULL;
606     numsizes = 0;
607 }
608
609 void dcdisconnected(void)
610 {
611     if(gdkread != -1)
612     {
613         gdk_input_remove(gdkread);
614         gdkread = -1;
615     }
616     dcfd = -1;
617     updatehublist();
618     updatetransferlists();
619     cursrch = nextsrch = -1;
620     gtk_tree_store_clear(srchmodel);
621     freesrchsizes();
622     gtk_widget_set_sensitive(main_connmenu, TRUE);
623     gtk_widget_set_sensitive(main_dconnmenu, FALSE);
624     gtk_widget_set_sensitive(main_simplesrch, TRUE);
625     gtk_widget_set_sensitive(main_realsrch, TRUE);
626     gtk_widget_set_sensitive(main_srchbtn, TRUE);
627     gtk_widget_set_sensitive(main_srchcanbtn, FALSE);
628     updatesbar(_("Disconnected"));
629 }
630
631 char *inputbox(char *title, char *prompt, char *def, int echo)
632 {
633     int resp;
634     GtkWidget *swnd;
635     char *buf;
636     
637     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);
638     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(inpdialog)->vbox), swnd = create_inpdialog_wnd(), TRUE, TRUE, 0);
639     gtk_widget_show(swnd);
640     if(!echo)
641         gtk_entry_set_visibility(GTK_ENTRY(inpdialog_entry), FALSE);
642     gtk_label_set_text(GTK_LABEL(inpdialog_prompt), prompt);
643     gtk_entry_set_text(GTK_ENTRY(inpdialog_entry), def);
644     resp = gtk_dialog_run(GTK_DIALOG(inpdialog));
645     if(!echo)
646         gtk_entry_set_visibility(GTK_ENTRY(inpdialog_entry), TRUE);
647     if(resp == GTK_RESPONSE_ACCEPT)
648         buf = strdup(gtk_entry_get_text(GTK_ENTRY(inpdialog_entry)));
649     else
650         buf = NULL;
651     gtk_widget_destroy(inpdialog);
652     updatewrite();
653     return(buf);
654 }
655
656 int msgbox(int type, int buttons, char *format, ...)
657 {
658     GtkWidget *swnd;
659     va_list args;
660     char *buf;
661     int resp;
662     
663     va_start(args, format);
664     buf = vsprintf2(format, args);
665     va_end(args);
666     swnd = gtk_message_dialog_new(GTK_WINDOW(main_wnd), GTK_DIALOG_MODAL, type, buttons, "%s", buf);
667     resp = gtk_dialog_run(GTK_DIALOG(swnd));
668     gtk_widget_destroy(swnd);
669     free(buf);
670     return(resp);
671 }
672
673 void readconfigfile(void)
674 {
675     FILE *cfgfile;
676     char *homedir, *buf, *p;
677     int w, h;
678     
679     if((homedir = getenv("HOME")) == NULL)
680     {
681         fprintf(stderr, "warning: could not find home directory!\n");
682         return;
683     }
684     buf = sprintf2("%s/.dolconrc", homedir);
685     if((cfgfile = fopen(buf, "r")) == NULL)
686     {
687         if(errno != ENOENT)
688             perror(buf);
689         free(buf);
690         return;
691     }
692     free(buf);
693     buf = smalloc(1024);
694     while(fgets(buf, 1024, cfgfile) != NULL)
695     {
696         if(strlen(buf) < 1)
697             continue;
698         p = buf + strlen(buf);
699         if(p[-1] == '\n')
700             *(--p) = 0;
701         if((p = strchr(buf, ':')) == NULL)
702             continue;
703         *(p++) = 0;
704         while((*p == ' ') || (*p == '\t'))
705             p++;
706         if(!strcmp(buf, "wnd-width"))
707         {
708             w = atoi(p);
709         } else if(!strcmp(buf, "wnd-height")) {
710             h = atoi(p);
711         } else if(!strcmp(buf, "pane1-pos")) {
712             gtk_paned_set_position(GTK_PANED(main_pane1), atoi(p));
713         } else if(!strcmp(buf, "pane2-pos")) {
714             gtk_paned_set_position(GTK_PANED(main_pane2), atoi(p));
715         } else if(!strcmp(buf, "pane3-pos")) {
716             gtk_paned_set_position(GTK_PANED(main_pane3), atoi(p));
717         } else if(!strcmp(buf, "pubhubaddr")) {
718             free(pubhubaddr);
719             pubhubaddr = sstrdup(p);
720         } else if(!strcmp(buf, "dcserver")) {
721             free(dcserver);
722             dcserver = sstrdup(p);
723         } else if(!strcmp(buf, "advexpanded")) {
724             gtk_expander_set_expanded(GTK_EXPANDER(main_advexp), atoi(p));
725         } else if(!strcmp(buf, "connectas")) {
726             free(connectas);
727             connectas = sstrdup(p);
728         } else if(!strcmp(buf, "autoconn")) {
729             autoconn = atoi(p);
730         } else if(!strcmp(buf, "filternoslots")) {
731             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(main_filternoslots), atoi(p));
732         }
733     }
734     free(buf);
735     fclose(cfgfile);
736 /*
737     if(w != 1589)
738         abort();
739 */
740     gtk_window_resize(GTK_WINDOW(main_wnd), w, h);
741 }
742
743 void updateconfigfile(void)
744 {
745     FILE *cfgfile;
746     char *homedir, *buf;
747     int w, h;
748     
749     if((homedir = getenv("HOME")) == NULL)
750     {
751         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
752         return;
753     }
754     buf = sprintf2("%s/.dolconrc", homedir);
755     if((cfgfile = fopen(buf, "w")) == NULL)
756     {
757         free(buf);
758         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not open configuration file for writing: %s"), strerror(errno));
759         return;
760     }
761     free(buf);
762     gtk_window_get_size(GTK_WINDOW(main_wnd), &w, &h);
763     fprintf(cfgfile, "wnd-width: %i\n", w);
764     fprintf(cfgfile, "wnd-height: %i\n", h);
765     fprintf(cfgfile, "pane1-pos: %i\n", gtk_paned_get_position(GTK_PANED(main_pane1)));
766     fprintf(cfgfile, "pane2-pos: %i\n", gtk_paned_get_position(GTK_PANED(main_pane2)));
767     fprintf(cfgfile, "pane3-pos: %i\n", gtk_paned_get_position(GTK_PANED(main_pane3)));
768     fprintf(cfgfile, "pubhubaddr: %s\n", pubhubaddr);
769     fprintf(cfgfile, "dcserver: %s\n", dcserver);
770     fprintf(cfgfile, "advexpanded: %i\n", gtk_expander_get_expanded(GTK_EXPANDER(main_advexp)));
771     fprintf(cfgfile, "connectas: %s\n", connectas);
772     fprintf(cfgfile, "autoconn: %i\n", autoconn);
773     fprintf(cfgfile, "filternoslots: %i\n", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(main_filternoslots)));
774     fclose(cfgfile);
775 }
776
777 gboolean initdeath(GtkWidget *widget, gpointer data)
778 {
779     updateconfigfile();
780     gtk_main_quit();
781     return(TRUE);
782 }
783
784 void cb_inpdialog_entry_activate(GtkWidget *widget, gpointer data)
785 {
786     gtk_dialog_response(GTK_DIALOG(inpdialog), GTK_RESPONSE_ACCEPT);
787 }
788
789 int loginconv(int type, wchar_t *prompt, char **resp, void *data)
790 {
791     int ret;
792     char *buf;
793     
794     ret = 0;
795     buf = icwcstombs(prompt, "UTF-8");
796     switch(type)
797     {
798     case DC_LOGIN_CONV_NOECHO:
799         if((*resp = inputbox(_("Login"), buf, "", 0)) == NULL)
800             ret = 1;
801         break;
802     case DC_LOGIN_CONV_ECHO:
803         if((*resp = inputbox(_("Login"), buf, "", 1)) == NULL)
804             ret = 1;
805         break;
806     case DC_LOGIN_CONV_INFO:
807         msgbox(GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "%s", buf);
808         break;
809     case DC_LOGIN_CONV_ERROR:
810         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", buf);
811         break;
812     }
813     free(buf);
814     updatewrite();
815     return(ret);
816 }
817
818 void getfnlistcallback(int resp, void *data)
819 {
820     updatehublist();
821 }
822
823 void gettrlistcallback(int resp, void *data)
824 {
825     updatetransferlists();
826 }
827
828 void logincallback(int err, wchar_t *reason, void *data)
829 {
830     switch(err)
831     {
832     case DC_LOGIN_ERR_SUCCESS:
833         dc_queuecmd(NULL, NULL, L"notify", L"all", L"on", L"fn:peer", L"off", NULL);
834         dc_getfnlistasync(getfnlistcallback, NULL);
835         dc_gettrlistasync(gettrlistcallback, NULL);
836         updatesbar("Authenticated");
837         break;
838     case DC_LOGIN_ERR_NOLOGIN:
839         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not negotiate an acceptable authentication mechanism"));
840         dc_disconnect();
841         dcdisconnected();
842         break;
843     case DC_LOGIN_ERR_SERVER:
844         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The server has encountered an error"));
845         dc_disconnect();
846         dcdisconnected();
847         break;
848     case DC_LOGIN_ERR_USER:
849         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal client error"));
850         dc_disconnect();
851         dcdisconnected();
852         break;
853     case DC_LOGIN_ERR_CONV:
854         dc_disconnect();
855         dcdisconnected();
856         break;
857     case DC_LOGIN_ERR_AUTHFAIL:
858         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Login attempt failed!"));
859         dc_disconnect();
860         dcdisconnected();
861         break;
862     }
863     updatewrite();
864 }
865
866 GtkTreeIter *ref2iter(GtkTreeRowReference *ref)
867 {
868     static GtkTreeIter iter;
869     GtkTreePath *path;
870     
871     assert((path = gtk_tree_row_reference_get_path(ref)) != NULL);
872     assert(gtk_tree_model_get_iter(GTK_TREE_MODEL(srchmodel), &iter, path));
873     gtk_tree_path_free(path);
874     return(&iter);
875 }
876
877 GtkTreeRowReference *iter2ref(GtkTreeIter *iter)
878 {
879     GtkTreePath *path;
880     GtkTreeRowReference *ref;
881     
882     assert((path = gtk_tree_model_get_path(GTK_TREE_MODEL(srchmodel), iter)) != NULL);
883     assert((ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(srchmodel), path)) != NULL);
884     gtk_tree_path_free(path);
885     return(ref);
886 }
887
888 struct srchsize *finddiscsize(void)
889 {
890     int i;
891     GtkTreeIter iter;
892     
893     for(i = 0; i < numsizes; i++)
894     {
895         if(srchsizes[i].size == -1)
896             return(&srchsizes[i]);
897     }
898     srchsizes = srealloc(srchsizes, sizeof(*srchsizes) * ++numsizes);
899     srchsizes[i].size = -1;
900     srchsizes[i].num = 1;
901     srchsizes[i].slots = 0;
902     srchsizes[i].resptime = 0.0;
903     gtk_tree_store_append(srchmodel, &iter, NULL);
904     gtk_tree_store_set(srchmodel, &iter, 3, _("Discrete sizes"), 7, 1, -1);
905     srchsizes[i].ref = iter2ref(&iter);
906     return(&srchsizes[i]);
907 }
908
909 struct knownspeed *findksentbyname(char *userid)
910 {
911     int i;
912     
913     for(i = 0; i < numspeeds; i++)
914     {
915         if(!strcmp(knownspeeds[i].userid, userid))
916             return(&knownspeeds[i]);
917     }
918     return(NULL);
919 }
920
921 struct knownspeed *findksentbyseq(int seq)
922 {
923     int i;
924     
925     for(i = 0; i < numspeeds; i++)
926     {
927         if(knownspeeds[i].seq == seq)
928             return(&knownspeeds[i]);
929     }
930     return(NULL);
931 }
932
933 gboolean ksupdaterow(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
934 {
935     struct knownspeed *ks;
936     char *userid;
937     
938     gtk_tree_model_get(GTK_TREE_MODEL(model), iter, 1, &userid, -1);
939     if(userid == NULL)
940         return(FALSE);
941     ks = findksentbyname(userid);
942     if(ks == NULL)
943     {
944         knownspeeds = srealloc(knownspeeds, (numspeeds + 1) * sizeof(*knownspeeds));
945         ks = &knownspeeds[numspeeds];
946         numspeeds++;
947         ks->userid = sstrdup(userid);
948         ks->speed = -1;
949         ks->seq = -2;
950         ksqueryseq = -2;
951     }
952     g_free(userid);
953     if(ks->speed != -1)
954         gtk_tree_store_set(GTK_TREE_STORE(model), iter, 8, ks->speed, -1);
955     return(FALSE);
956 }
957
958 gint ksupdatecb(gpointer data)
959 {
960     int i, oldnum;
961     time_t now;
962     wchar_t **users, *buf;
963     size_t userssize, usersdata;
964     
965     if(ksquerytag != -1)
966         return(TRUE);
967     now = time(NULL);
968     oldnum = numspeeds;
969     for(i = 0; i < numspeeds;)
970     {
971         if(now - knownspeeds[i].fetched > 60)
972         {
973             free(knownspeeds[i].userid);
974             memmove(&knownspeeds[i], &knownspeeds[i + 1], (--numspeeds - i) * sizeof(*knownspeeds));
975         } else {
976             i++;
977         }
978     }
979     if(oldnum != numspeeds)
980     {
981         if(numspeeds == 0)
982         {
983             free(knownspeeds);
984             knownspeeds = NULL;
985         } else {
986             knownspeeds = srealloc(knownspeeds, numspeeds * sizeof(*knownspeeds));
987         }
988     }
989     gtk_tree_model_foreach(GTK_TREE_MODEL(srchmodel), ksupdaterow, NULL);
990     if(ksqueryseq == -2)
991     {
992         users = NULL;
993         userssize = usersdata = 0;
994         ksqueryseq = 0;
995         for(i = 0; i < numspeeds; i++)
996         {
997             if(knownspeeds[i].seq == -2)
998             {
999                 assert((buf = icmbstowcs(knownspeeds[i].userid, "UTF-8")) != NULL);
1000                 knownspeeds[i].seq = ksqueryseq++;
1001                 addtobuf(users, buf);
1002             }
1003         }
1004         addtobuf(users, NULL);
1005         ksquerytag = dc_queuecmd(NULL, NULL, L"filtercmd", L"userspeeda", L"%a", users, NULL);
1006         dc_freewcsarr(users);
1007     }
1008     updatewrite();
1009     return(TRUE);
1010 }
1011
1012 void handleresps(void)
1013 {
1014     int i;
1015     struct dc_response *resp;
1016     struct dc_intresp *ires;
1017     struct dc_fnetnode *fn;
1018     struct fndata *fndata;
1019     GtkTextIter iter;
1020     GtkTreeIter titer, piter;
1021     char *buf, *p;
1022     int tosbuf;
1023     struct srchsize *ss;
1024     struct knownspeed *ks;
1025     
1026     while((resp = dc_getresp()) != NULL)
1027     {
1028         if(!wcscmp(resp->cmdname, L".connect"))
1029         {
1030             if(resp->code != 201)
1031             {
1032                 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The server refused the connection"));
1033                 dc_disconnect();
1034                 dcdisconnected();
1035             } else if(dc_checkprotocol(resp, DC_LATEST)) {
1036                 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Server protocol revision mismatch"));
1037                 dc_disconnect();
1038                 dcdisconnected();
1039             } else {
1040                 tosbuf = 0x10; /* Minimum delay */
1041                 setsockopt(dcfd, IPPROTO_IP, IP_TOS, &tosbuf, sizeof(tosbuf));
1042                 updatesbar(_("Connected"));
1043                 dc_loginasync(connectas, 1, loginconv, logincallback, NULL);
1044             }
1045         } else if(!wcscmp(resp->cmdname, L".notify")) {
1046             dc_uimisc_handlenotify(resp);
1047             switch(resp->code)
1048             {
1049             case 600:
1050                 if((ires = dc_interpret(resp)) != NULL)
1051                 {
1052                     if((fn = dc_findfnetnode(ires->argv[0].val.num)) != NULL)
1053                     {
1054                         fndata = fn->udata;
1055                         gtk_text_buffer_get_end_iter(fndata->textbuf, &iter);
1056                         if((buf = icwcstombs(ires->argv[3].val.str, "UTF-8")) != NULL)
1057                         {
1058                             gtk_text_buffer_insert_with_tags_by_name(fndata->textbuf, &iter, "<", -1, "sender", NULL);
1059                             gtk_text_buffer_insert_with_tags_by_name(fndata->textbuf, &iter, buf, -1, "sender", NULL);
1060                             gtk_text_buffer_insert_with_tags_by_name(fndata->textbuf, &iter, ">", -1, "sender", NULL);
1061                             gtk_text_buffer_insert(fndata->textbuf, &iter, " ", -1);
1062                             free(buf);
1063                         }
1064                         if((buf = icwcstombs(ires->argv[4].val.str, "UTF-8")) != NULL)
1065                         {
1066                             gtk_text_buffer_insert(fndata->textbuf, &iter, buf, -1);
1067                             gtk_text_buffer_insert(fndata->textbuf, &iter, "\n", -1);
1068                             free(buf);
1069                             if(curchat == fn->id)
1070                                 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(main_chatview), &iter, 0, 0, 0, 0);
1071                         }
1072                     }
1073                     dc_freeires(ires);
1074                 }
1075                 break;
1076             case 601:
1077             case 602:
1078             case 603:
1079             case 604:
1080             case 605:
1081                 updatehublist();
1082                 break;
1083             case 610:
1084             case 611:
1085             case 612:
1086             case 613:
1087             case 614:
1088             case 615:
1089             case 616:
1090             case 617:
1091             case 618:
1092                 updatetransferlists();
1093                 break;
1094             case 620:
1095                 if((ires = dc_interpret(resp)) != NULL)
1096                 {
1097                     if(ires->argv[0].val.num == nextsrch)
1098                         srcheta = time(NULL) + ires->argv[0].val.num;
1099                     dc_freeires(ires);
1100                 }
1101                 break;
1102             case 621:
1103                 if((ires = dc_interpret(resp)) != NULL)
1104                 {
1105                     if(ires->argv[0].val.num == nextsrch)
1106                     {
1107                         if(cursrch != -1)
1108                             dc_queuecmd(NULL, NULL, L"cansrch", L"%i", cursrch, NULL);
1109                         cursrch = nextsrch;
1110                         nextsrch = -1;
1111                         gtk_widget_set_sensitive(main_realsrch, TRUE);
1112                         gtk_widget_set_sensitive(main_simplesrch, TRUE);
1113                         gtk_widget_set_sensitive(main_srchbtn, TRUE);
1114                         gtk_widget_set_sensitive(main_srchcanbtn, FALSE);
1115                         srchstatupdate();
1116                         gtk_entry_set_text(GTK_ENTRY(main_realsrch), "");
1117                         gtk_entry_set_text(GTK_ENTRY(main_simplesrch), "");
1118                         gtk_tree_store_clear(srchmodel);
1119                         freesrchsizes();
1120                     }
1121                     dc_freeires(ires);
1122                 }
1123                 break;
1124             case 622:
1125                 if((ires = dc_interpret(resp)) != NULL)
1126                 {
1127                     if(ires->argv[0].val.num == cursrch)
1128                     {
1129                         for(i = 0; i < numsizes; i++)
1130                         {
1131                             if(srchsizes[i].size == ires->argv[4].val.lnum)
1132                                 break;
1133                         }
1134                         if(i == numsizes)
1135                         {
1136                             srchsizes = srealloc(srchsizes, sizeof(*srchsizes) * ++numsizes);
1137                             srchsizes[i].size = (gint64)ires->argv[4].val.lnum;
1138                             srchsizes[i].num = 1;
1139                             srchsizes[i].slots = ires->argv[5].val.num;
1140                             srchsizes[i].resptime = ires->argv[7].val.flnum;
1141                             ss = finddiscsize();
1142                             ss->slots += ires->argv[5].val.num;
1143                             if((ss->resptime == 0.0) || (ss->resptime > ires->argv[7].val.flnum))
1144                                 ss->resptime = ires->argv[7].val.flnum;
1145                             piter = *ref2iter(ss->ref);
1146                             gtk_tree_store_set(srchmodel, &piter, 5, ss->slots, 6, ss->resptime, -1);
1147                             gtk_tree_store_append(srchmodel, &titer, &piter);
1148                             srchsizes[i].ref = iter2ref(&titer);
1149                         } else if(srchsizes[i].num == 1) {
1150                             char *filename, *peername, *fnetname, *hash;
1151                             int slots, speed;
1152                             double resptime;
1153                             
1154                             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);
1155                             gtk_tree_store_remove(srchmodel, ref2iter(srchsizes[i].ref));
1156                             gtk_tree_row_reference_free(srchsizes[i].ref);
1157                             ss = finddiscsize();
1158                             ss->slots -= slots;
1159                             gtk_tree_store_set(srchmodel, ref2iter(ss->ref), 5, ss->slots, -1);
1160                             gtk_tree_store_append(srchmodel, &piter, NULL);
1161                             srchsizes[i].slots = ires->argv[5].val.num + slots;
1162                             srchsizes[i].resptime = (ires->argv[7].val.flnum < resptime)?ires->argv[7].val.flnum:resptime;
1163                             srchsizes[i].num = 2;
1164                             srchsizes[i].ref = iter2ref(&piter);
1165                             gtk_tree_store_set(srchmodel, &piter, 4, srchsizes[i].size, 5, srchsizes[i].slots, 6, srchsizes[i].resptime, 7, 2, -1);
1166                             if((buf = icwcstombs(ires->argv[1].val.str, "UTF-8")) != NULL)
1167                             {
1168                                 p = buf;
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"