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