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