Fix line-wrapping bug in baseconv.
[doldaconnect.git] / config / dolconf.c
1 /*
2  *  Dolda Connect - Modular multiuser Direct Connect-style client
3  *  Copyright (C) 2007 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 <stdlib.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <signal.h>
26 #include <errno.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <locale.h>
30 #include <libintl.h>
31 #include <pwd.h>
32 #include <stdarg.h>
33 #include <arpa/inet.h>
34 #include <doldaconnect/uilib.h>
35 #include <doldaconnect/uimisc.h>
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40 #include <utils.h>
41
42 struct validation {
43     int (*check)(const char *val);
44     char *invmsg;
45 };
46
47 struct cfvar {
48     char *name;
49     char *rname;
50     char *val;
51     struct validation *vld;
52     GtkWidget **astw, **cfww;
53 };
54
55 char *cfname;
56 GtkWindow *rootwnd = NULL;
57 GtkListStore *shares;
58 int state, dirty = 1;
59 int ignoreclose = 0;
60
61 void astcancel(GtkWidget *widget, gpointer uudata);
62 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata);
63 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata);
64 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata);
65 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata);
66 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata);
67 void cb_ast_checkports(GtkWidget *widget, gpointer uudata);
68 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata);
69 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata);
70 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata);
71 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata);
72 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata);
73 void cb_cfw_save_activate(GtkWidget *widget, gpointer uudata);
74 void cb_cfw_hup_activate(GtkWidget *widget, gpointer uudata);
75 void cb_cfw_quit_activate(GtkWidget *widget, gpointer uudata);
76 void cb_cfw_shareadd_clicked(GtkWidget *widget, gpointer uudata);
77 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata);
78
79 #define _(text) gettext(text)
80
81 #include "dolconf-assistant.gtk"
82 #include "dolconf-wnd.gtk"
83
84 int v_nonempty(const char *val)
85 {
86     return(strlen(val) > 0);
87 }
88
89 int v_dcstring(const char *val)
90 {
91     return((strchr(val, ' ') == NULL) &&
92            (strchr(val, '|') == NULL) &&
93            (strchr(val, '$') == NULL));
94 }
95
96 int v_natural(const char *val)
97 {
98     if(!*val)
99         return(0);
100     for(; *val; val++) {
101         if(!isdigit(*val))
102             return(0);
103     }
104     return(1);
105 }
106
107 int v_integer(const char *val)
108 {
109     int f, d;
110     
111     for(f = 1, d = 0; *val; val++, f = 0) {
112         if(isdigit(*val)) {
113             d = 1;
114         } else if(f && (*val == '-')) {
115         } else {
116             return(0);
117         }
118     }
119     return(d);
120 }
121
122 int v_ipv4(const char *val)
123 {
124     struct in_addr buf;
125     
126     return(inet_aton(val, &buf) != 0);
127 }
128
129 #undef _
130 #define _(text) text
131
132 struct validation nonempty = {
133     .check = v_nonempty,
134     .invmsg = _("%s must not be empty"),
135 };
136
137 struct validation dcstring = {
138     .check = v_dcstring,
139     .invmsg = _("%s must not contain spaces, `|' or `$'"),
140 };
141
142 struct validation natural = {
143     .check = v_natural,
144     .invmsg = _("%s must be a natural number"),
145 };
146
147 struct validation integer = {
148     .check = v_integer,
149     .invmsg = _("%s must be an integer"),
150 };
151
152 struct validation ipv4 = {
153     .check = v_ipv4,
154     .invmsg = _("%s must be an IP address"),
155 };
156
157 struct validation *vldxlate[] = {
158     &nonempty, &dcstring, &natural, &integer, &ipv4,
159     NULL
160 };
161
162 struct cfvar config[] = {
163     {"cli.defnick", _("Screen name"), "", &dcstring, &ast_nick, &cfw_nick},
164     {"net.mode", NULL, "0", &natural},
165     {"net.visibleipv4", "IP address", "0.0.0.0", &ipv4, NULL, &cfw_extip},
166     {"ui.onlylocal", NULL, "0", &natural},
167     {"ui.port", NULL, "-1", &integer},
168     {"auth.authless", NULL, "0", &natural},
169     {"transfer.slots", _("Upload slots"), "6", &natural},
170     {"dc.speedstring", _("Connection speed"), "DSL", &dcstring, NULL, &cfw_cntype},
171     {"dc.email", _("E-mail address"), "spam@spam.net", &dcstring, NULL, &cfw_mail},
172     {"dc.desc", _("Share description"), "", NULL, &ast_desc, &cfw_desc},
173     {"dc.tcpport", _("Direct Connect TCP port"), "0", &natural, NULL, &cfw_tcpport},
174     {"dc.udpport", _("Direct Connect UDP port"), "0", &natural, NULL, &cfw_udpport},
175     {NULL}
176 };
177
178 #undef _
179 #define _(text) gettext(text)
180
181 struct cfvar *findcfvar(char *name)
182 {
183     struct cfvar *v;
184     
185     for(v = config; v->name != NULL; v++) {
186         if(!strcmp(v->name, name))
187             return(v);
188     }
189     return(NULL);
190 }
191
192 void setcfvar(char *name, const char *val)
193 {
194     struct cfvar *v;
195     
196     v = findcfvar(name);
197     if(!strcmp(v->val, val))
198         return;
199     free(v->val);
200     v->val = sstrdup(val);
201     dirty = 1;
202 }
203
204 int msgbox(int type, int buttons, char *format, ...)
205 {
206     GtkWidget *swnd;
207     va_list args;
208     char *buf;
209     int resp;
210     
211     va_start(args, format);
212     buf = vsprintf2(format, args);
213     va_end(args);
214     swnd = gtk_message_dialog_new(rootwnd, GTK_DIALOG_MODAL, type, buttons, "%s", buf);
215     gtk_window_set_title(GTK_WINDOW(swnd), _("Dolda Connect configurator"));
216     resp = gtk_dialog_run(GTK_DIALOG(swnd));
217     gtk_widget_destroy(swnd);
218     free(buf);
219     return(resp);
220 }
221
222 void prepstatic(void)
223 {
224     struct validation **v;
225     struct cfvar *c;
226     
227     for(v = vldxlate; *v != NULL; v++)
228         (*v)->invmsg = gettext((*v)->invmsg);
229     for(c = config; c->name != NULL; c++) {
230         if(c->rname != NULL)
231             c->rname = gettext(c->rname);
232         c->val = sstrdup(c->val);
233     }
234 }
235
236 char *getword(char **p)
237 {
238     char *buf, *p2, delim;
239     size_t len;
240     
241     if(**p == '\"')
242         delim = '\"';
243     else
244         delim = ' ';
245     p2 = *p;
246     while((p2 = strchr(p2 + 1, delim)) != NULL) {
247         if(p2[-1] != '\\')
248             break;
249     }
250     if(p2 == NULL)
251         p2 = *p + strlen(*p);
252     len = p2 - *p - ((*p2 == '\"')?1:0);
253     buf = smalloc(len + 1);
254     memcpy(buf, *p, len);
255     buf[len] = 0;
256     *p = p2 + ((*p2 == '\"')?1:0);
257     for(p2 = buf; *p2; p2++, len--) {
258         if(*p2 == '\\')
259             memmove(p2, p2 + 1, len--);
260     }
261     return(buf);
262 }
263
264 char *quoteword(char *word)
265 {
266     char *wp, *buf, *bp;
267     int dq, numbs, numc;
268     
269     dq = 0;
270     numbs = 0;
271     numc = 0;
272     if(*word == '\0')
273     {
274         dq = 1;
275     } else {
276         for(wp = word; *wp != '\0'; wp++)
277         {
278             if(!dq && isspace(*wp))
279                 dq = 1;
280             if((*wp == '\\') || (*wp == '\"'))
281                 numbs++;
282             numc++;
283         }
284     }
285     if(!dq && !numbs)
286         return(NULL);
287     bp = buf = smalloc(sizeof(wchar_t) * (numc + numbs + (dq?2:0) + 1));
288     if(dq)
289         *(bp++) = '\"';
290     for(wp = word; *wp != '\0'; wp++)
291     {
292         if((*wp == '\\') || (*wp == '\"'))
293             *(bp++) = '\\';
294         *(bp++) = *wp;
295     }
296     if(dq)
297         *(bp++) = '\"';
298     *(bp++) = '\0';
299     return(buf);
300 }
301
302 int readconfig(void)
303 {
304     int rv;
305     FILE *cf;
306     char lbuf[1024];
307     char *key, *val, *p;
308     size_t len;
309     struct cfvar *var;
310     GtkTreeIter iter;
311     
312     rv = 0;
313     if((cf = fopen(cfname, "r")) == NULL) {
314         msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not open the configuration file for reading: %s"), strerror(errno));
315         return(-1);
316     }
317     key = val = NULL;
318     while(fgets(lbuf, sizeof(lbuf), cf) != NULL) {
319         len = strlen(lbuf);
320         if(lbuf[len - 1] == '\n')
321             lbuf[len - 1] = 0;
322         if(key != NULL) {
323             free(key);
324             key = NULL;
325         }
326         if(val != NULL) {
327             free(val);
328             val = NULL;
329         }
330         if(!strncmp(lbuf, "set ", 4)) {
331             p = lbuf + 4;
332             if(((key = getword(&p)) == NULL) || (*(p++) != ' ') || ((val = getword(&p)) == NULL)) {
333                 rv = 1;
334                 continue;
335             }
336             for(var = config; var->name != NULL; var++) {
337                 if(!strcmp(var->name, key)) {
338                     free(var->val);
339                     var->val = sstrdup(val);
340                     break;
341                 }
342             }
343             if(var->name == NULL)
344                 rv = 1;
345         } else if(!strncmp(lbuf, "share ", 6)) {
346             p = lbuf + 6;
347             if(((key = getword(&p)) == NULL) || (*(p++) != ' ') || ((val = getword(&p)) == NULL)) {
348                 rv = 1;
349                 continue;
350             }
351             gtk_list_store_append(shares, &iter);
352             gtk_list_store_set(shares, &iter, 0, key, 1, val, -1);
353         } else if(!lbuf[0] || lbuf[0] == '#') {
354         } else {
355             rv = 1;
356         }
357     }
358     if(key != NULL)
359         free(key);
360     if(val != NULL)
361         free(val);
362     fclose(cf);
363     dirty = 0;
364     return(rv);
365 }
366
367 void writeconfig(void)
368 {
369     FILE *cf;
370     struct cfvar *var;
371     GtkTreeIter iter;
372     char *buf, *buf2;
373     
374     if((cf = fopen(cfname, "w")) == NULL) {
375         msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not open the configuration file for writing: %s"), strerror(errno));
376         return;
377     }
378     fputs("# This file was generated by dolconf v" VERSION "\n\n", cf);
379     for(var = config; var->name != NULL; var++) {
380         fprintf(cf, "set %s ", var->name);
381         if((buf = quoteword(var->val)) == NULL) {
382             fputs(var->val, cf);
383         } else {
384             fputs(buf, cf);
385             free(buf);
386         }
387         fputc('\n', cf);
388     }
389     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
390         fputc('\n', cf);
391         do {
392             fputs("share ", cf);
393             gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 0, &buf2, -1);
394             if((buf = quoteword(buf2)) == NULL) {
395                 fputs(buf2, cf);
396             } else {
397                 fputs(buf, cf);
398                 free(buf);
399             }
400             g_free(buf2);
401             fputc(' ', cf);
402             gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf2, -1);
403             if((buf = quoteword(buf2)) == NULL) {
404                 fputs(buf2, cf);
405             } else {
406                 fputs(buf, cf);
407                 free(buf);
408             }
409             g_free(buf2);
410             fputc('\n', cf);
411         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
412     }
413     fclose(cf);
414     dirty = 0;
415 }
416
417 void fillcfw(void)
418 {
419     struct cfvar *var;
420     
421     for(var = config; var->name != NULL; var++) {
422         if(var->cfww != NULL)
423             gtk_entry_set_text(GTK_ENTRY(*(var->cfww)), var->val);
424     }
425     if(atoi(findcfvar("dc.tcpport")->val) || atoi(findcfvar("dc.udpport")->val))
426         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_orport), TRUE);
427     else
428         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_orport), FALSE);
429     if(strcmp(findcfvar("net.visibleipv4")->val, "0.0.0.0"))
430         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_oraddr), TRUE);
431     else
432         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_oraddr), FALSE);
433     if(strcmp(findcfvar("ui.port")->val, "-1")) {
434         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_uinet), TRUE);
435         if(strcmp(findcfvar("auth.authless")->val, "1"))
436             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), FALSE);
437         else
438             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), TRUE);
439     } else {
440         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_uinet), FALSE);
441         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), FALSE);
442     }
443 }
444
445 void ast2conf(void)
446 {
447     setcfvar("cli.defnick", gtk_entry_get_text(GTK_ENTRY(ast_nick)));
448     setcfvar("dc.desc", gtk_entry_get_text(GTK_ENTRY(ast_desc)));
449     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_mode_psv))) {
450         setcfvar("net.mode", "1");
451     } else {
452         setcfvar("net.mode", "0");
453         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_mode_nat))) {
454             setcfvar("net.visibleipv4", gtk_entry_get_text(GTK_ENTRY(ast_extip)));
455             setcfvar("dc.tcpport", gtk_entry_get_text(GTK_ENTRY(ast_udpport)));
456             setcfvar("dc.udpport", gtk_entry_get_text(GTK_ENTRY(ast_tcpport)));
457         } else {
458             setcfvar("net.visibleipv4", "0.0.0.0");
459             setcfvar("dc.tcpport", "0");
460             setcfvar("dc.udpport", "0");
461         }
462     }
463 }
464
465 void cfw2conf(void)
466 {
467     struct cfvar *var;
468     const char *val;
469     
470     for(var = config; var->name != NULL; var++) {
471         if(var->cfww != NULL) {
472             val = gtk_entry_get_text(GTK_ENTRY(*(var->cfww)));
473             if(!strcmp(var->val, val))
474                 continue;
475             free(var->val);
476             var->val = sstrdup(val);
477             dirty = 1;
478         }
479     }
480     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_mode_act))) {
481         setcfvar("net.mode", "0");
482         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_orport))) {
483             setcfvar("dc.tcpport", "0");
484             setcfvar("dc.udpport", "0");
485         }
486         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_oraddr))) {
487             setcfvar("net.visibleipv4", "0.0.0.0");
488         }
489     } else {
490         setcfvar("net.mode", "1");
491     }
492     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_uinet))) {
493         setcfvar("ui.port", "1500");
494         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_authless)))
495             setcfvar("auth.authless", "1");
496         else
497             setcfvar("auth.authless", "0");
498     } else {
499         setcfvar("ui.port", "-1");
500         setcfvar("auth.authless", "0");
501     }
502 }
503
504 struct cfvar *cfwvalid(void)
505 {
506     struct cfvar *cv;
507     
508     for(cv = config; cv->name != NULL; cv++) {
509         if((cv->vld != NULL) && !cv->vld->check(cv->val)) {
510             if(cv->rname) {
511                 return(cv);
512             } else {
513                 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Auto-generated variable %s has an invalid value \"%s\")"), cv->name, cv->val);
514                 abort();
515             }
516         }
517     }
518     return(NULL);
519 }
520
521 void astcancel(GtkWidget *widget, gpointer uudata)
522 {
523     if(ignoreclose)
524         return;
525     gtk_main_quit();
526     state = -1;
527 }
528
529 #define bufcats(buf, str) bufcat(buf, str, strlen(str))
530
531 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata)
532 {
533     char *s, *buf;
534     size_t sdata, ssize;
535     struct cfvar *var;
536     GtkTreeIter iter;
537     
538     ast2conf();
539     s = NULL;
540     sdata = ssize = 0;
541     for(var = config; var->name != NULL; var++) {
542         if(var->rname == NULL)
543             continue;
544         bufcats(s, var->rname);
545         bufcats(s, ": ");
546         bufcat(s, var->val, strlen(var->val));
547         addtobuf(s, '\n');
548     }
549     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
550         addtobuf(s, '\n');
551         bufcats(s, _("Shares:\n"));
552         do {
553             addtobuf(s, '\t');
554             gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf, -1);
555             bufcats(s, buf);
556             g_free(buf);
557             addtobuf(s, '\n');
558         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
559     }
560     gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(ast_summary)), s, sdata);
561     free(s);
562 }
563
564 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata)
565 {
566     writeconfig();
567     gtk_main_quit();
568     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_dolcon)))
569         state = 2;
570     else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_exit)))
571         state = -1;
572     else
573         state = 0;
574     ignoreclose = 1;
575 }
576
577 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata)
578 {
579     if(v_dcstring(gtk_entry_get_text(GTK_ENTRY(ast_nick))))
580         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, TRUE);
581     else
582         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, FALSE);
583 }
584
585 int hasshare(int col, char *name)
586 {
587     GtkTreeIter iter;
588     char *buf;
589     
590     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
591         do {
592             gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, col, &buf, -1);
593             if(!strcmp(buf, name)) {
594                 g_free(buf);
595                 return(1);
596             }
597             g_free(buf);
598         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
599     }
600     return(0);
601 }
602
603 int shareadd(void)
604 {
605     int i, ret;
606     GSList *fns, *next;
607     char *fn, *sn, *p;
608     GtkTreeIter iter;
609     GtkWidget *chd;
610     int resp;
611     
612     chd = gtk_file_chooser_dialog_new(_("Shared directories"), rootwnd, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
613     gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(chd), TRUE);
614     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chd), TRUE);
615     resp = gtk_dialog_run(GTK_DIALOG(chd));
616     if(resp != GTK_RESPONSE_ACCEPT) {
617         gtk_widget_destroy(chd);
618         return(0);
619     }
620     ret = 0;
621     fns = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chd));
622     gtk_widget_destroy(chd);
623     while(fns != NULL) {
624         fn = fns->data;
625         if(!hasshare(1, fn)) {
626             if((p = strrchr(fn, '/')) == NULL)
627                 p = fn;
628             else
629                 p++;
630             sn = sstrdup(p);
631             if(hasshare(0, sn)) {
632                 for(i = 2; 1; i++) {
633                     free(sn);
634                     sn = sprintf2("%s%i", p, i);
635                     if(!hasshare(0, sn))
636                         break;
637                 }
638             }
639             gtk_list_store_append(shares, &iter);
640             gtk_list_store_set(shares, &iter, 0, sn, 1, fn, -1);
641             free(sn);
642             ret = 1;
643         }
644         g_free(fn);
645         next = fns->next;
646         g_slist_free_1(fns);
647         fns = next;
648     }
649     return(ret);
650 }
651
652 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata)
653 {
654     if(shareadd())
655         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, TRUE);
656 }
657
658 void cb_cfw_shareadd_clicked(GtkWidget *widget, gpointer uudata)
659 {
660     shareadd();
661 }
662
663 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata)
664 {
665     GtkTreeIter iter;
666     
667     if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(ast_sharelist)), NULL, &iter))
668         gtk_list_store_remove(shares, &iter);
669     if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(shares), NULL) == 0)
670         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, FALSE);
671 }
672
673 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata)
674 {
675     GtkTreeIter iter;
676     
677     if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(cfw_sharelist)), NULL, &iter))
678         gtk_list_store_remove(shares, &iter);
679 }
680
681 void cb_ast_checkports(GtkWidget *widget, gpointer uudata)
682 {
683     gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3,
684                                     v_natural(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) &&
685                                     (atoi(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) >= 1024) &&
686                                     v_natural(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) &&
687                                     (atoi(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) >= 1024) &&
688                                     v_ipv4(gtk_entry_get_text(GTK_ENTRY(ast_extip))));
689 }
690
691 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata)
692 {
693     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
694         gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), TRUE);
695         cb_ast_checkports(widget, NULL);
696     } else {
697         gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), FALSE);
698         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3, TRUE);
699     }
700 }
701
702 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata)
703 {
704     gtk_widget_set_sensitive(GTK_WIDGET(cfw_natbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
705 }
706
707 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata)
708 {
709     gtk_widget_set_sensitive(GTK_WIDGET(cfw_portbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
710 }
711
712 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata)
713 {
714     gtk_widget_set_sensitive(GTK_WIDGET(cfw_addrbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
715 }
716
717 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata)
718 {
719     gtk_widget_set_sensitive(GTK_WIDGET(cfw_uibox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
720 }
721
722 void cb_cfw_hup_activate(GtkWidget *widget, gpointer uudata)
723 {
724     int tag;
725     struct dc_response *resp;
726     
727     if(dc_connectsync2(dc_srv_local, DC_LATEST) < 0) {
728         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
729         return;
730     }
731     if(dc_login(NULL, 1, dc_convnone, NULL) != DC_LOGIN_ERR_SUCCESS) {
732         dc_disconnect();
733         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
734         return;
735     }
736     tag = dc_queuecmd(NULL, NULL, L"hup", NULL);
737     if((resp = dc_gettaggedrespsync(tag)) != NULL) {
738         if(resp->code != 200)
739             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
740         dc_freeresp(resp);
741     }
742     dc_disconnect();
743 }
744
745 void cb_cfw_save_activate(GtkWidget *widget, gpointer uudata)
746 {
747     struct cfvar *cv;
748     
749     if((cv = cfwvalid()) != NULL) {
750         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, cv->vld->invmsg, cv->rname);
751         return;
752     }
753     cfw2conf();
754     writeconfig();
755 }
756
757 void cb_cfw_quit_activate(GtkWidget *widget, gpointer uudata)
758 {
759     cfw2conf();
760     if(dirty) {
761         if(msgbox(GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("There are unsaved changes. Do you wish to discard the changes and exit anyway?")) == GTK_RESPONSE_NO)
762             return;
763     }
764     gtk_main_quit();
765     state = -1;
766 }
767
768 int main(int argc, char **argv)
769 {
770     int i, c, ex;
771     struct passwd *pwd;
772     
773     setlocale(LC_ALL, "");
774     bindtextdomain(PACKAGE, LOCALEDIR);
775     textdomain(PACKAGE);
776     prepstatic();
777     dc_init();
778     
779     gtk_init(&argc, &argv);
780     state = -1;
781     while((c = getopt(argc, argv, "haw")) != -1) {
782         switch(c) {
783         case 'a':
784             state = 1;
785             break;
786         case 'w':
787             state = 0;
788             break;
789         case 'h':
790             printf("usage: dolconf [-haw]\n");
791             printf("\t-h\tDisplay this help message\n");
792             printf("\t-a\tGo directly to the assistant\n");
793             printf("\t-w\tGo directly to the standard window\n");
794             exit(0);
795         default:
796             fprintf(stderr, "usage: dolconf [-haw]\n");
797             exit(1);
798         }
799     }
800     
801     create_ast_wnd();
802     create_cfw_wnd();
803     shares = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
804     gtk_tree_view_set_model(GTK_TREE_VIEW(ast_sharelist), GTK_TREE_MODEL(shares));
805     gtk_tree_view_set_model(GTK_TREE_VIEW(cfw_sharelist), GTK_TREE_MODEL(shares));
806     
807     cfname = NULL;
808     if(getenv("HOME") != NULL) {
809         cfname = sprintf2("%s/.doldacond.conf", getenv("HOME"));
810     } else {
811         if((pwd = getpwuid(getuid())) != NULL)
812             cfname = sprintf2("%s/.doldacond.conf", pwd->pw_dir);
813     }
814     if(cfname == NULL) {
815         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
816         exit(1);
817     }
818     
819     ex = !access(cfname, F_OK);
820     if(state == -1) {
821         if(!ex) {
822             if(msgbox(GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("It appears that you have not run this setup program before. Would you like to run the first-time setup assistant?")) == GTK_RESPONSE_YES)
823                 state = 1;
824             else
825                 state = 0;
826         } else {
827             state = 0;
828         }
829     }
830     
831     if(ex && (state == 0)) {
832         if(readconfig() == 1) {
833             if(msgbox(GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("The configuration file appears to have been edited outside the control of this program. If you continue using this program, all settings not handled by it will be lost. Do you wish to continue?")) == GTK_RESPONSE_NO)
834                 exit(1);
835         }
836     }
837     
838     while(state != -1) {
839         if(state == 0) {
840             gtk_window_set_default_size(GTK_WINDOW(cfw_wnd), 500, 350);
841             gtk_widget_show(cfw_wnd);
842             fillcfw();
843             rootwnd = GTK_WINDOW(cfw_wnd);
844             gtk_main();
845             gtk_widget_hide(cfw_wnd);
846             rootwnd = NULL;
847         } else if(state == 1) {
848             gtk_window_set_default_size(GTK_WINDOW(ast_wnd), 500, 350);
849             gtk_widget_show(ast_wnd);
850             rootwnd = GTK_WINDOW(ast_wnd);
851             gtk_main();
852             gtk_widget_hide(ast_wnd);
853             ignoreclose = 0;
854             rootwnd = NULL;
855         } else if(state == 2) {
856             for(i = 3; i < FD_SETSIZE; i++)
857                 close(i);
858             execlp("dolcon", "dolcon", NULL);
859             perror("dolcon");
860             exit(127);
861         } else {
862             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Unknown state)"));
863             abort();
864         }
865     }
866     return(0);
867 }