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;
52 GtkWindow *rootwnd = NULL;
55 int v_nonempty(const char *val)
57 return(strlen(val) > 0);
60 int v_dcstring(const char *val)
62 return((strchr(val, ' ') == NULL) &&
63 (strchr(val, '|') == NULL) &&
64 (strchr(val, '$') == NULL));
67 int v_natural(const char *val)
78 int v_integer(const char *val)
82 for(f = 1, d = 0; *val; val++, f = 0) {
85 } else if(f && (*val == '-')) {
93 int v_ipv4(const char *val)
97 return(inet_aton(val, &buf) != 0);
102 struct validation nonempty = {
104 .invmsg = _("%s must not be empty"),
107 struct validation dcstring = {
109 .invmsg = _("%s must not contain spaces, `|' or `$'"),
112 struct validation natural = {
114 .invmsg = _("%s must be a natural number"),
117 struct validation integer = {
119 .invmsg = _("%s must be an integer"),
122 struct validation ipv4 = {
124 .invmsg = _("%s must be an IP address"),
127 struct validation *vldxlate[] = {
128 &nonempty, &dcstring, &natural, &integer, &ipv4,
132 struct cfvar config[] = {
133 {"cli.defnick", _("Nickname"), "", &dcstring},
134 {"net.mode", NULL, "0", &natural},
135 {"net.visibleipv4", "IP address", "0.0.0.0", &ipv4},
136 {"ui.onlylocal", NULL, "0", &natural},
137 {"ui.port", NULL, "-1", &integer},
138 {"auth.authless", NULL, "0", &natural},
139 {"transfer.slots", _("Upload slots"), "6", &natural},
140 {"dc.speedstring", _("Connection speed"), "DSL", &dcstring},
141 {"dc.desc", _("Share description"), "", NULL},
142 {"dc.tcpport", _("Direct Connect TCP port"), "0", &natural},
143 {"dc.udpport", _("Direct Connect UDP port"), "0", &natural},
148 #define _(text) gettext(text)
150 void astcancel(GtkWidget *widget, gpointer uudata);
151 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata);
152 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata);
153 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata);
154 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata);
155 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata);
156 void cb_ast_checkports(GtkWidget *widget, gpointer uudata);
157 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata);
159 #include "dolconf-assistant.gtk"
161 struct cfvar *findcfvar(char *name)
165 for(v = config; v->name != NULL; v++) {
166 if(!strcmp(v->name, name))
172 void setcfvar(char *name, const char *val)
178 v->val = sstrdup(val);
181 int msgbox(int type, int buttons, char *format, ...)
188 va_start(args, format);
189 buf = vsprintf2(format, args);
191 swnd = gtk_message_dialog_new(rootwnd, GTK_DIALOG_MODAL, type, buttons, "%s", buf);
192 gtk_window_set_title(GTK_WINDOW(swnd), _("Dolda Connect configurator"));
193 resp = gtk_dialog_run(GTK_DIALOG(swnd));
194 gtk_widget_destroy(swnd);
199 void prepstatic(void)
201 struct validation **v;
204 for(v = vldxlate; *v != NULL; v++)
205 (*v)->invmsg = gettext((*v)->invmsg);
206 for(c = config; c->name != NULL; c++) {
208 c->rname = gettext(c->rname);
209 c->val = sstrdup(c->val);
213 char *getword(char **p)
215 char *buf, *p2, delim;
223 while((p2 = strchr(p2 + 1, delim)) != NULL) {
228 p2 = *p + strlen(*p);
230 buf = smalloc(len + 1);
231 memcpy(buf, *p, len);
233 *p = p2 + ((*p2 == '\"')?1:0);
234 for(p2 = buf; *p2; p2++, len--) {
236 memmove(p2, p2 + 1, len--);
241 char *quoteword(char *word)
253 for(wp = word; *wp != '\0'; wp++)
255 if(!dq && isspace(*wp))
257 if((*wp == '\\') || (*wp == '\"'))
264 bp = buf = smalloc(sizeof(wchar_t) * (numc + numbs + (dq?2:0) + 1));
267 for(wp = word; *wp != '\0'; wp++)
269 if((*wp == '\\') || (*wp == '\"'))
290 if((cf = fopen(cfname, "r")) == NULL) {
291 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not open the configuration file for reading: %s"), strerror(errno));
295 while(fgets(lbuf, sizeof(lbuf), cf) != NULL) {
297 if(lbuf[len - 1] == '\n')
307 if(!strncmp(lbuf, "set ", 4)) {
309 if(((key = getword(&p)) == NULL) || (*(p++) != ' ') || ((val = getword(&p)) == NULL)) {
313 for(var = config; var->name != NULL; var++) {
314 if(!strcmp(var->name, key)) {
316 var->val = sstrdup(val);
320 if(var->name == NULL)
322 } else if(!strncmp(lbuf, "share ", 6)) {
324 if(((key = getword(&p)) == NULL) || (*(p++) != ' ') || ((val = getword(&p)) == NULL)) {
328 gtk_list_store_append(shares, &iter);
329 gtk_list_store_set(shares, &iter, 0, key, 1, val, -1);
330 } else if(!lbuf[0] || lbuf[0] == '#') {
343 void writeconfig(void)
350 if((cf = fopen(cfname, "w")) == NULL) {
351 msgbox(GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("Could not open the configuration file for writing: %s"), strerror(errno));
354 fputs("# This file was generated by dolconf v" VERSION "\n\n", cf);
355 for(var = config; var->name != NULL; var++) {
356 fprintf(cf, "set %s ", var->name);
357 if((buf = quoteword(var->val)) == NULL) {
365 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
369 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 0, &buf2, -1);
370 if((buf = quoteword(buf2)) == NULL) {
378 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf2, -1);
379 if((buf = quoteword(buf2)) == NULL) {
387 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
392 void astcancel(GtkWidget *widget, gpointer uudata)
397 #define bufcats(buf, str) bufcat(buf, str, strlen(str))
399 void astupdate(GtkWidget *widget, GtkWidget *page, gpointer uudata)
406 setcfvar("cli.defnick", gtk_entry_get_text(GTK_ENTRY(ast_nick)));
407 setcfvar("dc.desc", gtk_entry_get_text(GTK_ENTRY(ast_desc)));
408 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_mode_psv))) {
409 setcfvar("net.mode", "1");
411 setcfvar("net.mode", "0");
412 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ast_mode_nat))) {
413 setcfvar("net.visibleipv4", gtk_entry_get_text(GTK_ENTRY(ast_extip)));
414 setcfvar("dc.tcpport", gtk_entry_get_text(GTK_ENTRY(ast_udpport)));
415 setcfvar("dc.udpport", gtk_entry_get_text(GTK_ENTRY(ast_tcpport)));
417 setcfvar("net.visibleipv4", "0.0.0.0");
418 setcfvar("dc.tcpport", "0");
419 setcfvar("dc.udpport", "0");
424 for(var = config; var->name != NULL; var++) {
425 if(var->rname == NULL)
427 bufcats(s, var->rname);
429 bufcat(s, var->val, strlen(var->val));
432 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
434 bufcats(s, _("Shares:\n"));
437 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, 1, &buf, -1);
441 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
443 gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(ast_summary)), s, sdata);
447 void cb_ast_wnd_apply(GtkWidget *widget, gpointer uudata)
453 void cb_ast_nick_changed(GtkWidget *widget, gpointer uudata)
455 if(v_dcstring(gtk_entry_get_text(GTK_ENTRY(ast_nick))))
456 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, TRUE);
458 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page1, FALSE);
461 int hasshare(int col, char *name)
466 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(shares), &iter)) {
468 gtk_tree_model_get(GTK_TREE_MODEL(shares), &iter, col, &buf, -1);
469 if(!strcmp(buf, name)) {
474 } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(shares), &iter));
479 void cb_ast_shareadd_clicked(GtkWidget *widget, gpointer uudata)
488 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);
489 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(chd), TRUE);
490 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chd), TRUE);
491 resp = gtk_dialog_run(GTK_DIALOG(chd));
492 if(resp != GTK_RESPONSE_ACCEPT) {
493 gtk_widget_destroy(chd);
496 fns = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chd));
497 gtk_widget_destroy(chd);
500 if(!hasshare(1, fn)) {
501 if((p = strrchr(fn, '/')) == NULL)
506 if(hasshare(0, sn)) {
509 sn = sprintf2("%s%i", p, i);
514 gtk_list_store_append(shares, &iter);
515 gtk_list_store_set(shares, &iter, 0, sn, 1, fn, -1);
517 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, TRUE);
526 void cb_ast_sharerem_clicked(GtkWidget *widget, gpointer uudata)
530 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(ast_sharelist)), NULL, &iter))
531 gtk_list_store_remove(shares, &iter);
532 if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(shares), NULL) == 0)
533 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page2, FALSE);
536 void cb_ast_checkports(GtkWidget *widget, gpointer uudata)
538 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3,
539 v_natural(gtk_entry_get_text(GTK_ENTRY(ast_tcpport))) &&
540 v_natural(gtk_entry_get_text(GTK_ENTRY(ast_udpport))) &&
541 v_ipv4(gtk_entry_get_text(GTK_ENTRY(ast_extip))));
544 void cb_ast_mode_nat_toggled(GtkWidget *widget, gpointer uudata)
546 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
547 gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), TRUE);
548 cb_ast_checkports(widget, NULL);
550 gtk_widget_set_sensitive(GTK_WIDGET(ast_portbox), FALSE);
551 gtk_assistant_set_page_complete(GTK_ASSISTANT(ast_wnd), ast_page3, TRUE);
555 int main(int argc, char **argv)
559 setlocale(LC_ALL, "");
560 bindtextdomain(PACKAGE, LOCALEDIR);
564 gtk_init(&argc, &argv);
566 shares = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
567 gtk_tree_view_set_model(GTK_TREE_VIEW(ast_sharelist), GTK_TREE_MODEL(shares));
570 if(getenv("HOME") != NULL) {
571 cfname = sprintf2("%s/.doldacond.conf", getenv("HOME"));
573 if((pwd = getpwuid(getuid())) != NULL)
574 cfname = sprintf2("%s/.doldacond.conf", pwd->pw_dir);
577 msgbox(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Could not get your home directory!"));
581 if(access(cfname, F_OK)) {
582 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) {
583 gtk_window_set_default_size(GTK_WINDOW(ast_wnd), 500, 350);
584 gtk_widget_show(ast_wnd);
587 if(readconfig() == 1) {
588 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)