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>
34 #include <doldaconnect/uilib.h>
35 #include <doldaconnect/uimisc.h>
43 int (*check)(const char *val);
51 struct validation *vld;
52 GtkWidget **astw, **cfww;
56 GtkWindow *rootwnd = NULL;
61 #define _(text) gettext(text)
63 #include "dolconf-assistant.gtkh"
64 #include "dolconf-wnd.gtkh"
66 int v_nonempty(const char *val)
68 return(strlen(val) > 0);
71 int v_dcstring(const char *val)
73 return((strchr(val, ' ') == NULL) &&
74 (strchr(val, '|') == NULL) &&
75 (strchr(val, '$') == NULL));
78 int v_natural(const char *val)
89 int v_integer(const char *val)
93 for(f = 1, d = 0; *val; val++, f = 0) {
96 } else if(f && (*val == '-')) {
104 int v_ipv4(const char *val)
108 return(inet_aton(val, &buf) != 0);
114 struct validation nonempty = {
116 .invmsg = _("%s must not be empty"),
119 struct validation dcstring = {
121 .invmsg = _("%s must not contain spaces, `|' or `$'"),
124 struct validation natural = {
126 .invmsg = _("%s must be a natural number"),
129 struct validation integer = {
131 .invmsg = _("%s must be an integer"),
134 struct validation ipv4 = {
136 .invmsg = _("%s must be an IP address"),
139 struct validation *vldxlate[] = {
140 &nonempty, &dcstring, &natural, &integer, &ipv4,
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},
161 #define _(text) gettext(text)
163 struct cfvar *findcfvar(char *name)
167 for(v = config; v->name != NULL; v++) {
168 if(!strcmp(v->name, name))
174 void setcfvar(char *name, const char *val)
179 if(!strcmp(v->val, val))
182 v->val = sstrdup(val);
186 int msgbox(int type, int buttons, char *format, ...)
193 va_start(args, format);
194 buf = vsprintf2(format, 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);
204 void prepstatic(void)
206 struct validation **v;
209 for(v = vldxlate; *v != NULL; v++)
210 (*v)->invmsg = gettext((*v)->invmsg);
211 for(c = config; c->name != NULL; c++) {
213 c->rname = gettext(c->rname);
214 c->val = sstrdup(c->val);
218 char *getword(char **p)
220 char *buf, *p2, delim;
228 while((p2 = strchr(p2 + 1, delim)) != NULL) {
233 p2 = *p + strlen(*p);
234 len = p2 - *p - ((*p2 == '\"')?1:0);
235 buf = smalloc(len + 1);
236 memcpy(buf, *p, len);
238 *p = p2 + ((*p2 == '\"')?1:0);
239 for(p2 = buf; *p2; p2++, len--) {
241 memmove(p2, p2 + 1, len--);
246 char *quoteword(char *word)
258 for(wp = word; *wp != '\0'; wp++)
260 if(!dq && isspace(*wp))
262 if((*wp == '\\') || (*wp == '\"'))
269 bp = buf = smalloc(sizeof(wchar_t) * (numc + numbs + (dq?2:0) + 1));
272 for(wp = word; *wp != '\0'; wp++)
274 if((*wp == '\\') || (*wp == '\"'))
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));
300 while(fgets(lbuf, sizeof(lbuf), cf) != NULL) {
302 if(lbuf[len - 1] == '\n')
312 if(!strncmp(lbuf, "set ", 4)) {
314 if(((key = getword(&p)) == NULL) || (*(p++) != ' ') || ((val = getword(&p)) == NULL)) {
318 for(var = config; var->name != NULL; var++) {
319 if(!strcmp(var->name, key)) {
321 var->val = sstrdup(val);
325 if(var->name == NULL)
327 } else if(!strncmp(lbuf, "share ", 6)) {
329 if(((key = getword(&p)) == NULL) || (*(p++) != ' ') || ((val = getword(&p)) == NULL)) {
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] == '#') {
349 void writeconfig(void)
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));
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) {
371 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
375 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 0, &buf2, -1);
376 if((buf = quoteword(buf2)) == NULL) {
384 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf2, -1);
385 if((buf = quoteword(buf2)) == NULL) {
393 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
403 for(var = config; var->name != NULL; var++) {
404 if(var->cfww != NULL)
405 gtk_entry_set_text(GTK_ENTRY(*(var->cfww)), var->val);
407 if(atoi(findcfvar("dc.tcpport")->val) || atoi(findcfvar("dc.udpport")->val))
408 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_orport), TRUE);
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);
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);
420 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), TRUE);
422 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_uinet), FALSE);
423 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfw_authless), FALSE);
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");
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)));
440 setcfvar("net.visibleipv4", "0.0.0.0");
441 setcfvar("dc.tcpport", "0");
442 setcfvar("dc.udpport", "0");
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))
458 var->val = sstrdup(val);
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");
468 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cfw_oraddr))) {
469 setcfvar("net.visibleipv4", "0.0.0.0");
472 setcfvar("net.mode", "1");
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");
479 setcfvar("auth.authless", "0");
481 setcfvar("ui.port", "-1");
482 setcfvar("auth.authless", "0");
486 struct cfvar *cfwvalid(void)
490 for(cv = config; cv->name != NULL; cv++) {
491 if((cv->vld != NULL) && !cv->vld->check(cv->val)) {
495 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Auto-generated variable %s has an invalid value \"%s\")"), cv->name, cv->val);
503 void astcancel(GtkWidget *widget, gpointer uudata)
511 #define bufcats(buf, str) bufcat(buf, str, strlen(str))
513 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata)
523 for(var = config; var->name != NULL; var++) {
524 if(var->rname == NULL)
526 bufcats(s, var->rname);
528 bufcat(s, var->val, strlen(var->val));
531 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
533 bufcats(s, _("Shares:\n"));
536 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf, -1);
540 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
542 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(ast_summary)), s, sdata);
546 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata)
550 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_dolcon)))
552 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_action_exit)))
559 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata)
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);
564 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, FALSE);
567 int hasshare(int col, char *name)
572 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
574 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, col, &buf, -1);
575 if(!strcmp(buf, name)) {
580 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
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);
603 fns = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chd));
604 gtk_widget_destroy(chd);
607 if(!hasshare(1, fn)) {
608 if((p = strrchr(fn, '/')) == NULL)
613 if(hasshare(0, sn)) {
616 sn = sprintf2("%s%i", p, i);
621 gtk_list_store_append(shares, &iter);
622 gtk_list_store_set(shares, &iter, 0, sn, 1, fn, -1);
634 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata)
637 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, TRUE);
640 void cb_cfw_shareadd_clicked(GtkWidget *widget, gpointer uudata)
646 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata)
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);
656 void cb_cfw_sharerem_clicked(GtkWidget *widget, gpointer uudata)
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);
666 void cb_ast_checkports(GtkWidget *widget, gpointer uudata)
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))));
676 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata)
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);
682 gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), FALSE);
683 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3, TRUE);
687 void cb_cfw_mode_act_toggled(GtkWidget *widget, gpointer uudata)
689 gtk_widget_set_sensitive(GTK_WIDGET(cfw_natbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
692 void cb_cfw_orport_toggled(GtkWidget *widget, gpointer uudata)
694 gtk_widget_set_sensitive(GTK_WIDGET(cfw_portbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
697 void cb_cfw_oraddr_toggled(GtkWidget *widget, gpointer uudata)
699 gtk_widget_set_sensitive(GTK_WIDGET(cfw_addrbox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
702 void cb_cfw_uinet_toggled(GtkWidget *widget, gpointer uudata)
704 gtk_widget_set_sensitive(GTK_WIDGET(cfw_uibox), gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
707 void cb_cfw_hup_activate(GtkWidget *widget, gpointer uudata)
710 struct dc_response *resp;
712 if(dc_connectsync2(dc_srv_local, DC_LATEST) < 0) {
713 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
716 if(dc_login(NULL, 1, dc_convnone, NULL) != DC_LOGIN_ERR_SUCCESS) {
718 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not connect to server"));
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"));
730 void cb_cfw_save_activate(GtkWidget *widget, gpointer uudata)
734 if((cv = cfwvalid()) != NULL) {
735 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, cv->vld->invmsg, cv->rname);
742 void cb_cfw_quit_activate(GtkWidget *widget, gpointer uudata)
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)
753 int main(int argc, char **argv)
758 setlocale(LC_ALL, "");
759 bindtextdomain(PACKAGE, LOCALEDIR);
764 gtk_init(&argc, &argv);
766 while((c = getopt(argc, argv, "haw")) != -1) {
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");
781 fprintf(stderr, "usage: dolconf [-haw]\n");
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));
793 if(getenv("HOME") != NULL) {
794 cfname = sprintf2("%s/.doldacond.conf", getenv("HOME"));
796 if((pwd = getpwuid(getuid())) != NULL)
797 cfname = sprintf2("%s/.doldacond.conf", pwd->pw_dir);
800 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
804 ex = !access(cfname, F_OK);
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)
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)
825 gtk_window_set_default_size(GTK_WINDOW(cfw_wnd), 500, 350);
826 gtk_widget_show(cfw_wnd);
828 rootwnd = GTK_WINDOW(cfw_wnd);
830 gtk_widget_hide(cfw_wnd);
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);
837 gtk_widget_hide(ast_wnd);
840 } else if(state == 2) {
841 for(i = 3; i < FD_SETSIZE; i++)
843 execlp("dolcon-launch", "dolcon-launch", NULL);
844 perror("dolcon-launch");
847 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Internal error (Unknown state)"));
854 #include "dolconf-assistant.gtk"
855 #include "dolconf-wnd.gtk"