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