Set dirty flag when modifying shares with dolconf.
[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     if(shareadd())
643         dirty = 1;
644 }
645
646 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata)
647 {
648     GtkTreeIter iter;
649     
650     if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(ast_sharelist)), NULL, &iter))
651         gtk_list_store_remove(shares, &iter);
652     if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(shares), NULL) == 0)
653         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, FALSE);
654 }
655
656 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata)
657 {
658     GtkTreeIter iter;
659     
660     if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(cfw_sharelist)), NULL, &iter)) {
661         gtk_list_store_remove(shares, &iter);
662         dirty = 1;
663     }
664 }
665
666 void cb_ast_checkports(GtkWidget *widget, gpointer uudata)
667 {
668     gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3,
669                                     v_natural(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) &&
670                                     (atoi(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) >= 1024) &&
671                                     v_natural(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) &&
672                                     (atoi(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) >= 1024) &&
673                                     v_ipv4(gtk_entry_get_text(GTK_ENTRY(ast_extip))));
674 }
675
676 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata)
677 {
678     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
679         gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), TRUE);
680         cb_ast_checkports(widget, NULL);
681     } else {
682         gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), FALSE);
683         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3, TRUE);
684     }
685 }
686
687 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata)
688 {
689     gtk_widget_set_sensitive(GTK_WIDGET(cfw_natbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
690 }
691
692 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata)
693 {
694     gtk_widget_set_sensitive(GTK_WIDGET(cfw_portbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
695 }
696
697 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata)
698 {
699     gtk_widget_set_sensitive(GTK_WIDGET(cfw_addrbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
700 }
701
702 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata)
703 {
704     gtk_widget_set_sensitive(GTK_WIDGET(cfw_uibox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
705 }
706
707 void cb_cfw_hup_activate(GtkWidget *widget, gpointer uudata)
708 {
709     int tag;
710     struct dc_response *resp;
711     
712     if(dc_connectsync2(dc_srv_local, DC_LATEST) < 0) {
713         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
714         return;
715     }
716     if(dc_login(NULL, 1, dc_convnone, NULL) != DC_LOGIN_ERR_SUCCESS) {
717         dc_disconnect();
718         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
719         return;
720     }
721     tag = dc_queuecmd(NULL, NULL, L"hup", NULL);
722     if((resp = dc_gettaggedrespsync(tag)) != NULL) {
723         if(resp->code != 200)
724             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
725         dc_freeresp(resp);
726     }
727     dc_disconnect();
728 }
729
730 void cb_cfw_save_activate(GtkWidget *widget, gpointer uudata)
731 {
732     struct cfvar *cv;
733     
734     if((cv = cfwvalid()) != NULL) {
735         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, cv->vld->invmsg, cv->rname);
736         return;
737     }
738     cfw2conf();
739     writeconfig();
740 }
741
742 void cb_cfw_quit_activate(GtkWidget *widget, gpointer uudata)
743 {
744     cfw2conf();
745     if(dirty) {
746         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)
747             return;
748     }
749     gtk_main_quit();
750     state = -1;
751 }
752
753 int main(int argc, char **argv)
754 {
755     int i, c, ex;
756     struct passwd *pwd;
757     
758     setlocale(LC_ALL, "");
759     bindtextdomain(PACKAGE, LOCALEDIR);
760     textdomain(PACKAGE);
761     prepstatic();
762     dc_init();
763     
764     gtk_init(&argc, &argv);
765     state = -1;
766     while((c = getopt(argc, argv, "haw")) != -1) {
767         switch(c) {
768         case 'a':
769             state = 1;
770             break;
771         case 'w':
772             state = 0;
773             break;
774         case 'h':
775             printf("usage: dolconf [-haw]\n");
776             printf("\t-h\tDisplay this help message\n");
777             printf("\t-a\tGo directly to the assistant\n");
778             printf("\t-w\tGo directly to the standard window\n");
779             exit(0);
780         default:
781             fprintf(stderr, "usage: dolconf [-haw]\n");
782             exit(1);
783         }
784     }
785     
786     create_ast_wnd();
787     create_cfw_wnd();
788     shares = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
789     gtk_tree_view_set_model(GTK_TREE_VIEW(ast_sharelist), GTK_TREE_MODEL(shares));
790     gtk_tree_view_set_model(GTK_TREE_VIEW(cfw_sharelist), GTK_TREE_MODEL(shares));
791     
792     cfname = NULL;
793     if(getenv("HOME") != NULL) {
794         cfname = sprintf2("%s/.doldacond.conf", getenv("HOME"));
795     } else {
796         if((pwd = getpwuid(getuid())) != NULL)
797             cfname = sprintf2("%s/.doldacond.conf", pwd->pw_dir);
798     }
799     if(cfname == NULL) {
800         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
801         exit(1);
802     }
803     
804     ex = !access(cfname, F_OK);
805     if(state == -1) {
806         if(!ex) {
807             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)
808                 state = 1;
809             else
810                 state = 0;
811         } else {
812             state = 0;
813         }
814     }
815     
816     if(ex && (state == 0)) {
817         if(readconfig() == 1) {
818             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)
819                 exit(1);
820         }
821     }
822     
823     while(state != -1) {
824         if(state == 0) {
825             gtk_window_set_default_size(GTK_WINDOW(cfw_wnd), 500, 350);
826             gtk_widget_show(cfw_wnd);
827             fillcfw();
828             rootwnd = GTK_WINDOW(cfw_wnd);
829             gtk_main();
830             gtk_widget_hide(cfw_wnd);
831             rootwnd = NULL;
832         } else if(state == 1) {
833             gtk_window_set_default_size(GTK_WINDOW(ast_wnd), 500, 350);
834             gtk_widget_show(ast_wnd);
835             rootwnd = GTK_WINDOW(ast_wnd);
836             gtk_main();
837             gtk_widget_hide(ast_wnd);
838             ignoreclose = 0;
839             rootwnd = NULL;
840         } else if(state == 2) {
841             for(i = 3; i < FD_SETSIZE; i++)
842                 close(i);
843             execlp("dolcon-launch", "dolcon-launch", NULL);
844             perror("dolcon-launch");
845             exit(127);
846         } else {
847             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Unknown state)"));
848             abort();
849         }
850     }
851     return(0);
852 }
853
854 #include "dolconf-assistant.gtk"
855 #include "dolconf-wnd.gtk"