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
28 #include <gdk/gdkkeysyms.h>
33 #include <arpa/inet.h>
41 int (*check)(const char *val);
49 struct validation *vld;
50 GtkWidget **astw, **cfww;
54 GtkWindow *rootwnd = NULL;
59 void astcancel(GtkWidget *widget, gpointer uudata);
60 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata);
61 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata);
62 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata);
63 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata);
64 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata);
65 void cb_ast_checkports(GtkWidget *widget, gpointer uudata);
66 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata);
67 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata);
68 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata);
69 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata);
70 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata);
71 void cb_cfw_save_activate(GtkWidget *widget, gpointer uudata);
72 void cb_cfw_quit_activate(GtkWidget *widget, gpointer uudata);
73 void cb_cfw_shareadd_clicked(GtkWidget *widget, gpointer uudata);
74 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata);
76 #define _(text) gettext(text)
78 #include "dolconf-assistant.gtk"
79 #include "dolconf-wnd.gtk"
81 int v_nonempty(const char *val)
83 return(strlen(val) > 0);
86 int v_dcstring(const char *val)
88 return((strchr(val, ' ') == NULL) &&
89 (strchr(val, '|') == NULL) &&
90 (strchr(val, '$') == NULL));
93 int v_natural(const char *val)
104 int v_integer(const char *val)
108 for(f = 1, d = 0; *val; val++, f = 0) {
111 } else if(f && (*val == '-')) {
119 int v_ipv4(const char *val)
123 return(inet_aton(val, &buf) != 0);
129 struct validation nonempty = {
131 .invmsg = _("%s must not be empty"),
134 struct validation dcstring = {
136 .invmsg = _("%s must not contain spaces, `|' or `$'"),
139 struct validation natural = {
141 .invmsg = _("%s must be a natural number"),
144 struct validation integer = {
146 .invmsg = _("%s must be an integer"),
149 struct validation ipv4 = {
151 .invmsg = _("%s must be an IP address"),
154 struct validation *vldxlate[] = {
155 &nonempty, &dcstring, &natural, &integer, &ipv4,
159 struct cfvar config[] = {
160 {"cli.defnick", _("Screen name"), "", &dcstring, &ast_nick, &cfw_nick},
161 {"net.mode", NULL, "0", &natural},
162 {"net.visibleipv4", "IP address", "0.0.0.0", &ipv4, NULL, &cfw_extip},
163 {"ui.onlylocal", NULL, "0", &natural},
164 {"ui.port", NULL, "-1", &integer},
165 {"auth.authless", NULL, "0", &natural},
166 {"transfer.slots", _("Upload slots"), "6", &natural},
167 {"dc.speedstring", _("Connection speed"), "DSL", &dcstring, NULL, &cfw_cntype},
168 {"dc.email", _("E-mail address"), "spam@spam.net", &dcstring, NULL, &cfw_mail},
169 {"dc.desc", _("Share description"), "", NULL, &ast_desc, &cfw_desc},
170 {"dc.tcpport", _("Direct Connect TCP port"), "0", &natural, NULL, &cfw_tcpport},
171 {"dc.udpport", _("Direct Connect UDP port"), "0", &natural, NULL, &cfw_udpport},
176 #define _(text) gettext(text)
178 struct cfvar *findcfvar(char *name)
182 for(v = config; v->name != NULL; v++) {
183 if(!strcmp(v->name, name))
189 void setcfvar(char *name, const char *val)
194 if(!strcmp(v->val, val))
197 v->val = sstrdup(val);
201 int msgbox(int type, int buttons, char *format, ...)
208 va_start(args, format);
209 buf = vsprintf2(format, args);
211 swnd = gtk_message_dialog_new(rootwnd, GTK_DIALOG_MODAL, type, buttons, "%s", buf);
212 gtk_window_set_title(GTK_WINDOW(swnd), _("Dolda Connect configurator"));
213 resp = gtk_dialog_run(GTK_DIALOG(swnd));
214 gtk_widget_destroy(swnd);
219 void prepstatic(void)
221 struct validation **v;
224 for(v = vldxlate; *v != NULL; v++)
225 (*v)->invmsg = gettext((*v)->invmsg);
226 for(c = config; c->name != NULL; c++) {
228 c->rname = gettext(c->rname);
229 c->val = sstrdup(c->val);
233 char *getword(char **p)
235 char *buf, *p2, delim;
243 while((p2 = strchr(p2 + 1, delim)) != NULL) {
248 p2 = *p + strlen(*p);
249 len = p2 - *p - ((*p2 == '\"')?1:0);
250 buf = smalloc(len + 1);
251 memcpy(buf, *p, len);
253 *p = p2 + ((*p2 == '\"')?1:0);
254 for(p2 = buf; *p2; p2++, len--) {
256 memmove(p2, p2 + 1, len--);
261 char *quoteword(char *word)
273 for(wp = word; *wp != '\0'; wp++)
275 if(!dq && isspace(*wp))
277 if((*wp == '\\') || (*wp == '\"'))
284 bp = buf = smalloc(sizeof(wchar_t) * (numc + numbs + (dq?2:0) + 1));
287 for(wp = word; *wp != '\0'; wp++)
289 if((*wp == '\\') || (*wp == '\"'))
310 if((cf = fopen(cfname, "r")) == NULL) {
311 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not open the configuration file for reading: %s"), strerror(errno));
315 while(fgets(lbuf, sizeof(lbuf), cf) != NULL) {
317 if(lbuf[len - 1] == '\n')
327 if(!strncmp(lbuf, "set ", 4)) {
329 if(((key = getword(&p)) == NULL) || (*(p++) != ' ') || ((val = getword(&p)) == NULL)) {
333 for(var = config; var->name != NULL; var++) {
334 if(!strcmp(var->name, key)) {
336 var->val = sstrdup(val);
340 if(var->name == NULL)
342 } else if(!strncmp(lbuf, "share ", 6)) {
344 if(((key = getword(&p)) == NULL) || (*(p++) != ' ') || ((val = getword(&p)) == NULL)) {
348 gtk_list_store_append(shares, &iter);
349 gtk_list_store_set(shares, &iter, 0, key, 1, val, -1);
350 } else if(!lbuf[0] || lbuf[0] == '#') {
364 void writeconfig(void)
371 if((cf = fopen(cfname, "w")) == NULL) {
372 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not open the configuration file for writing: %s"), strerror(errno));
375 fputs("# This file was generated by dolconf v" VERSION "\n\n", cf);
376 for(var = config; var->name != NULL; var++) {
377 fprintf(cf, "set %s ", var->name);
378 if((buf = quoteword(var->val)) == NULL) {
386 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
390 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 0, &buf2, -1);
391 if((buf = quoteword(buf2)) == NULL) {
399 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf2, -1);
400 if((buf = quoteword(buf2)) == NULL) {
408 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
418 for(var = config; var->name != NULL; var++) {
419 if(var->cfww != NULL)
420 gtk_entry_set_text(GTK_ENTRY(*(var->cfww)), var->val);
422 if(atoi(findcfvar("dc.tcpport")->val) || atoi(findcfvar("dc.udpport")->val))
423 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_orport), TRUE);
425 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_orport), FALSE);
426 if(strcmp(findcfvar("net.visibleipv4")->val, "0.0.0.0"))
427 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_oraddr), TRUE);
429 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_oraddr), FALSE);
430 if(strcmp(findcfvar("ui.port")->val, "-1")) {
431 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_uinet), TRUE);
432 if(strcmp(findcfvar("auth.authless")->val, "1"))
433 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), FALSE);
435 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), TRUE);
437 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_uinet), FALSE);
438 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), FALSE);
444 setcfvar("cli.defnick", gtk_entry_get_text(GTK_ENTRY(ast_nick)));
445 setcfvar("dc.desc", gtk_entry_get_text(GTK_ENTRY(ast_desc)));
446 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_mode_psv))) {
447 setcfvar("net.mode", "1");
449 setcfvar("net.mode", "0");
450 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_mode_nat))) {
451 setcfvar("net.visibleipv4", gtk_entry_get_text(GTK_ENTRY(ast_extip)));
452 setcfvar("dc.tcpport", gtk_entry_get_text(GTK_ENTRY(ast_udpport)));
453 setcfvar("dc.udpport", gtk_entry_get_text(GTK_ENTRY(ast_tcpport)));
455 setcfvar("net.visibleipv4", "0.0.0.0");
456 setcfvar("dc.tcpport", "0");
457 setcfvar("dc.udpport", "0");
467 for(var = config; var->name != NULL; var++) {
468 if(var->cfww != NULL) {
469 val = gtk_entry_get_text(GTK_ENTRY(*(var->cfww)));
470 if(!strcmp(var->val, val))
473 var->val = sstrdup(val);
477 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_mode_act))) {
478 setcfvar("net.mode", "0");
479 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_orport))) {
480 setcfvar("dc.tcpport", "0");
481 setcfvar("dc.udpport", "0");
483 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_oraddr))) {
484 setcfvar("net.visibleipv4", "0.0.0.0");
487 setcfvar("net.mode", "1");
489 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_uinet))) {
490 setcfvar("ui.port", "1500");
491 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_authless)))
492 setcfvar("auth.authless", "1");
494 setcfvar("auth.authless", "0");
496 setcfvar("ui.port", "-1");
497 setcfvar("auth.authless", "0");
501 struct cfvar *cfwvalid(void)
505 for(cv = config; cv->name != NULL; cv++) {
506 if((cv->vld != NULL) && !cv->vld->check(cv->val)) {
510 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Auto-generated variable %s has an invalid value \"%s\")"), cv->name, cv->val);
518 void astcancel(GtkWidget *widget, gpointer uudata)
526 #define bufcats(buf, str) bufcat(buf, str, strlen(str))
528 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata)
538 for(var = config; var->name != NULL; var++) {
539 if(var->rname == NULL)
541 bufcats(s, var->rname);
543 bufcat(s, var->val, strlen(var->val));
546 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
548 bufcats(s, _("Shares:\n"));
551 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf, -1);
555 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
557 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(ast_summary)), s, sdata);
561 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata)
565 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_dolcon)))
567 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_exit)))
574 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata)
576 if(v_dcstring(gtk_entry_get_text(GTK_ENTRY(ast_nick))))
577 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, TRUE);
579 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, FALSE);
582 int hasshare(int col, char *name)
587 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
589 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, col, &buf, -1);
590 if(!strcmp(buf, name)) {
595 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
609 chd = gtk_file_chooser_dialog_new(_("Shared directories"), rootwnd, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
610 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(chd), TRUE);
611 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chd), TRUE);
612 resp = gtk_dialog_run(GTK_DIALOG(chd));
613 if(resp != GTK_RESPONSE_ACCEPT) {
614 gtk_widget_destroy(chd);
618 fns = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chd));
619 gtk_widget_destroy(chd);
622 if(!hasshare(1, fn)) {
623 if((p = strrchr(fn, '/')) == NULL)
628 if(hasshare(0, sn)) {
631 sn = sprintf2("%s%i", p, i);
636 gtk_list_store_append(shares, &iter);
637 gtk_list_store_set(shares, &iter, 0, sn, 1, fn, -1);
649 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata)
652 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, TRUE);
655 void cb_cfw_shareadd_clicked(GtkWidget *widget, gpointer uudata)
660 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata)
664 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(ast_sharelist)), NULL, &iter))
665 gtk_list_store_remove(shares, &iter);
666 if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(shares), NULL) == 0)
667 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, FALSE);
670 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata)
674 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(cfw_sharelist)), NULL, &iter))
675 gtk_list_store_remove(shares, &iter);
678 void cb_ast_checkports(GtkWidget *widget, gpointer uudata)
680 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3,
681 v_natural(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) &&
682 (atoi(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) >= 1024) &&
683 v_natural(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) &&
684 (atoi(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) >= 1024) &&
685 v_ipv4(gtk_entry_get_text(GTK_ENTRY(ast_extip))));
688 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata)
690 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
691 gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), TRUE);
692 cb_ast_checkports(widget, NULL);
694 gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), FALSE);
695 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3, TRUE);
699 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata)
701 gtk_widget_set_sensitive(GTK_WIDGET(cfw_natbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
704 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata)
706 gtk_widget_set_sensitive(GTK_WIDGET(cfw_portbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
709 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata)
711 gtk_widget_set_sensitive(GTK_WIDGET(cfw_addrbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
714 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata)
716 gtk_widget_set_sensitive(GTK_WIDGET(cfw_uibox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
719 void cb_cfw_save_activate(GtkWidget *widget, gpointer uudata)
723 if((cv = cfwvalid()) != NULL) {
724 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, cv->vld->invmsg, cv->rname);
731 void cb_cfw_quit_activate(GtkWidget *widget, gpointer uudata)
735 if(msgbox(GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("There are unsaved changes. Do you wish to discard the changes and exit anyway?")) == GTK_RESPONSE_NO)
742 int main(int argc, char **argv)
747 setlocale(LC_ALL, "");
748 bindtextdomain(PACKAGE, LOCALEDIR);
752 gtk_init(&argc, &argv);
754 while((c = getopt(argc, argv, "haw")) != -1) {
764 fprintf((c == 'h')?stdout:stderr, "usage: dolconf [-haw]\n");
765 exit((c == 'h')?0:1);
771 shares = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
772 gtk_tree_view_set_model(GTK_TREE_VIEW(ast_sharelist), GTK_TREE_MODEL(shares));
773 gtk_tree_view_set_model(GTK_TREE_VIEW(cfw_sharelist), GTK_TREE_MODEL(shares));
776 if(getenv("HOME") != NULL) {
777 cfname = sprintf2("%s/.doldacond.conf", getenv("HOME"));
779 if((pwd = getpwuid(getuid())) != NULL)
780 cfname = sprintf2("%s/.doldacond.conf", pwd->pw_dir);
783 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
787 ex = !access(cfname, F_OK);
790 if(msgbox(GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("It appears that you have not run this setup program before. Would you like to run the first-time setup assistant?")) == GTK_RESPONSE_YES)
799 if(ex && (state == 0)) {
800 if(readconfig() == 1) {
801 if(msgbox(GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("The configuration file appears to have been edited outside the control of this program. If you continue using this program, all settings not handled by it will be lost. Do you wish to continue?")) == GTK_RESPONSE_NO)
808 gtk_window_set_default_size(GTK_WINDOW(cfw_wnd), 500, 350);
809 gtk_widget_show(cfw_wnd);
811 rootwnd = GTK_WINDOW(cfw_wnd);
813 gtk_widget_hide(cfw_wnd);
815 } else if(state == 1) {
816 gtk_window_set_default_size(GTK_WINDOW(ast_wnd), 500, 350);
817 gtk_widget_show(ast_wnd);
818 rootwnd = GTK_WINDOW(ast_wnd);
820 gtk_widget_hide(ast_wnd);
823 } else if(state == 2) {
824 for(i = 3; i < FD_SETSIZE; i++)
826 execlp("dolcon", "dolcon", NULL);
830 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Unknown state)"));