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