2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2007 Fredrik Tolf <fredrik@dolda2000.com>
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.
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.
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
32 #include <arpa/inet.h>
40 int (*check)(const char *val);
48 struct validation *vld;
49 GtkWidget **astw, **cfww;
53 GtkWindow *rootwnd = NULL;
58 void astcancel(GtkWidget *widget, gpointer uudata);
59 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata);
60 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata);
61 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata);
62 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata);
63 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata);
64 void cb_ast_checkports(GtkWidget *widget, gpointer uudata);
65 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata);
66 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata);
67 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata);
68 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata);
69 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata);
70 void cb_cfw_save_clicked(GtkWidget *widget, gpointer uudata);
71 void cb_cfw_quit_clicked(GtkWidget *widget, gpointer uudata);
72 void cb_cfw_shareadd_clicked(GtkWidget *widget, gpointer uudata);
73 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata);
75 #define _(text) gettext(text)
77 #include "dolconf-assistant.gtk"
78 #include "dolconf-wnd.gtk"
80 int v_nonempty(const char *val)
82 return(strlen(val) > 0);
85 int v_dcstring(const char *val)
87 return((strchr(val, ' ') == NULL) &&
88 (strchr(val, '|') == NULL) &&
89 (strchr(val, '$') == NULL));
92 int v_natural(const char *val)
103 int v_integer(const char *val)
107 for(f = 1, d = 0; *val; val++, f = 0) {
110 } else if(f && (*val == '-')) {
118 int v_ipv4(const char *val)
122 return(inet_aton(val, &buf) != 0);
128 struct validation nonempty = {
130 .invmsg = _("%s must not be empty"),
133 struct validation dcstring = {
135 .invmsg = _("%s must not contain spaces, `|' or `$'"),
138 struct validation natural = {
140 .invmsg = _("%s must be a natural number"),
143 struct validation integer = {
145 .invmsg = _("%s must be an integer"),
148 struct validation ipv4 = {
150 .invmsg = _("%s must be an IP address"),
153 struct validation *vldxlate[] = {
154 &nonempty, &dcstring, &natural, &integer, &ipv4,
158 struct cfvar config[] = {
159 {"cli.defnick", _("Screen name"), "", &dcstring, &ast_nick, &cfw_nick},
160 {"net.mode", NULL, "0", &natural},
161 {"net.visibleipv4", "IP address", "0.0.0.0", &ipv4, NULL, &cfw_extip},
162 {"ui.onlylocal", NULL, "0", &natural},
163 {"ui.port", NULL, "-1", &integer},
164 {"auth.authless", NULL, "0", &natural},
165 {"transfer.slots", _("Upload slots"), "6", &natural},
166 {"dc.speedstring", _("Connection speed"), "DSL", &dcstring, NULL, &cfw_cntype},
167 {"dc.email", _("E-mail address"), "spam@spam.net", &dcstring, NULL, &cfw_mail},
168 {"dc.desc", _("Share description"), "", NULL, &ast_desc, &cfw_desc},
169 {"dc.tcpport", _("Direct Connect TCP port"), "0", &natural, NULL, &cfw_tcpport},
170 {"dc.udpport", _("Direct Connect UDP port"), "0", &natural, NULL, &cfw_udpport},
175 #define _(text) gettext(text)
177 struct cfvar *findcfvar(char *name)
181 for(v = config; v->name != NULL; v++) {
182 if(!strcmp(v->name, name))
188 void setcfvar(char *name, const char *val)
194 v->val = sstrdup(val);
197 int msgbox(int type, int buttons, char *format, ...)
204 va_start(args, format);
205 buf = vsprintf2(format, args);
207 swnd = gtk_message_dialog_new(rootwnd, GTK_DIALOG_MODAL, type, buttons, "%s", buf);
208 gtk_window_set_title(GTK_WINDOW(swnd), _("Dolda Connect configurator"));
209 resp = gtk_dialog_run(GTK_DIALOG(swnd));
210 gtk_widget_destroy(swnd);
215 void prepstatic(void)
217 struct validation **v;
220 for(v = vldxlate; *v != NULL; v++)
221 (*v)->invmsg = gettext((*v)->invmsg);
222 for(c = config; c->name != NULL; c++) {
224 c->rname = gettext(c->rname);
225 c->val = sstrdup(c->val);
229 char *getword(char **p)
231 char *buf, *p2, delim;
239 while((p2 = strchr(p2 + 1, delim)) != NULL) {
244 p2 = *p + strlen(*p);
245 len = p2 - *p - ((*p2 == '\"')?1:0);
246 buf = smalloc(len + 1);
247 memcpy(buf, *p, len);
249 *p = p2 + ((*p2 == '\"')?1:0);
250 for(p2 = buf; *p2; p2++, len--) {
252 memmove(p2, p2 + 1, len--);
257 char *quoteword(char *word)
269 for(wp = word; *wp != '\0'; wp++)
271 if(!dq && isspace(*wp))
273 if((*wp == '\\') || (*wp == '\"'))
280 bp = buf = smalloc(sizeof(wchar_t) * (numc + numbs + (dq?2:0) + 1));
283 for(wp = word; *wp != '\0'; wp++)
285 if((*wp == '\\') || (*wp == '\"'))
306 if((cf = fopen(cfname, "r")) == NULL) {
307 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not open the configuration file for reading: %s"), strerror(errno));
311 while(fgets(lbuf, sizeof(lbuf), cf) != NULL) {
313 if(lbuf[len - 1] == '\n')
323 if(!strncmp(lbuf, "set ", 4)) {
325 if(((key = getword(&p)) == NULL) || (*(p++) != ' ') || ((val = getword(&p)) == NULL)) {
329 for(var = config; var->name != NULL; var++) {
330 if(!strcmp(var->name, key)) {
332 var->val = sstrdup(val);
336 if(var->name == NULL)
338 } else if(!strncmp(lbuf, "share ", 6)) {
340 if(((key = getword(&p)) == NULL) || (*(p++) != ' ') || ((val = getword(&p)) == NULL)) {
344 gtk_list_store_append(shares, &iter);
345 gtk_list_store_set(shares, &iter, 0, key, 1, val, -1);
346 } else if(!lbuf[0] || lbuf[0] == '#') {
359 void writeconfig(void)
366 if((cf = fopen(cfname, "w")) == NULL) {
367 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not open the configuration file for writing: %s"), strerror(errno));
370 fputs("# This file was generated by dolconf v" VERSION "\n\n", cf);
371 for(var = config; var->name != NULL; var++) {
372 fprintf(cf, "set %s ", var->name);
373 if((buf = quoteword(var->val)) == NULL) {
381 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
385 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 0, &buf2, -1);
386 if((buf = quoteword(buf2)) == NULL) {
394 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf2, -1);
395 if((buf = quoteword(buf2)) == NULL) {
403 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
412 for(var = config; var->name != NULL; var++) {
413 if(var->cfww != NULL)
414 gtk_entry_set_text(GTK_ENTRY(*(var->cfww)), var->val);
416 if(atoi(findcfvar("dc.tcpport")->val) || atoi(findcfvar("dc.udpport")->val))
417 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_orport), TRUE);
419 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_orport), FALSE);
420 if(strcmp(findcfvar("net.visibleipv4")->val, "0.0.0.0"))
421 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_oraddr), TRUE);
423 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_oraddr), FALSE);
424 if(strcmp(findcfvar("ui.port")->val, "-1")) {
425 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_uinet), TRUE);
426 if(strcmp(findcfvar("auth.authless")->val, "1"))
427 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), FALSE);
429 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), TRUE);
431 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_uinet), FALSE);
432 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), FALSE);
438 setcfvar("cli.defnick", gtk_entry_get_text(GTK_ENTRY(ast_nick)));
439 setcfvar("dc.desc", gtk_entry_get_text(GTK_ENTRY(ast_desc)));
440 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_mode_psv))) {
441 setcfvar("net.mode", "1");
443 setcfvar("net.mode", "0");
444 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_mode_nat))) {
445 setcfvar("net.visibleipv4", gtk_entry_get_text(GTK_ENTRY(ast_extip)));
446 setcfvar("dc.tcpport", gtk_entry_get_text(GTK_ENTRY(ast_udpport)));
447 setcfvar("dc.udpport", gtk_entry_get_text(GTK_ENTRY(ast_tcpport)));
449 setcfvar("net.visibleipv4", "0.0.0.0");
450 setcfvar("dc.tcpport", "0");
451 setcfvar("dc.udpport", "0");
460 for(var = config; var->name != NULL; var++) {
461 if(var->cfww != NULL) {
463 var->val = sstrdup(gtk_entry_get_text(GTK_ENTRY(*(var->cfww))));
466 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_mode_act))) {
467 setcfvar("net.mode", "0");
468 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_orport))) {
469 setcfvar("dc.tcpport", "0");
470 setcfvar("dc.udpport", "0");
472 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_oraddr))) {
473 setcfvar("net.visibleipv4", "0.0.0.0");
476 setcfvar("net.mode", "1");
478 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_uinet))) {
479 setcfvar("ui.port", "1500");
480 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_authless)))
481 setcfvar("auth.authless", "1");
483 setcfvar("auth.authless", "0");
485 setcfvar("ui.port", "-1");
486 setcfvar("auth.authless", "0");
490 void astcancel(GtkWidget *widget, gpointer uudata)
498 #define bufcats(buf, str) bufcat(buf, str, strlen(str))
500 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata)
510 for(var = config; var->name != NULL; var++) {
511 if(var->rname == NULL)
513 bufcats(s, var->rname);
515 bufcat(s, var->val, strlen(var->val));
518 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
520 bufcats(s, _("Shares:\n"));
523 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf, -1);
527 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
529 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(ast_summary)), s, sdata);
533 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata)
537 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_dolcon)))
539 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_exit)))
546 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata)
548 if(v_dcstring(gtk_entry_get_text(GTK_ENTRY(ast_nick))))
549 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, TRUE);
551 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, FALSE);
554 int hasshare(int col, char *name)
559 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
561 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, col, &buf, -1);
562 if(!strcmp(buf, name)) {
567 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
581 chd = gtk_file_chooser_dialog_new(_("Shared directories"), rootwnd, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
582 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(chd), TRUE);
583 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chd), TRUE);
584 resp = gtk_dialog_run(GTK_DIALOG(chd));
585 if(resp != GTK_RESPONSE_ACCEPT) {
586 gtk_widget_destroy(chd);
590 fns = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chd));
591 gtk_widget_destroy(chd);
594 if(!hasshare(1, fn)) {
595 if((p = strrchr(fn, '/')) == NULL)
600 if(hasshare(0, sn)) {
603 sn = sprintf2("%s%i", p, i);
608 gtk_list_store_append(shares, &iter);
609 gtk_list_store_set(shares, &iter, 0, sn, 1, fn, -1);
621 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata)
624 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, TRUE);
627 void cb_cfw_shareadd_clicked(GtkWidget *widget, gpointer uudata)
632 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata)
636 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(ast_sharelist)), NULL, &iter))
637 gtk_list_store_remove(shares, &iter);
638 if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(shares), NULL) == 0)
639 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, FALSE);
642 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata)
646 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(cfw_sharelist)), NULL, &iter))
647 gtk_list_store_remove(shares, &iter);
650 void cb_ast_checkports(GtkWidget *widget, gpointer uudata)
652 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3,
653 v_natural(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) &&
654 (atoi(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) >= 1024) &&
655 v_natural(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) &&
656 (atoi(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) >= 1024) &&
657 v_ipv4(gtk_entry_get_text(GTK_ENTRY(ast_extip))));
660 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata)
662 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
663 gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), TRUE);
664 cb_ast_checkports(widget, NULL);
666 gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), FALSE);
667 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3, TRUE);
671 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata)
673 gtk_widget_set_sensitive(GTK_WIDGET(cfw_natbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
676 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata)
678 gtk_widget_set_sensitive(GTK_WIDGET(cfw_portbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
681 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata)
683 gtk_widget_set_sensitive(GTK_WIDGET(cfw_addrbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
686 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata)
688 gtk_widget_set_sensitive(GTK_WIDGET(cfw_uibox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
691 void cb_cfw_save_clicked(GtkWidget *widget, gpointer uudata)
695 for(cv = config; cv->name != NULL; cv++) {
696 if((cv->vld != NULL) && !cv->vld->check(cv->val)) {
698 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, cv->vld->invmsg, cv->rname);
700 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Auto-generated variable %s has an invalid value \"%s\")"), cv->name, cv->val);
709 void cb_cfw_quit_clicked(GtkWidget *widget, gpointer uudata)
715 int main(int argc, char **argv)
720 setlocale(LC_ALL, "");
721 bindtextdomain(PACKAGE, LOCALEDIR);
725 gtk_init(&argc, &argv);
727 while((c = getopt(argc, argv, "haw")) != -1) {
737 fprintf((c == 'h')?stdout:stderr, "usage: dolconf [-haw]\n");
738 exit((c == 'h')?0:1);
744 shares = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
745 gtk_tree_view_set_model(GTK_TREE_VIEW(ast_sharelist), GTK_TREE_MODEL(shares));
746 gtk_tree_view_set_model(GTK_TREE_VIEW(cfw_sharelist), GTK_TREE_MODEL(shares));
749 if(getenv("HOME") != NULL) {
750 cfname = sprintf2("%s/.doldacond.conf", getenv("HOME"));
752 if((pwd = getpwuid(getuid())) != NULL)
753 cfname = sprintf2("%s/.doldacond.conf", pwd->pw_dir);
756 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
760 ex = !access(cfname, F_OK);
761 if(!ex && (state == -1)) {
762 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)
768 if(ex && (state == 0)) {
769 if(readconfig() == 1) {
770 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)
777 gtk_window_set_default_size(GTK_WINDOW(cfw_wnd), 500, 350);
778 gtk_widget_show(cfw_wnd);
780 rootwnd = GTK_WINDOW(cfw_wnd);
782 gtk_widget_hide(cfw_wnd);
784 } else if(state == 1) {
785 gtk_window_set_default_size(GTK_WINDOW(ast_wnd), 500, 350);
786 gtk_widget_show(ast_wnd);
787 rootwnd = GTK_WINDOW(ast_wnd);
789 gtk_widget_hide(ast_wnd);
792 } else if(state == 2) {
793 for(i = 3; i < FD_SETSIZE; i++)
795 execlp("dolcon", "dolcon", NULL);
799 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Unknown state)"));