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 void astcancel(GtkWidget *widget, gpointer uudata)
509 #define bufcats(buf, str) bufcat(buf, str, strlen(str))
511 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata)
521 for(var = config; var->name != NULL; var++) {
522 if(var->rname == NULL)
524 bufcats(s, var->rname);
526 bufcat(s, var->val, strlen(var->val));
529 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
531 bufcats(s, _("Shares:\n"));
534 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf, -1);
538 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
540 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(ast_summary)), s, sdata);
544 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata)
548 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_dolcon)))
550 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_exit)))
557 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata)
559 if(v_dcstring(gtk_entry_get_text(GTK_ENTRY(ast_nick))))
560 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, TRUE);
562 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, FALSE);
565 int hasshare(int col, char *name)
570 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
572 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, col, &buf, -1);
573 if(!strcmp(buf, name)) {
578 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
592 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);
593 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(chd), TRUE);
594 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chd), TRUE);
595 resp = gtk_dialog_run(GTK_DIALOG(chd));
596 if(resp != GTK_RESPONSE_ACCEPT) {
597 gtk_widget_destroy(chd);
601 fns = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chd));
602 gtk_widget_destroy(chd);
605 if(!hasshare(1, fn)) {
606 if((p = strrchr(fn, '/')) == NULL)
611 if(hasshare(0, sn)) {
614 sn = sprintf2("%s%i", p, i);
619 gtk_list_store_append(shares, &iter);
620 gtk_list_store_set(shares, &iter, 0, sn, 1, fn, -1);
632 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata)
635 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, TRUE);
638 void cb_cfw_shareadd_clicked(GtkWidget *widget, gpointer uudata)
643 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata)
647 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(ast_sharelist)), NULL, &iter))
648 gtk_list_store_remove(shares, &iter);
649 if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(shares), NULL) == 0)
650 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, FALSE);
653 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata)
657 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(cfw_sharelist)), NULL, &iter))
658 gtk_list_store_remove(shares, &iter);
661 void cb_ast_checkports(GtkWidget *widget, gpointer uudata)
663 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3,
664 v_natural(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) &&
665 (atoi(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) >= 1024) &&
666 v_natural(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) &&
667 (atoi(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) >= 1024) &&
668 v_ipv4(gtk_entry_get_text(GTK_ENTRY(ast_extip))));
671 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata)
673 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
674 gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), TRUE);
675 cb_ast_checkports(widget, NULL);
677 gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), FALSE);
678 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3, TRUE);
682 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata)
684 gtk_widget_set_sensitive(GTK_WIDGET(cfw_natbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
687 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata)
689 gtk_widget_set_sensitive(GTK_WIDGET(cfw_portbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
692 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata)
694 gtk_widget_set_sensitive(GTK_WIDGET(cfw_addrbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
697 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata)
699 gtk_widget_set_sensitive(GTK_WIDGET(cfw_uibox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
702 void cb_cfw_save_activate(GtkWidget *widget, gpointer uudata)
706 for(cv = config; cv->name != NULL; cv++) {
707 if((cv->vld != NULL) && !cv->vld->check(cv->val)) {
709 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, cv->vld->invmsg, cv->rname);
711 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Auto-generated variable %s has an invalid value \"%s\")"), cv->name, cv->val);
720 void cb_cfw_quit_activate(GtkWidget *widget, gpointer uudata)
726 int main(int argc, char **argv)
731 setlocale(LC_ALL, "");
732 bindtextdomain(PACKAGE, LOCALEDIR);
736 gtk_init(&argc, &argv);
738 while((c = getopt(argc, argv, "haw")) != -1) {
748 fprintf((c == 'h')?stdout:stderr, "usage: dolconf [-haw]\n");
749 exit((c == 'h')?0:1);
755 shares = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
756 gtk_tree_view_set_model(GTK_TREE_VIEW(ast_sharelist), GTK_TREE_MODEL(shares));
757 gtk_tree_view_set_model(GTK_TREE_VIEW(cfw_sharelist), GTK_TREE_MODEL(shares));
760 if(getenv("HOME") != NULL) {
761 cfname = sprintf2("%s/.doldacond.conf", getenv("HOME"));
763 if((pwd = getpwuid(getuid())) != NULL)
764 cfname = sprintf2("%s/.doldacond.conf", pwd->pw_dir);
767 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
771 ex = !access(cfname, F_OK);
774 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)
783 if(ex && (state == 0)) {
784 if(readconfig() == 1) {
785 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)
792 gtk_window_set_default_size(GTK_WINDOW(cfw_wnd), 500, 350);
793 gtk_widget_show(cfw_wnd);
795 rootwnd = GTK_WINDOW(cfw_wnd);
797 gtk_widget_hide(cfw_wnd);
799 } else if(state == 1) {
800 gtk_window_set_default_size(GTK_WINDOW(ast_wnd), 500, 350);
801 gtk_widget_show(ast_wnd);
802 rootwnd = GTK_WINDOW(ast_wnd);
804 gtk_widget_hide(ast_wnd);
807 } else if(state == 2) {
808 for(i = 3; i < FD_SETSIZE; i++)
810 execlp("dolcon", "dolcon", NULL);
814 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Unknown state)"));