Better wording.
[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 struct cfvar *cfwvalid(void)
502 {
503     struct cfvar *cv;
504     
505     for(cv = config; cv->name != NULL; cv++) {
506         if((cv->vld != NULL) && !cv->vld->check(cv->val)) {
507             if(cv->rname) {
508                 return(cv);
509             } else {
510                 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Auto-generated variable %s has an invalid value \"%s\")"), cv->name, cv->val);
511                 abort();
512             }
513         }
514     }
515     return(NULL);
516 }
517
518 void astcancel(GtkWidget *widget, gpointer uudata)
519 {
520     if(ignoreclose)
521         return;
522     gtk_main_quit();
523     state = -1;
524 }
525
526 #define bufcats(buf, str) bufcat(buf, str, strlen(str))
527
528 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata)
529 {
530     char *s, *buf;
531     size_t sdata, ssize;
532     struct cfvar *var;
533     GtkTreeIter iter;
534     
535     ast2conf();
536     s = NULL;
537     sdata = ssize = 0;
538     for(var = config; var->name != NULL; var++) {
539         if(var->rname == NULL)
540             continue;
541         bufcats(s, var->rname);
542         bufcats(s, ": ");
543         bufcat(s, var->val, strlen(var->val));
544         addtobuf(s, '\n');
545     }
546     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
547         addtobuf(s, '\n');
548         bufcats(s, _("Shares:\n"));
549         do {
550             addtobuf(s, '\t');
551             gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf, -1);
552             bufcats(s, buf);
553             g_free(buf);
554             addtobuf(s, '\n');
555         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
556     }
557     gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(ast_summary)), s, sdata);
558     free(s);
559 }
560
561 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata)
562 {
563     writeconfig();
564     gtk_main_quit();
565     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_dolcon)))
566         state = 2;
567     else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_exit)))
568         state = -1;
569     else
570         state = 0;
571     ignoreclose = 1;
572 }
573
574 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata)
575 {
576     if(v_dcstring(gtk_entry_get_text(GTK_ENTRY(ast_nick))))
577         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, TRUE);
578     else
579         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, FALSE);
580 }
581
582 int hasshare(int col, char *name)
583 {
584     GtkTreeIter iter;
585     char *buf;
586     
587     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
588         do {
589             gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, col, &buf, -1);
590             if(!strcmp(buf, name)) {
591                 g_free(buf);
592                 return(1);
593             }
594             g_free(buf);
595         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
596     }
597     return(0);
598 }
599
600 int shareadd(void)
601 {
602     int i, ret;
603     GSList *fns, *next;
604     char *fn, *sn, *p;
605     GtkTreeIter iter;
606     GtkWidget *chd;
607     int resp;
608     
609     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);
610     gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(chd), TRUE);
611     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chd), TRUE);
612     resp = gtk_dialog_run(GTK_DIALOG(chd));
613     if(resp != GTK_RESPONSE_ACCEPT) {
614         gtk_widget_destroy(chd);
615         return(0);
616     }
617     ret = 0;
618     fns = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chd));
619     gtk_widget_destroy(chd);
620     while(fns != NULL) {
621         fn = fns->data;
622         if(!hasshare(1, fn)) {
623             if((p = strrchr(fn, '/')) == NULL)
624                 p = fn;
625             else
626                 p++;
627             sn = sstrdup(p);
628             if(hasshare(0, sn)) {
629                 for(i = 2; 1; i++) {
630                     free(sn);
631                     sn = sprintf2("%s%i", p, i);
632                     if(!hasshare(0, sn))
633                         break;
634                 }
635             }
636             gtk_list_store_append(shares, &iter);
637             gtk_list_store_set(shares, &iter, 0, sn, 1, fn, -1);
638             free(sn);
639             ret = 1;
640         }
641         g_free(fn);
642         next = fns->next;
643         g_slist_free_1(fns);
644         fns = next;
645     }
646     return(ret);
647 }
648
649 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata)
650 {
651     if(shareadd())
652         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, TRUE);
653 }
654
655 void cb_cfw_shareadd_clicked(GtkWidget *widget, gpointer uudata)
656 {
657     shareadd();
658 }
659
660 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata)
661 {
662     GtkTreeIter iter;
663     
664     if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(ast_sharelist)), NULL, &iter))
665         gtk_list_store_remove(shares, &iter);
666     if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(shares), NULL) == 0)
667         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, FALSE);
668 }
669
670 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata)
671 {
672     GtkTreeIter iter;
673     
674     if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(cfw_sharelist)), NULL, &iter))
675         gtk_list_store_remove(shares, &iter);
676 }
677
678 void cb_ast_checkports(GtkWidget *widget, gpointer uudata)
679 {
680     gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3,
681                                     v_natural(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) &&
682                                     (atoi(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) >= 1024) &&
683                                     v_natural(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) &&
684                                     (atoi(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) >= 1024) &&
685                                     v_ipv4(gtk_entry_get_text(GTK_ENTRY(ast_extip))));
686 }
687
688 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata)
689 {
690     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
691         gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), TRUE);
692         cb_ast_checkports(widget, NULL);
693     } else {
694         gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), FALSE);
695         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3, TRUE);
696     }
697 }
698
699 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata)
700 {
701     gtk_widget_set_sensitive(GTK_WIDGET(cfw_natbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
702 }
703
704 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata)
705 {
706     gtk_widget_set_sensitive(GTK_WIDGET(cfw_portbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
707 }
708
709 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata)
710 {
711     gtk_widget_set_sensitive(GTK_WIDGET(cfw_addrbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
712 }
713
714 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata)
715 {
716     gtk_widget_set_sensitive(GTK_WIDGET(cfw_uibox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
717 }
718
719 void cb_cfw_save_activate(GtkWidget *widget, gpointer uudata)
720 {
721     struct cfvar *cv;
722     
723     if((cv = cfwvalid()) != NULL) {
724         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, cv->vld->invmsg, cv->rname);
725         return;
726     }
727     cfw2conf();
728     writeconfig();
729 }
730
731 void cb_cfw_quit_activate(GtkWidget *widget, gpointer uudata)
732 {
733     cfw2conf();
734     if(dirty) {
735         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)
736             return;
737     }
738     gtk_main_quit();
739     state = -1;
740 }
741
742 int main(int argc, char **argv)
743 {
744     int i, c, ex;
745     struct passwd *pwd;
746     
747     setlocale(LC_ALL, "");
748     bindtextdomain(PACKAGE, LOCALEDIR);
749     textdomain(PACKAGE);
750     prepstatic();
751     
752     gtk_init(&argc, &argv);
753     state = -1;
754     while((c = getopt(argc, argv, "haw")) != -1) {
755         switch(c) {
756         case 'a':
757             state = 1;
758             break;
759         case 'w':
760             state = 0;
761             break;
762         case 'h':
763         default:
764             fprintf((c == 'h')?stdout:stderr, "usage: dolconf [-haw]\n");
765             exit((c == 'h')?0:1);
766         }
767     }
768     
769     create_ast_wnd();
770     create_cfw_wnd();
771     shares = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
772     gtk_tree_view_set_model(GTK_TREE_VIEW(ast_sharelist), GTK_TREE_MODEL(shares));
773     gtk_tree_view_set_model(GTK_TREE_VIEW(cfw_sharelist), GTK_TREE_MODEL(shares));
774     
775     cfname = NULL;
776     if(getenv("HOME") != NULL) {
777         cfname = sprintf2("%s/.doldacond.conf", getenv("HOME"));
778     } else {
779         if((pwd = getpwuid(getuid())) != NULL)
780             cfname = sprintf2("%s/.doldacond.conf", pwd->pw_dir);
781     }
782     if(cfname == NULL) {
783         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
784         exit(1);
785     }
786     
787     ex = !access(cfname, F_OK);
788     if(state == -1) {
789         if(!ex) {
790             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)
791                 state = 1;
792             else
793                 state = 0;
794         } else {
795             state = 0;
796         }
797     }
798     
799     if(ex && (state == 0)) {
800         if(readconfig() == 1) {
801             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)
802                 exit(1);
803         }
804     }
805     
806     while(state != -1) {
807         if(state == 0) {
808             gtk_window_set_default_size(GTK_WINDOW(cfw_wnd), 500, 350);
809             gtk_widget_show(cfw_wnd);
810             fillcfw();
811             rootwnd = GTK_WINDOW(cfw_wnd);
812             gtk_main();
813             gtk_widget_hide(cfw_wnd);
814             rootwnd = NULL;
815         } else if(state == 1) {
816             gtk_window_set_default_size(GTK_WINDOW(ast_wnd), 500, 350);
817             gtk_widget_show(ast_wnd);
818             rootwnd = GTK_WINDOW(ast_wnd);
819             gtk_main();
820             gtk_widget_hide(ast_wnd);
821             ignoreclose = 0;
822             rootwnd = NULL;
823         } else if(state == 2) {
824             for(i = 3; i < FD_SETSIZE; i++)
825                 close(i);
826             execlp("dolcon", "dolcon", NULL);
827             perror("dolcon");
828             exit(127);
829         } else {
830             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Unknown state)"));
831             abort();
832         }
833     }
834     return(0);
835 }