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