Fixed documentation typo.
[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 + ((delim == '\"')?1:0), 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 int 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(-1);
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     return(0);
398 }
399
400 void fillcfw(void)
401 {
402     struct cfvar *var;
403     
404     for(var = config; var->name != NULL; var++) {
405         if(var->cfww != NULL)
406             gtk_entry_set_text(GTK_ENTRY(*(var->cfww)), var->val);
407     }
408     if(atoi(findcfvar("dc.tcpport")->val) || atoi(findcfvar("dc.udpport")->val))
409         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_orport), TRUE);
410     else
411         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_orport), FALSE);
412     if(strcmp(findcfvar("net.visibleipv4")->val, "0.0.0.0"))
413         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_oraddr), TRUE);
414     else
415         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_oraddr), FALSE);
416     if(strcmp(findcfvar("ui.port")->val, "-1")) {
417         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_uinet), TRUE);
418         if(strcmp(findcfvar("auth.authless")->val, "1"))
419             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), FALSE);
420         else
421             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), TRUE);
422     } else {
423         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_uinet), FALSE);
424         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), FALSE);
425     }
426 }
427
428 void ast2conf(void)
429 {
430     setcfvar("cli.defnick", gtk_entry_get_text(GTK_ENTRY(ast_nick)));
431     setcfvar("dc.desc", gtk_entry_get_text(GTK_ENTRY(ast_desc)));
432     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_mode_psv))) {
433         setcfvar("net.mode", "1");
434     } else {
435         setcfvar("net.mode", "0");
436         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_mode_nat))) {
437             setcfvar("net.visibleipv4", gtk_entry_get_text(GTK_ENTRY(ast_extip)));
438             setcfvar("dc.tcpport", gtk_entry_get_text(GTK_ENTRY(ast_udpport)));
439             setcfvar("dc.udpport", gtk_entry_get_text(GTK_ENTRY(ast_tcpport)));
440         } else {
441             setcfvar("net.visibleipv4", "0.0.0.0");
442             setcfvar("dc.tcpport", "0");
443             setcfvar("dc.udpport", "0");
444         }
445     }
446 }
447
448 void cfw2conf(void)
449 {
450     struct cfvar *var;
451     const char *val;
452     
453     for(var = config; var->name != NULL; var++) {
454         if(var->cfww != NULL) {
455             val = gtk_entry_get_text(GTK_ENTRY(*(var->cfww)));
456             if(!strcmp(var->val, val))
457                 continue;
458             free(var->val);
459             var->val = sstrdup(val);
460             dirty = 1;
461         }
462     }
463     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_mode_act))) {
464         setcfvar("net.mode", "0");
465         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_orport))) {
466             setcfvar("dc.tcpport", "0");
467             setcfvar("dc.udpport", "0");
468         }
469         if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_oraddr))) {
470             setcfvar("net.visibleipv4", "0.0.0.0");
471         }
472     } else {
473         setcfvar("net.mode", "1");
474     }
475     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_uinet))) {
476         setcfvar("ui.port", "1500");
477         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_authless)))
478             setcfvar("auth.authless", "1");
479         else
480             setcfvar("auth.authless", "0");
481     } else {
482         setcfvar("ui.port", "-1");
483         setcfvar("auth.authless", "0");
484     }
485 }
486
487 struct cfvar *cfwvalid(void)
488 {
489     struct cfvar *cv;
490     
491     for(cv = config; cv->name != NULL; cv++) {
492         if((cv->vld != NULL) && !cv->vld->check(cv->val)) {
493             if(cv->rname) {
494                 return(cv);
495             } else {
496                 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Auto-generated variable %s has an invalid value \"%s\")"), cv->name, cv->val);
497                 abort();
498             }
499         }
500     }
501     return(NULL);
502 }
503
504 void astcancel(GtkWidget *widget, gpointer uudata)
505 {
506     if(ignoreclose)
507         return;
508     gtk_main_quit();
509     state = -1;
510 }
511
512 #define bufcats(buf, str) bufcat(buf, str, strlen(str))
513
514 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata)
515 {
516     char *s, *buf;
517     size_t sdata, ssize;
518     struct cfvar *var;
519     GtkTreeIter iter;
520     
521     ast2conf();
522     s = NULL;
523     sdata = ssize = 0;
524     for(var = config; var->name != NULL; var++) {
525         if(var->rname == NULL)
526             continue;
527         bufcats(s, var->rname);
528         bufcats(s, ": ");
529         bufcat(s, var->val, strlen(var->val));
530         addtobuf(s, '\n');
531     }
532     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
533         addtobuf(s, '\n');
534         bufcats(s, _("Shares:\n"));
535         do {
536             addtobuf(s, '\t');
537             gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf, -1);
538             bufcats(s, buf);
539             g_free(buf);
540             addtobuf(s, '\n');
541         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
542     }
543     gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(ast_summary)), s, sdata);
544     free(s);
545 }
546
547 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata)
548 {
549     writeconfig();
550     gtk_main_quit();
551     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_dolcon)))
552         state = 2;
553     else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_exit)))
554         state = -1;
555     else
556         state = 0;
557     ignoreclose = 1;
558 }
559
560 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata)
561 {
562     if(v_dcstring(gtk_entry_get_text(GTK_ENTRY(ast_nick))))
563         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, TRUE);
564     else
565         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, FALSE);
566 }
567
568 int hasshare(int col, char *name)
569 {
570     GtkTreeIter iter;
571     char *buf;
572     
573     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
574         do {
575             gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, col, &buf, -1);
576             if(!strcmp(buf, name)) {
577                 g_free(buf);
578                 return(1);
579             }
580             g_free(buf);
581         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
582     }
583     return(0);
584 }
585
586 int shareadd(void)
587 {
588     int i, ret;
589     GSList *fns, *next;
590     char *fn, *sn, *p;
591     GtkTreeIter iter;
592     GtkWidget *chd;
593     int resp;
594     
595     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);
596     gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(chd), TRUE);
597     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chd), TRUE);
598     resp = gtk_dialog_run(GTK_DIALOG(chd));
599     if(resp != GTK_RESPONSE_ACCEPT) {
600         gtk_widget_destroy(chd);
601         return(0);
602     }
603     ret = 0;
604     fns = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chd));
605     gtk_widget_destroy(chd);
606     while(fns != NULL) {
607         fn = fns->data;
608         if(!hasshare(1, fn)) {
609             if((p = strrchr(fn, '/')) == NULL)
610                 p = fn;
611             else
612                 p++;
613             sn = sstrdup(p);
614             if(hasshare(0, sn)) {
615                 for(i = 2; 1; i++) {
616                     free(sn);
617                     sn = sprintf2("%s%i", p, i);
618                     if(!hasshare(0, sn))
619                         break;
620                 }
621             }
622             gtk_list_store_append(shares, &iter);
623             gtk_list_store_set(shares, &iter, 0, sn, 1, fn, -1);
624             free(sn);
625             ret = 1;
626         }
627         g_free(fn);
628         next = fns->next;
629         g_slist_free_1(fns);
630         fns = next;
631     }
632     return(ret);
633 }
634
635 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata)
636 {
637     if(shareadd())
638         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, TRUE);
639 }
640
641 void cb_cfw_shareadd_clicked(GtkWidget *widget, gpointer uudata)
642 {
643     if(shareadd())
644         dirty = 1;
645 }
646
647 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata)
648 {
649     GtkTreeIter iter;
650     
651     if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(ast_sharelist)), NULL, &iter))
652         gtk_list_store_remove(shares, &iter);
653     if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(shares), NULL) == 0)
654         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, FALSE);
655 }
656
657 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata)
658 {
659     GtkTreeIter iter;
660     
661     if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(cfw_sharelist)), NULL, &iter)) {
662         gtk_list_store_remove(shares, &iter);
663         dirty = 1;
664     }
665 }
666
667 void cb_ast_checkports(GtkWidget *widget, gpointer uudata)
668 {
669     gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3,
670                                     v_natural(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) &&
671                                     (atoi(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) >= 1024) &&
672                                     v_natural(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) &&
673                                     (atoi(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) >= 1024) &&
674                                     v_ipv4(gtk_entry_get_text(GTK_ENTRY(ast_extip))));
675 }
676
677 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata)
678 {
679     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
680         gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), TRUE);
681         cb_ast_checkports(widget, NULL);
682     } else {
683         gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), FALSE);
684         gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3, TRUE);
685     }
686 }
687
688 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata)
689 {
690     gtk_widget_set_sensitive(GTK_WIDGET(cfw_natbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
691 }
692
693 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata)
694 {
695     gtk_widget_set_sensitive(GTK_WIDGET(cfw_portbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
696 }
697
698 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata)
699 {
700     gtk_widget_set_sensitive(GTK_WIDGET(cfw_addrbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
701 }
702
703 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata)
704 {
705     gtk_widget_set_sensitive(GTK_WIDGET(cfw_uibox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
706 }
707
708 int hupserver(int disperr)
709 {
710     int tag, ret;
711     struct dc_response *resp;
712     
713     if(dc_connectsync2(dc_srv_local, DC_LATEST) < 0) {
714         if(disperr)
715             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
716         return(-1);
717     }
718     if(dc_login(NULL, 1, dc_convnone, NULL) != DC_LOGIN_ERR_SUCCESS) {
719         dc_disconnect();
720         if(disperr)
721             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
722         return(-1);
723     }
724     ret = 0;
725     tag = dc_queuecmd(NULL, NULL, L"hup", NULL);
726     if((resp = dc_gettaggedrespsync(tag)) != NULL) {
727         if(resp->code != 200) {
728             if(disperr)
729                 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
730             ret = -1;
731         }
732         dc_freeresp(resp);
733     }
734     dc_disconnect();
735     return(ret);
736 }
737
738 int saveconfig(void)
739 {
740     struct cfvar *cv;
741     
742     cfw2conf();
743     if((cv = cfwvalid()) != NULL) {
744         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, cv->vld->invmsg, cv->rname);
745         return(-1);
746     }
747     return(writeconfig());
748 }
749
750 /*
751 void cb_cfw_hup_activate(GtkWidget *widget, gpointer uudata)
752 {
753     hupserver(1);
754 }
755
756 void cb_cfw_save_activate(GtkWidget *widget, gpointer uudata)
757 {
758     struct cfvar *cv;
759     
760     if((cv = cfwvalid()) != NULL) {
761         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, cv->vld->invmsg, cv->rname);
762         return;
763     }
764     cfw2conf();
765     writeconfig();
766 }
767
768 void cb_cfw_quit_activate(GtkWidget *widget, gpointer uudata)
769 {
770     cfw2conf();
771     if(dirty) {
772         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)
773             return;
774     }
775     gtk_main_quit();
776     state = -1;
777 }
778 */
779
780 void cb_cfw_ok_clicked(GtkWidget *widget, gpointer uudata)
781 {
782     if(saveconfig())
783         return;
784     hupserver(0);
785     gtk_main_quit();
786     state = -1;
787 }
788
789 void cb_cfw_cancel_clicked(GtkWidget *widget, gpointer uudata)
790 {
791     gtk_main_quit();
792     state = -1;
793 }
794
795 void cb_cfw_apply_clicked(GtkWidget *widget, gpointer uudata)
796 {
797     if(saveconfig())
798         return;
799     hupserver(1);
800 }
801
802 int main(int argc, char **argv)
803 {
804     int i, c, ex;
805     struct passwd *pwd;
806     
807     setlocale(LC_ALL, "");
808     bindtextdomain(PACKAGE, LOCALEDIR);
809     textdomain(PACKAGE);
810     prepstatic();
811     dc_init();
812     
813     gtk_init(&argc, &argv);
814     state = -1;
815     while((c = getopt(argc, argv, "hawV")) != -1) {
816         switch(c) {
817         case 'a':
818             state = 1;
819             break;
820         case 'w':
821             state = 0;
822             break;
823         case 'h':
824             printf("usage: dolconf [-haw]\n");
825             printf("\t-h\tDisplay this help message\n");
826             printf("\t-a\tGo directly to the assistant\n");
827             printf("\t-w\tGo directly to the standard window\n");
828             printf("\t-V\tDisplay version info and exit\n");
829             exit(0);
830         case 'V':
831             printf("%s", RELEASEINFO);
832             exit(0);
833         default:
834             fprintf(stderr, "usage: dolconf [-hawV]\n");
835             exit(1);
836         }
837     }
838     
839     create_ast_wnd();
840     create_cfw_wnd();
841     shares = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
842     gtk_tree_view_set_model(GTK_TREE_VIEW(ast_sharelist), GTK_TREE_MODEL(shares));
843     gtk_tree_view_set_model(GTK_TREE_VIEW(cfw_sharelist), GTK_TREE_MODEL(shares));
844     
845     cfname = NULL;
846     if(getenv("HOME") != NULL) {
847         cfname = sprintf2("%s/.doldacond.conf", getenv("HOME"));
848     } else {
849         if((pwd = getpwuid(getuid())) != NULL)
850             cfname = sprintf2("%s/.doldacond.conf", pwd->pw_dir);
851     }
852     if(cfname == NULL) {
853         msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
854         exit(1);
855     }
856     
857     ex = !access(cfname, F_OK);
858     if(state == -1) {
859         if(!ex) {
860             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)
861                 state = 1;
862             else
863                 state = 0;
864         } else {
865             state = 0;
866         }
867     }
868     
869     if(ex && (state == 0)) {
870         if(readconfig() == 1) {
871             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)
872                 exit(1);
873         }
874     }
875     
876     while(state != -1) {
877         if(state == 0) {
878             gtk_window_set_default_size(GTK_WINDOW(cfw_wnd), 500, 350);
879             gtk_widget_show(cfw_wnd);
880             fillcfw();
881             rootwnd = GTK_WINDOW(cfw_wnd);
882             gtk_main();
883             gtk_widget_hide(cfw_wnd);
884             rootwnd = NULL;
885         } else if(state == 1) {
886             gtk_window_set_default_size(GTK_WINDOW(ast_wnd), 500, 350);
887             gtk_widget_show(ast_wnd);
888             rootwnd = GTK_WINDOW(ast_wnd);
889             gtk_main();
890             gtk_widget_hide(ast_wnd);
891             ignoreclose = 0;
892             rootwnd = NULL;
893         } else if(state == 2) {
894             for(i = 3; i < FD_SETSIZE; i++)
895                 close(i);
896             execlp("dolcon-launch", "dolcon-launch", NULL);
897             perror("dolcon-launch");
898             exit(127);
899         } else {
900             msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Unknown state)"));
901             abort();
902         }
903     }
904     return(0);
905 }
906
907 #include "dolconf-assistant.gtk"
908 #include "dolconf-wnd.gtk"