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