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