Properly process Gtk2 error-times as gint64s.
[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     gint64 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((const time_t[]){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, 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, (gint64)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", L"fn:peer", L"off", 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                                 if(strrchr(p, '/') != NULL)
1169                                     p = strrchr(p, '/') + 1;
1170                                 gtk_tree_store_set(srchmodel, &piter, 3, p, -1);
1171                                 free(buf);
1172                             }
1173                             gtk_tree_store_append(srchmodel, &titer, &piter);
1174                             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);
1175                             g_free(filename); g_free(peername); g_free(fnetname); g_free(hash);
1176                             gtk_tree_store_append(srchmodel, &titer, &piter);
1177                         } else {
1178                             srchsizes[i].num++;
1179                             srchsizes[i].slots += ires->argv[5].val.num;
1180                             if(ires->argv[7].val.flnum < srchsizes[i].resptime)
1181                                 srchsizes[i].resptime = ires->argv[7].val.flnum;
1182                             piter = *ref2iter(srchsizes[i].ref);
1183                             gtk_tree_store_set(srchmodel, &piter, 5, srchsizes[i].slots, 6, srchsizes[i].resptime, 7, srchsizes[i].num, -1);
1184                             gtk_tree_store_append(srchmodel, &titer, &piter);
1185                         }
1186                         if((buf = icwcstombs(ires->argv[1].val.str, "UTF-8")) != NULL)
1187                         {
1188                             gtk_tree_store_set(srchmodel, &titer, 3, buf, -1);
1189                             free(buf);
1190                         }
1191                         if((buf = icwcstombs(ires->argv[2].val.str, "UTF-8")) != NULL)
1192                         {
1193                             gtk_tree_store_set(srchmodel, &titer, 0, buf, -1);
1194                             free(buf);
1195                         }
1196                         if((buf = icwcstombs(ires->argv[3].val.str, "UTF-8")) != NULL)
1197                         {
1198                             gtk_tree_store_set(srchmodel, &titer, 1, buf, -1);
1199                             gtk_tree_store_set(srchmodel, &titer, 2, buf, -1);
1200                             free(buf);
1201                         }
1202                         if((buf = icwcstombs(ires->argv[8].val.str, "UTF-8")) != NULL)
1203                         {
1204                             gtk_tree_store_set(srchmodel, &titer, 9, buf, -1);
1205                             free(buf);
1206                         }
1207                         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);
1208                     }
1209                     dc_freeires(ires);
1210                 }
1211                 break;
1212             default:
1213                 break;
1214             }
1215         } else if(!wcscmp(resp->cmdname, L"filtercmd")) {
1216             if((ksquerytag >= 0) && (ksquerytag == resp->tag))
1217             {
1218                 for(i = 0; i < resp->numlines; i++)
1219                 {
1220                     assert((ks = findksentbyseq(i)) != NULL);
1221                     ks->speed = wcstol(resp->rlines[i].argv[1], NULL, 10);
1222                     ks->seq = -1;
1223                     ks->fetched = time(NULL);
1224                 }
1225                 ksquerytag = -1;
1226                 ksupdatecb(NULL);
1227             } else if((lsrestag >= 0) && (lsrestag == resp->tag)) {
1228                 for(i = 0; i < resp->numlines; i++)
1229                 {
1230                     if(!wcsncmp(resp->rlines[i].argv[1], L"id:", 3))
1231                     {
1232                         gtk_list_store_append(reslist, &titer);
1233                         gtk_list_store_set(reslist, &titer, 0, icswcstombs(resp->rlines[i].argv[1] + 3, "UTF-8", NULL), -1);
1234                     } else if(!wcsncmp(resp->rlines[i].argv[1], L"size:", 5)) {
1235                         gtk_list_store_set(reslist, &titer, 1, (gint64)wcstoll(resp->rlines[i].argv[1] + 5, NULL, 10), -1);
1236                     } else if(!wcsncmp(resp->rlines[i].argv[1], L"prog:", 5)) {
1237                         gtk_list_store_set(reslist, &titer, 2, (gint64)wcstoll(resp->rlines[i].argv[1] + 5, NULL, 10), -1);
1238                     } else if(!wcsncmp(resp->rlines[i].argv[1], L"name:", 5)) {
1239                         gtk_list_store_set(reslist, &titer, 3, icswcstombs(resp->rlines[i].argv[1] + 5, "UTF-8", NULL), -1);
1240                     } else if(!wcsncmp(resp->rlines[i].argv[1], L"lock:", 5)) {
1241                         if(!wcscmp(resp->rlines[i].argv[1] + 5, L"yes"))
1242                             gtk_list_store_set(reslist, &titer, 4, TRUE, -1);
1243                         else
1244                             gtk_list_store_set(reslist, &titer, 4, FALSE, -1);
1245                     } else if(!wcsncmp(resp->rlines[i].argv[1], L"hash:", 5)) {
1246                         gtk_list_store_set(reslist, &titer, 5, icswcstombs(resp->rlines[i].argv[1] + 5, "UTF-8", NULL), -1);
1247                     }
1248                 }
1249                 lsrestag = -1;
1250                 gtk_widget_set_sensitive(reslist_reload, TRUE);
1251             }
1252         }
1253         dc_freeresp(resp);
1254     }
1255     updatewrite();
1256 }
1257
1258 void dcfdcallback(gpointer data, gint source, GdkInputCondition condition)
1259 {
1260     int errnobak;
1261     
1262     if(((condition & GDK_INPUT_READ) && dc_handleread()) || ((condition & GDK_INPUT_WRITE) && dc_handlewrite()))
1263     {
1264         errnobak = errno;
1265         dcdisconnected();
1266         if(errnobak == 0)
1267         {
1268             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The server has closed the connection"));
1269         } else {
1270             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("The connection to the server failed:\n\n%s"), strerror(errnobak));
1271         }
1272         return;
1273     }
1274     handleresps();
1275 }
1276
1277 void cb_main_dconnmenu_activate(GtkWidget *widget, gpointer data)
1278 {
1279     if(dcfd < 0)
1280         return;
1281     dc_disconnect();
1282     dcdisconnected();
1283 }
1284
1285 void cb_main_prefmenu_activate(GtkWidget *widget, gpointer data)
1286 {
1287     GtkWidget *dialog, *swnd;
1288     int resp;
1289     
1290     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);
1291     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), swnd = create_pref_wnd(), TRUE, TRUE, 0);
1292     gtk_entry_set_text(GTK_ENTRY(pref_pubhuburl), pubhubaddr);
1293     gtk_entry_set_text(GTK_ENTRY(pref_connectas), connectas);
1294     gtk_entry_set_text(GTK_ENTRY(pref_dcserver), dcserver);
1295     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pref_autoconn), autoconn);
1296     gtk_widget_show(swnd);
1297     resp = gtk_dialog_run(GTK_DIALOG(dialog));
1298     if(resp == GTK_RESPONSE_ACCEPT)
1299     {
1300         free(pubhubaddr);
1301         pubhubaddr = sstrdup(gtk_entry_get_text(GTK_ENTRY(pref_pubhuburl)));
1302         free(connectas);
1303         connectas = sstrdup(gtk_entry_get_text(GTK_ENTRY(pref_connectas)));
1304         free(dcserver);
1305         dcserver = sstrdup(gtk_entry_get_text(GTK_ENTRY(pref_dcserver)));
1306         autoconn = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_autoconn));
1307     }
1308     gtk_widget_destroy(dialog);
1309 }
1310
1311 void cb_main_lsres_activate(GtkWidget *widget, gpointer data)
1312 {
1313     gtk_list_store_clear(reslist);
1314     gtk_widget_set_sensitive(reslist_delete, FALSE);
1315     gtk_widget_set_sensitive(reslist_search, FALSE);
1316     gtk_widget_show(reslist_wnd);
1317     if(lsrestag == -1)
1318     {
1319         lsrestag = dc_queuecmd(NULL, NULL, L"filtercmd", L"lsres", NULL);
1320         gtk_widget_set_sensitive(reslist_reload, FALSE);
1321     }
1322     updatewrite();
1323 }
1324
1325 void dcconnect(char *host)
1326 {
1327     dcfd = dc_connect(host);
1328     if(dcfd < 0)
1329     {
1330         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect:\n\n%s"), strerror(errno));
1331         return;
1332     }
1333     gdkread = gdk_input_add(dcfd, GDK_INPUT_READ, dcfdcallback, NULL);
1334     updatewrite();
1335     gtk_widget_set_sensitive(main_connmenu, FALSE);
1336     gtk_widget_set_sensitive(main_dconnmenu, TRUE);
1337     updatesbar(_("Connecting..."));
1338 }
1339
1340 void cb_main_connmenu_activate(GtkWidget *widget, gpointer data)
1341 {
1342     char *buf;
1343     
1344     if(dcfd >= 0)
1345         return;
1346     if((buf = inputbox(_("Connect"), _("Server address:"), dcserver, 1)) == NULL)
1347         return;
1348     dcconnect(buf);
1349     free(buf);
1350 }
1351
1352 void cb_main_sdmenu_activate(GtkWidget *widget, gpointer data)
1353 {
1354     int tag;
1355     struct dc_response *resp;
1356
1357     if(dcfd < 0)
1358     {
1359         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1360         return;
1361     }
1362     tag = dc_queuecmd(NULL, NULL, L"shutdown", NULL);
1363     if((resp = dc_gettaggedrespsync(tag)) != NULL)
1364     {
1365         if(resp->code == 502)
1366             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1367         dc_freeresp(resp);
1368     }
1369     handleresps();
1370 }
1371
1372 void cb_main_fnaddr_activate(GtkWidget *widget, gpointer data)
1373 {
1374     int tag;
1375     struct dc_response *resp;
1376     wchar_t **toks;
1377     
1378     if(dcfd < 0)
1379     {
1380         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1381         return;
1382     }
1383     toks = dc_lexsexpr(icsmbstowcs((char *)gtk_entry_get_text(GTK_ENTRY(main_fnaddr)), "UTF-8", NULL));
1384     if(*toks == NULL)
1385     {
1386         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Illegal address entered"));
1387         return;
1388     }
1389     if(wcschr(toks[0], L':') == NULL)
1390     {
1391         toks[0] = srealloc(toks[0], (wcslen(toks[0]) + 5) * sizeof(wchar_t));
1392         wcscat(toks[0], L":411");
1393     }
1394     tag = dc_queuecmd(NULL, NULL, L"cnct", L"dc", L"%a", toks, NULL);
1395     dc_freewcsarr(toks);
1396     if((resp = dc_gettaggedrespsync(tag)) != NULL)
1397     {
1398         if(resp->code == 502)
1399             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1400         if(resp->code == 509)
1401             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("The server could not parse that address"));
1402         if(resp->code == 515)
1403             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("There are too many hubs connected"));
1404         dc_freeresp(resp);
1405     }
1406     gtk_entry_set_text(GTK_ENTRY(main_fnaddr), "");
1407     handleresps();
1408 }
1409
1410 void setpubhubmodel(GtkTreeModel *model, int sortcol, int numcols, int *cols, char **names)
1411 {
1412     GtkTreeViewColumn *col;
1413     GtkCellRenderer *rnd;
1414     GtkTreeModel *sortmodel;
1415     int i;
1416     
1417     while((col = gtk_tree_view_get_column(GTK_TREE_VIEW(main_phublist), 0)) != NULL)
1418         gtk_tree_view_remove_column(GTK_TREE_VIEW(main_phublist), col);
1419     for(i = 0; i < numcols; i++) {
1420         if(gtk_tree_model_get_column_type(model, cols[i]) == G_TYPE_INT64)
1421         {
1422             col = gtk_tree_view_column_new();
1423             gtk_tree_view_column_set_title(col, names[i]);
1424             rnd = gtk_cell_renderer_text_new();
1425             gtk_tree_view_column_pack_start(col, rnd, TRUE);
1426             gtk_tree_view_column_set_cell_data_func(col, rnd, transnicebytefunc2, GINT_TO_POINTER(cols[i]), NULL);
1427         } else {
1428             col = gtk_tree_view_column_new_with_attributes(names[i], gtk_cell_renderer_text_new(), "text", cols[i], NULL);
1429         }
1430         gtk_tree_view_column_set_sort_column_id(col, cols[i]);
1431         gtk_tree_view_column_set_resizable(col, TRUE);
1432         gtk_tree_view_append_column(GTK_TREE_VIEW(main_phublist), col);
1433     }
1434     sortmodel = gtk_tree_model_sort_new_with_model(model);
1435     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortmodel), sortcol, GTK_SORT_DESCENDING);
1436     gtk_tree_view_set_model(GTK_TREE_VIEW(main_phublist), sortmodel);
1437     g_object_unref(sortmodel);
1438 }
1439
1440 void cb_main_pubhubfilter_activate(GtkWidget *widget, gpointer data)
1441 {
1442     int err;
1443     const char *buf;
1444     char errbuf[1024];
1445     regex_t *filter;
1446     
1447     buf = gtk_entry_get_text(GTK_ENTRY(main_pubhubfilter));
1448     if(*buf)
1449     {
1450         filter = smalloc(sizeof(*filter));
1451         if((err = regcomp(filter, buf, REG_EXTENDED | REG_ICASE | REG_NOSUB)) != 0)
1452         {
1453             regerror(err, filter, errbuf, sizeof(errbuf));
1454             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Could not compile regex: %s", errbuf);
1455             regfree(filter);
1456             free(filter);
1457             return;
1458         }
1459     } else {
1460         filter = NULL;
1461     }
1462     fetchhublist(pubhubaddr, filter);
1463 }
1464
1465 void cb_main_pubhubabort_clicked(GtkWidget *widget, gpointer data)
1466 {
1467     aborthublist();
1468 }
1469
1470 void cb_main_dcnctbtn_clicked(GtkWidget *widget, gpointer data)
1471 {
1472     GtkTreeIter iter;
1473     int tag, id;
1474     struct dc_response *resp;
1475     
1476     if(dcfd < 0)
1477     {
1478         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1479         return;
1480     }
1481     if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(main_fnetnodes)), NULL, &iter))
1482     {
1483         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("No hub selected"));
1484         return;
1485     }
1486     gtk_tree_model_get(GTK_TREE_MODEL(fnmodel), &iter, 0, &id, -1);
1487     tag = dc_queuecmd(NULL, NULL, L"dcnct", L"%i", id, NULL);
1488     if((resp = dc_gettaggedrespsync(tag)) != NULL)
1489     {
1490         if(resp->code == 502)
1491             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1492         dc_freeresp(resp);
1493     }
1494     handleresps();
1495 }
1496
1497 void cb_main_phublist_cchange(GtkWidget *widget, gpointer data)
1498 {
1499     GtkTreeIter iter;
1500     GtkTreeModel *model;
1501     char *addr;
1502     
1503     if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(main_phublist)), &model, &iter))
1504         return;
1505     gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &addr, -1);
1506     gtk_entry_set_text(GTK_ENTRY(main_fnaddr), addr);
1507     g_free(addr);
1508 }
1509
1510 void cb_main_phublist_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data)
1511 {
1512     int tag;
1513     struct dc_response *resp;
1514     GtkTreeIter iter;
1515     GtkTreeModel *model;
1516     char *buf;
1517     
1518     if(dcfd < 0)
1519     {
1520         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1521         return;
1522     }
1523     model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
1524     if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
1525         return;
1526     gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &buf, -1);
1527     if(strchr(buf, ':') == NULL)
1528     {
1529         buf = g_realloc(buf, strlen(buf) + 5);
1530         strcat(buf, ":411");
1531     }
1532     tag = dc_queuecmd(NULL, NULL, L"cnct", L"dc", L"%s", buf, NULL);
1533     g_free(buf);
1534     gtk_entry_set_text(GTK_ENTRY(main_fnaddr), "");
1535     if((resp = dc_gettaggedrespsync(tag)) != NULL)
1536     {
1537         if(resp->code == 502)
1538             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1539         if(resp->code == 509)
1540             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("The server could not parse that address"));
1541         if(resp->code == 515)
1542             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("There are too many hubs connected"));
1543         dc_freeresp(resp);
1544     }
1545     handleresps();
1546 }
1547
1548 void cb_main_chatnodes_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer uudata)
1549 {
1550     GtkTreeIter iter;
1551     int id;
1552     struct dc_fnetnode *fn;
1553     struct fndata *data;
1554     
1555     if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(fnmodel), &iter, path))
1556         return;
1557     gtk_tree_model_get(GTK_TREE_MODEL(fnmodel), &iter, 0, &id, -1);
1558     if((fn = dc_findfnetnode(id)) == NULL)
1559         return;
1560     data = fn->udata;
1561     curchat = id;
1562     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fnmodel), &iter))
1563     {
1564         do
1565         {
1566             gtk_tree_model_get(GTK_TREE_MODEL(fnmodel), &iter, 0, &id, -1);
1567             if(id == curchat)
1568                 gtk_list_store_set(fnmodel, &iter, 5, "gtk-apply", -1);
1569             else
1570                 gtk_list_store_set(fnmodel, &iter, 5, NULL, -1);
1571         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(fnmodel), &iter));
1572     }
1573     gtk_text_view_set_buffer(GTK_TEXT_VIEW(main_chatview), GTK_TEXT_BUFFER(data->textbuf));
1574 }
1575
1576 void cb_main_chatstr_activate(GtkWidget *widget, gpointer data)
1577 {
1578     int tag;
1579     const char *buf;
1580     struct dc_response *resp;
1581     
1582     if(dcfd < 0)
1583     {
1584         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1585         return;
1586     }
1587     if(curchat < 0)
1588     {
1589         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("No hub selected"));
1590         return;
1591     }
1592     buf = gtk_entry_get_text(GTK_ENTRY(main_chatstr));
1593     tag = dc_queuecmd(NULL, NULL, L"sendchat", L"%i", curchat, L"1", L"", L"%s", buf, NULL);
1594     if((resp = dc_gettaggedrespsync(tag)) != NULL)
1595     {
1596         if(resp->code == 502)
1597             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1598         else if(resp->code == 504)
1599             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("This hub could not support all the types of characters in your chat message"));
1600         else if(resp->code == 513)
1601             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("This hub does not support chatting"));
1602         else if(resp->code != 200)
1603             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to chat (%i)"), resp->code);
1604         dc_freeresp(resp);
1605     }
1606     gtk_entry_set_text(GTK_ENTRY(main_chatstr), "");
1607     handleresps();
1608 }
1609
1610 void updatesrchfld(const char *simple)
1611 {
1612     char *buf, *s;
1613     char *p, *p2;
1614     size_t bufsize, bufdata;
1615     
1616     s = sstrdup(simple);
1617     buf = NULL;
1618     bufsize = bufdata = 0;
1619     p = s;
1620     do
1621     {
1622         p2 = strchr(p, ' ');
1623         if(p2 != NULL)
1624             *(p2++) = 0;
1625         if(*p)
1626         {
1627             if(bufdata > 0)
1628                 bufcat(buf, " & ", 3);
1629             bufcat(buf, "N~", 2);
1630             for(; *p; p++)
1631             {
1632                 if(strchr("[]()$^.*?+\\|\"!", *p) != NULL)
1633                     addtobuf(buf, '\\');
1634                 addtobuf(buf, *p);
1635             }
1636         }
1637         p = p2;
1638     } while(p2 != NULL);
1639     addtobuf(buf, 0);
1640     gtk_entry_set_text(GTK_ENTRY(main_realsrch), buf);
1641     free(buf);
1642     free(s);
1643 }
1644
1645 void cb_main_simplesrch_changed(GtkWidget *widget, gpointer data)
1646 {
1647     if(srchautoupdate)
1648         return;
1649     srchautoupdate = 1;
1650     updatesrchfld(gtk_entry_get_text(GTK_ENTRY(main_simplesrch)));
1651     srchautoupdate = 0;
1652 }
1653
1654 void cb_main_realsrch_changed(GtkWidget *widget, gpointer data)
1655 {
1656     if(srchautoupdate)
1657         return;
1658     srchautoupdate = 1;
1659     gtk_entry_set_text(GTK_ENTRY(main_simplesrch), "");
1660     srchautoupdate = 0;
1661 }
1662
1663 void cb_main_srchbtn_clicked(GtkWidget *widget, gpointer data)
1664 {
1665     wchar_t **toks;
1666     int tag;
1667     struct dc_response *resp;
1668     struct dc_intresp *ires;
1669     
1670     if(dcfd < 0)
1671     {
1672         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1673         return;
1674     }
1675     if(nextsrch != -1) /* Impossible case, but oh well... */
1676         return;
1677     toks = dc_lexsexpr(icsmbstowcs((char *)gtk_entry_get_text(GTK_ENTRY(main_realsrch)), "UTF-8", NULL));
1678     if(*toks == NULL)
1679     {
1680         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Please enter a search expression before searching"));
1681         return;
1682     }
1683     tag = dc_queuecmd(NULL, NULL, L"search", L"all", L"%a", toks, NULL);
1684     dc_freewcsarr(toks);
1685     if((resp = dc_gettaggedrespsync(tag)) != NULL)
1686     {
1687         if(resp->code == 501)
1688             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not find any hubs to search on"));
1689         else if(resp->code == 502)
1690             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1691         else if(resp->code == 509)
1692             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("The server could not parse your search expression"));
1693         else if(resp->code != 200)
1694             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to search (%i)"), resp->code);
1695         if(resp->code == 200)
1696         {
1697             if((ires = dc_interpret(resp)) != NULL)
1698             {
1699                 nextsrch = ires->argv[0].val.num;
1700                 srcheta = time(NULL) + ires->argv[1].val.num;
1701                 dc_freeires(ires);
1702             }
1703             gtk_widget_set_sensitive(main_realsrch, FALSE);
1704             gtk_widget_set_sensitive(main_simplesrch, FALSE);
1705             gtk_widget_set_sensitive(main_srchbtn, FALSE);
1706             gtk_widget_set_sensitive(main_srchcanbtn, TRUE);
1707             srchstatupdate();
1708         }
1709         dc_freeresp(resp);
1710     }
1711     handleresps();
1712 }
1713
1714 void cb_main_srchcanbtn_clicked(GtkWidget *widget, gpointer data)
1715 {
1716     if(nextsrch == -1)
1717         return;
1718     dc_queuecmd(NULL, NULL, L"cansrch", L"%i", nextsrch, NULL);
1719     nextsrch = -1;
1720     gtk_widget_set_sensitive(main_realsrch, TRUE);
1721     gtk_widget_set_sensitive(main_simplesrch, TRUE);
1722     gtk_widget_set_sensitive(main_srchbtn, TRUE);
1723     gtk_widget_set_sensitive(main_srchcanbtn, FALSE);
1724     srchstatupdate();
1725 }
1726
1727 gboolean cb_main_trlist_keypress(GtkWidget *widget, GdkEventKey *event, gpointer data)
1728 {
1729     int id, tag;
1730     GtkTreeSelection *sel;
1731     GtkTreeModel *model;
1732     GtkTreeIter iter;
1733     struct dc_response *resp;
1734     
1735     if((event->type == GDK_KEY_PRESS) && (event->keyval == GDK_Delete))
1736     {
1737         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1738         if(gtk_tree_selection_get_selected(sel, &model, &iter))
1739         {
1740             gtk_tree_model_get(model, &iter, 0, &id, -1);
1741             tag = dc_queuecmd(NULL, NULL, L"cancel", L"%i", id, NULL);
1742             if((resp = dc_gettaggedrespsync(tag)) != NULL)
1743             {
1744                 if(resp->code == 502)
1745                     msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1746                 else if(resp->code != 200)
1747                     msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to cancel (%i)"), resp->code);
1748                 dc_freeresp(resp);
1749             }
1750             handleresps();
1751         }
1752         return(TRUE);
1753     }
1754     return(FALSE);
1755 }
1756
1757 void cb_main_srchres_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data)
1758 {
1759     int tag;
1760     struct dc_response *resp;
1761     GtkTreeIter iter;
1762     GtkTreeModel *model;
1763     int num;
1764     gint64 size;
1765     char *tfnet, *tpeerid, *tfilename, *thash, *arg;
1766     wchar_t *fnet, *peerid, *filename, *hash;
1767     
1768     if(dcfd < 0)
1769     {
1770         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Not connected to DC server"));
1771         return;
1772     }
1773     model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
1774     if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
1775         return;
1776     gtk_tree_model_get(model, &iter, 7, &num, -1);
1777     if(num > 0)
1778         return;
1779     gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &tfnet, 1, &tpeerid, 3, &tfilename, 4, &size, 9, &thash, -1);
1780     fnet = icmbstowcs(tfnet, "UTF-8");
1781     peerid = icmbstowcs(tpeerid, "UTF-8");
1782     filename = icmbstowcs(tfilename, "UTF-8");
1783     hash = (thash == NULL)?NULL:icmbstowcs(thash, "UTF-8");
1784     if((fnet == NULL) || (peerid == NULL) || (filename == NULL))
1785     {
1786         if(fnet != NULL)
1787             free(fnet);
1788         if(peerid != NULL)
1789             free(peerid);
1790         if(filename != NULL)
1791             free(filename);
1792         if(hash != NULL)
1793             free(hash);
1794         g_free(tfnet);
1795         g_free(tpeerid);
1796         g_free(tfilename);
1797         if(thash != NULL)
1798             g_free(thash);
1799         return;
1800     }
1801     g_free(tfnet);
1802     g_free(tpeerid);
1803     g_free(tfilename);
1804     arg = (char *)gtk_entry_get_text(GTK_ENTRY(main_dlarg));
1805     if(*arg)
1806         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);
1807     else
1808         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);
1809     free(fnet);
1810     free(peerid);
1811     free(filename);
1812     if(hash != NULL)
1813         free(hash);
1814     if((resp = dc_gettaggedrespsync(tag)) != NULL)
1815     {
1816         if(resp->code == 502)
1817             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1818         if(resp->code != 200)
1819             msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to queue the download (%i)"), resp->code);
1820         dc_freeresp(resp);
1821     }
1822     handleresps();
1823 }
1824
1825 gboolean srchfilterfunc(GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
1826 {
1827     int slots;
1828     int filteratall;
1829     
1830     filteratall = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(main_filternoslots));
1831     if(!filteratall)
1832         return(TRUE);
1833     gtk_tree_model_get(model, iter, 5, &slots, -1);
1834     if(slots < 1)
1835         return(FALSE);
1836     return(TRUE);
1837 }
1838
1839 void cb_main_filternoslots_toggled(GtkToggleButton *widget, gpointer data)
1840 {
1841     gtk_tree_model_filter_refilter(srchmodelfilter);
1842 }
1843
1844 void cb_main_srhash_activate(GtkWidget *widget, gpointer data)
1845 {
1846     GtkTreeSelection *sel;
1847     GtkTreeModel *model;
1848     GtkTreeIter iter;
1849     char *hash, *buf;
1850     
1851     if(nextsrch != -1)
1852         return;
1853     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_srchres));
1854     if(gtk_tree_selection_get_selected(sel, &model, &iter))
1855     {
1856         gtk_tree_model_get(model, &iter, 9, &hash, -1);
1857         buf = sprintf2("H=%s", hash);
1858         gtk_entry_set_text(GTK_ENTRY(main_realsrch), buf);
1859         g_free(hash);
1860         free(buf);
1861         cb_main_srchbtn_clicked(widget, NULL);
1862     } else {
1863         return;
1864     }
1865 }
1866
1867 void cb_main_srcopy_activate(GtkWidget *widget, gpointer data)
1868 {
1869     GtkClipboard *cb;
1870     GtkTreeSelection *sel;
1871     GtkTreeModel *model;
1872     GtkTreeIter iter;
1873     char *hash;
1874     
1875     if(nextsrch != -1)
1876         return;
1877     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_srchres));
1878     if(!gtk_tree_selection_get_selected(sel, &model, &iter))
1879         return;
1880     gtk_tree_model_get(model, &iter, 9, &hash, -1);
1881     cb = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
1882     gtk_clipboard_set_text(cb, hash, -1);
1883     g_free(hash);
1884 }
1885
1886 void cb_main_trhash_activate(GtkWidget *widget, gpointer data)
1887 {
1888     GtkTreeSelection *sel;
1889     GtkTreeModel *model;
1890     GtkTreeIter iter;
1891     char *hash, *buf;
1892     
1893     if(nextsrch != -1)
1894         return;
1895     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_downloads));
1896     if(gtk_tree_selection_get_selected(sel, &model, &iter))
1897     {
1898         gtk_tree_model_get(model, &iter, 12, &hash, -1);
1899         buf = sprintf2("H=%s", hash);
1900         gtk_entry_set_text(GTK_ENTRY(main_realsrch), buf);
1901         g_free(hash);
1902         free(buf);
1903         cb_main_srchbtn_clicked(widget, NULL);
1904     } else {
1905         return;
1906     }
1907 }
1908
1909 void cb_main_trcopy_activate(GtkWidget *widget, gpointer data)
1910 {
1911     GtkClipboard *cb;
1912     GtkTreeSelection *sel;
1913     GtkTreeModel *model;
1914     GtkTreeIter iter;
1915     char *hash;
1916     
1917     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_downloads));
1918     if(!gtk_tree_selection_get_selected(sel, &model, &iter))
1919         return;
1920     gtk_tree_model_get(model, &iter, 12, &hash, -1);
1921     cb = gtk_clipboard_get(gdk_atom_intern("PRIMARY", FALSE));
1922     gtk_clipboard_set_text(cb, hash, -1);
1923     g_free(hash);
1924 }
1925
1926 void cb_main_trreset_activate(GtkWidget *widget, gpointer data)
1927 {
1928     GtkTreeSelection *sel;
1929     GtkTreeModel *model;
1930     GtkTreeIter iter;
1931     int id, tag;
1932     struct dc_response *resp;
1933     
1934     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_downloads));
1935     if(gtk_tree_selection_get_selected(sel, &model, &iter))
1936     {
1937         gtk_tree_model_get(model, &iter, 0, &id, -1);
1938         tag = dc_queuecmd(NULL, NULL, L"reset", L"%i", id, NULL);
1939         if((resp = dc_gettaggedrespsync(tag)) != NULL)
1940         {
1941             if(resp->code == 502)
1942                 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1943             else if(resp->code != 200)
1944                 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to reset (%i)"), resp->code);
1945             dc_freeresp(resp);
1946         }
1947         handleresps();
1948     } else {
1949         return;
1950     }
1951 }
1952
1953 void cb_main_trcancel_activate(GtkWidget *widget, gpointer data)
1954 {
1955     GtkTreeSelection *sel;
1956     GtkTreeModel *model;
1957     GtkTreeIter iter;
1958     int id, tag;
1959     struct dc_response *resp;
1960     
1961     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(main_downloads));
1962     if(gtk_tree_selection_get_selected(sel, &model, &iter))
1963     {
1964         gtk_tree_model_get(model, &iter, 0, &id, -1);
1965         tag = dc_queuecmd(NULL, NULL, L"cancel", L"%i", id, NULL);
1966         if((resp = dc_gettaggedrespsync(tag)) != NULL)
1967         {
1968             if(resp->code == 502)
1969                 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You do not have permission to do that"));
1970             else if(resp->code != 200)
1971                 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred while trying to cancel (%i)"), resp->code);
1972             dc_freeresp(resp);
1973         }
1974         handleresps();
1975     } else {
1976         return;
1977     }
1978 }
1979
1980 /* XXX: This is quite a hack, since the calling convention is
1981  * different for the popup-menu sig and the button-press-event sig. It
1982  * most certainly works, but I don't know how portable it is. */
1983 gboolean cb_main_srpopup(GtkWidget *widget, GdkEventButton *event, gpointer data)
1984 {
1985     GtkTreeSelection *sel;
1986     GtkTreeModel *model;
1987     GtkTreeIter iter;
1988     char *hash;
1989     
1990     if((event != NULL) && (event->button != 3))
1991         return(FALSE);
1992     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
1993     if(gtk_tree_selection_get_selected(sel, &model, &iter))
1994     {
1995         gtk_tree_model_get(model, &iter, 9, &hash, -1);
1996         if((hash == NULL) || (*hash == 0))
1997         {
1998             gtk_widget_set_sensitive(main_srhash, FALSE);
1999             gtk_widget_set_sensitive(main_srcopy, FALSE);
2000         } else {
2001             if(nextsrch == -1)
2002                 gtk_widget_set_sensitive(main_srhash, TRUE);
2003             else
2004                 gtk_widget_set_sensitive(main_srhash, FALSE);
2005             gtk_widget_set_sensitive(main_srcopy, TRUE);
2006         }
2007         g_free(hash);
2008     } else {
2009         return(FALSE);
2010     }
2011     if(event == NULL)
2012         gtk_menu_popup(GTK_MENU(main_srpopup), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
2013     else
2014         gtk_menu_popup(GTK_MENU(main_srpopup), NULL, NULL, NULL, NULL, event->button, event->time);
2015     return(FALSE);
2016 }
2017
2018 /* The above hack note goes for this one too. */
2019 gboolean cb_main_trpopup(GtkWidget *widget, GdkEventButton *event, gpointer data)
2020 {
2021     GtkTreeSelection *sel;
2022     GtkTreeModel *model;
2023     GtkTreeIter iter;
2024     char *hash;
2025     
2026     if((event != NULL) && (event->button != 3))
2027         return(FALSE);
2028     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
2029     if(gtk_tree_selection_get_selected(sel, &model, &iter))
2030     {
2031         gtk_tree_model_get(model, &iter, 12, &hash, -1);
2032         if((hash == NULL) || (*hash == 0))
2033         {
2034             gtk_widget_set_sensitive(main_trhash, FALSE);
2035             gtk_widget_set_sensitive(main_trcopy, FALSE);
2036         } else {
2037             if(nextsrch == -1)
2038                 gtk_widget_set_sensitive(main_trhash, TRUE);
2039             else
2040                 gtk_widget_set_sensitive(main_trhash, FALSE);
2041             gtk_widget_set_sensitive(main_trcopy, TRUE);
2042         }
2043         g_free(hash);
2044     } else {
2045         return(FALSE);
2046     }
2047     if(event == NULL)
2048         gtk_menu_popup(GTK_MENU(main_trpopup), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time());
2049     else
2050         gtk_menu_popup(GTK_MENU(main_trpopup), NULL, NULL, NULL, NULL, event->button, event->time);
2051     return(FALSE);
2052 }
2053
2054 void cb_reslist_reload_clicked(GtkWidget *widget, gpointer data)
2055 {
2056     if(lsrestag != -1)
2057         return;
2058     gtk_widget_set_sensitive(reslist_delete, FALSE);
2059     gtk_widget_set_sensitive(reslist_search, FALSE);
2060     gtk_list_store_clear(reslist);
2061     lsrestag = dc_queuecmd(NULL, NULL, L"filtercmd", L"lsres", NULL);
2062     gtk_widget_set_sensitive(reslist_reload, FALSE);
2063     updatewrite();
2064 }
2065
2066 int rmres(char *id)
2067 {
2068     int tag, ret;
2069     struct dc_response *resp;
2070     
2071     ret = -1;
2072     tag = dc_queuecmd(NULL, NULL, L"filtercmd", L"rmres", L"%s", id, NULL);
2073     if((resp = dc_gettaggedrespsync(tag)) != NULL)
2074     {
2075         if(resp->numlines > 0)
2076         {
2077             if(!wcscmp(resp->rlines[0].argv[1], L"ok"))
2078                 ret = 0;
2079             else if(!wcsncmp(resp->rlines[0].argv[1], L"err:", 4))
2080                 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("An error occurred (%ls)"), resp->rlines[0].argv[1] + 4);
2081         }
2082         dc_freeresp(resp);
2083     }
2084     handleresps();
2085     return(ret);
2086 }
2087
2088 void cb_reslist_delete_clicked(GtkWidget *widget, gpointer data)
2089 {
2090     GtkTreeIter iter;
2091     GtkTreeModel *model;
2092     char *id;
2093     gboolean locked;
2094     
2095     if(nextsrch != -1)
2096         return;
2097     if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(reslist_list)), &model, &iter))
2098         return;
2099     gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 0, &id, 4, &locked, -1);
2100     if(locked)
2101     {
2102         g_free(id);
2103         return;
2104     }
2105     if(!rmres(id))
2106         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
2107     g_free(id);
2108 }
2109
2110 void cb_reslist_search_clicked(GtkWidget *widget, gpointer data)
2111 {
2112     GtkTreeIter iter;
2113     GtkTreeModel *model;
2114     char *hash, *buf;
2115     
2116     if(nextsrch != -1)
2117         return;
2118     if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(reslist_list)), &model, &iter))
2119         return;
2120     gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 5, &hash, -1);
2121     buf = sprintf2("H=%s", hash);
2122     gtk_entry_set_text(GTK_ENTRY(main_realsrch), buf);
2123     free(buf);
2124     g_free(hash);
2125     cb_main_srchbtn_clicked(widget, NULL);
2126 }
2127
2128 void cb_reslist_list_cchange(GtkWidget *widget, gpointer data)
2129 {
2130     GtkTreeIter iter;
2131     GtkTreeModel *model;
2132     gboolean locked;
2133     char *hash;
2134     
2135     if(!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(reslist_list)), &model, &iter))
2136     {
2137         gtk_widget_set_sensitive(reslist_delete, FALSE);
2138         gtk_widget_set_sensitive(reslist_search, FALSE);
2139         return;
2140     }
2141     gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 4, &locked, 5, &hash, -1);
2142     gtk_widget_set_sensitive(reslist_delete, !locked);
2143     gtk_widget_set_sensitive(reslist_search, hash && *hash);
2144     g_free(hash);
2145 }
2146
2147 void cb_reslist_list_activate(GtkWidget *widget, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data)
2148 {
2149     GtkTreeIter iter;
2150     GtkTreeModel *model;
2151     char *hash, *buf;
2152
2153     model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
2154     if(!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path))
2155         return;
2156     if(nextsrch != -1)
2157         return;
2158     gtk_tree_model_get(model, &iter, 5, &hash, -1);
2159     buf = sprintf2("H=%s", hash);
2160     gtk_entry_set_text(GTK_ENTRY(main_realsrch), buf);
2161     free(buf);
2162     g_free(hash);
2163     cb_main_srchbtn_clicked(widget, NULL);
2164 }
2165
2166 gboolean cb_reslist_list_keypress(GtkWidget *widget, GdkEventKey *event, gpointer data)
2167 {
2168     GtkTreeSelection *sel;
2169     GtkTreeModel *model;
2170     GtkTreeIter iter;
2171     char *id;
2172     gboolean locked;
2173     
2174     if((event->type == GDK_KEY_PRESS) && (event->keyval == GDK_Delete))
2175     {
2176         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
2177         if(gtk_tree_selection_get_selected(sel, &model, &iter))
2178         {
2179             gtk_tree_model_get(model, &iter, 0, &id, 4, &locked, -1);
2180             if(!locked)
2181             {
2182                 if(!rmres(id))
2183                     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
2184             }
2185             g_free(id);
2186         }
2187         return(TRUE);
2188     }
2189     return(FALSE);
2190 }
2191
2192 void srchstatupdate(void)
2193 {
2194     char buf[1024];
2195     
2196     if(nextsrch == -1)
2197     {
2198         snprintf(buf, 1024, _("Ready to search"));
2199     } else {
2200         snprintf(buf, 1024, _("Search scheduled and will be submitted in %i seconds"), (int)(srcheta - time(NULL)));
2201     }
2202     if(strcmp(gtk_label_get_text(GTK_LABEL(main_srchstatus)), buf))
2203         gtk_label_set_text(GTK_LABEL(main_srchstatus), buf);
2204 }
2205
2206 gint srchstatupdatecb(gpointer data)
2207 {
2208     srchstatupdate();
2209     return(TRUE);
2210 }
2211
2212 void initchattags(void)
2213 {
2214     GtkTextTag *tag;
2215     
2216     chattags = gtk_text_tag_table_new();
2217     tag = gtk_text_tag_new("sender");
2218     g_object_set(tag, "foreground", "blue", NULL);
2219     gtk_text_tag_table_add(chattags, tag);
2220 }
2221
2222 #include "../dolda-icon.xpm"
2223
2224 int main(int argc, char **argv)
2225 {
2226     int c, connlocal;
2227     GtkWidget *wnd;
2228     PangoFontDescription *monospacefont;
2229     GtkTreeModel *sortmodel;
2230     struct passwd *pwent;
2231     
2232     setlocale(LC_ALL, "");
2233     bindtextdomain(PACKAGE, LOCALEDIR);
2234     textdomain(PACKAGE);
2235     gtk_init(&argc, &argv);
2236     connlocal = 0;
2237     while((c = getopt(argc, argv, "lhV")) != -1) {
2238         switch(c) {
2239         case 'l':
2240             connlocal = 1;
2241             break;
2242         case 'h':
2243             printf("usage: dolcon [-hlV]\n");
2244             printf("\t-h\tDisplay this help message\n");
2245             printf("\t-l\tConnect to the locally running daemon\n");
2246             printf("\t-V\tDisplay version info and exit\n");
2247             exit(0);
2248         case 'V':
2249             printf("%s", RELEASEINFO);
2250             exit(0);
2251         default:
2252             fprintf(stderr, "usage: dolcon [-hlV]\n");
2253             exit(1);
2254         }
2255     }
2256     dc_init();
2257     signal(SIGCHLD, SIG_IGN);
2258     pubhubaddr = sstrdup("http://dchublist.com/hublist.xml.bz2");
2259     dcserver = sstrdup("");
2260     if((pwent = getpwuid(getuid())) == NULL)
2261     {
2262         fprintf(stderr, "could not get your passwd data");
2263         exit(1);
2264     }
2265     connectas = sstrdup(pwent->pw_name);
2266     gtk_window_set_default_icon(gdk_pixbuf_new_from_xpm_data((const char **)dolda_icon_xpm));
2267     wnd = create_main_wnd();
2268     create_reslist_wnd();
2269     gtk_window_resize(GTK_WINDOW(reslist_wnd), 600, 400);
2270     initchattags();
2271
2272     fnmodel = gtk_list_store_new(6, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
2273     gtk_tree_view_set_model(GTK_TREE_VIEW(main_fnetnodes), GTK_TREE_MODEL(fnmodel));
2274     gtk_tree_view_set_model(GTK_TREE_VIEW(main_chatnodes), GTK_TREE_MODEL(fnmodel));
2275     
2276     reslist = gtk_list_store_new(6, G_TYPE_STRING, G_TYPE_INT64, G_TYPE_INT64, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING);
2277     gtk_tree_view_set_model(GTK_TREE_VIEW(reslist_list), GTK_TREE_MODEL(reslist));
2278     
2279     dlmodel = gtk_list_store_new(13, G_TYPE_INT, /* id */
2280                                  G_TYPE_INT,     /* dir */
2281                                  G_TYPE_INT,     /* state */
2282                                  G_TYPE_STRING,  /* peerid */
2283                                  G_TYPE_STRING,  /* peernick */
2284                                  G_TYPE_STRING,  /* path */
2285                                  G_TYPE_INT64,   /* size */
2286                                  G_TYPE_INT64,   /* curpos */
2287                                  G_TYPE_STRING,  /* stock */
2288                                  G_TYPE_FLOAT,   /* percentage */
2289                                  G_TYPE_INT,     /* error */
2290                                  G_TYPE_INT64,   /* errortime */
2291                                  G_TYPE_STRING); /* hash */
2292     gtk_tree_view_set_model(GTK_TREE_VIEW(main_downloads), GTK_TREE_MODEL(dlmodel));
2293
2294     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);
2295     gtk_tree_view_set_model(GTK_TREE_VIEW(main_uploads), GTK_TREE_MODEL(ulmodel));
2296
2297     srchmodel = gtk_tree_store_new(10, G_TYPE_STRING, /* fnetname */
2298                                    G_TYPE_STRING,     /* peerid */
2299                                    G_TYPE_STRING,     /* peername */
2300                                    G_TYPE_STRING,     /* filename */
2301                                    G_TYPE_INT64,      /* size */
2302                                    G_TYPE_INT,        /* slots */
2303                                    G_TYPE_DOUBLE,     /* resptime */
2304                                    G_TYPE_INT,        /* sizenum */
2305                                    G_TYPE_INT,        /* speed */
2306                                    G_TYPE_STRING);    /* hash */
2307     srchmodelfilter = GTK_TREE_MODEL_FILTER(gtk_tree_model_filter_new(GTK_TREE_MODEL(srchmodel), NULL));
2308     gtk_tree_model_filter_set_visible_func(srchmodelfilter, srchfilterfunc, NULL, NULL);
2309     sortmodel = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(srchmodelfilter));
2310     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(sortmodel), 4, GTK_SORT_DESCENDING);
2311     gtk_tree_view_set_model(GTK_TREE_VIEW(main_srchres), GTK_TREE_MODEL(sortmodel));
2312     g_object_unref(sortmodel);
2313
2314     monospacefont = pango_font_description_from_string("Monospace 10");
2315     gtk_widget_modify_font(main_chatview, monospacefont);
2316     pango_font_description_free(monospacefont);
2317     readconfigfile();
2318     updatesbar(_("Disconnected"));
2319     gtk_widget_show(wnd);
2320     if(connlocal)
2321         dcconnect(dc_srv_local);
2322     else if(autoconn)
2323         dcconnect(dcserver);
2324     g_timeout_add(500, srchstatupdatecb, NULL);
2325     g_timeout_add(5000, ksupdatecb, NULL);
2326     g_timeout_add(1000, updatetransfers, NULL);
2327     gtk_main();
2328     return(0);
2329 }
2330
2331 #include "mainwnd.gtk"
2332 #include "inpdialog.gtk"
2333 #include "pref.gtk"
2334 #include "reslist.gtk"