chiark / gitweb /
exercise the C client a bit from tests
[disorder] / disobedience / login.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2007 Richard Kettlewell
4  *
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.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
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
18  * USA
19  */
20 /** @file disobedience/login.c
21  * @brief Login box for Disobedience
22  */
23
24 #include "disobedience.h"
25 #include "split.h"
26 #include "filepart.h"
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30
31 /** @brief One field in the login window */
32 struct login_window_item {
33   /** @brief Description label */
34   const char *description;
35
36   /** @brief Return the current value */
37   const char *(*get)(void);
38
39   /** @brief Set a new value */
40   void (*set)(const char *value);
41
42   /** @brief Flags
43    * 
44    * - @ref LWI_HIDDEN - this is a password
45    */
46   unsigned flags;
47
48 };
49
50 /** @brief This is a password */
51 #define LWI_HIDDEN 0x0001
52
53 /** @brief Current login window */
54 static GtkWidget *login_window;
55
56 /** @brief Set connection defaults */
57 static void default_connect(void) {
58   if(!config->connect.n) {
59     config->connect.n = 2;
60     config->connect.s = xcalloc(2, sizeof (char *));
61     config->connect.s[0] = xstrdup("localhost");
62     config->connect.s[1] = xstrdup("9999"); /* whatever */
63   }
64 }
65
66 static const char *get_hostname(void) { return config->connect.s[0]; }
67 static const char *get_service(void) { return config->connect.s[1]; }
68 static const char *get_username(void) { return config->username; }
69 static const char *get_password(void) { return config->password; }
70
71 static void set_hostname(const char *s) { config->connect.s[0] = (char *)s; }
72 static void set_service(const char *s) { config->connect.s[1] = (char *)s; }
73 static void set_username(const char *s) { config->username = s; }
74 static void set_password(const char *s) { config->password = s; }
75
76 /** @brief Table used to generate the form */
77 static const struct login_window_item lwis[] = {
78   { "Hostname", get_hostname, set_hostname, 0 },
79   { "Service", get_service, set_service, 0 },
80   { "User name", get_username, set_username, 0 },
81   { "Password", get_password, set_password, LWI_HIDDEN },
82 };
83 #define NLWIS (sizeof lwis / sizeof *lwis)
84
85 static GtkWidget *lwi_entry[NLWIS];
86
87 static void update_config(void) {
88   size_t n;
89
90   for(n = 0; n < NLWIS; ++n)
91     lwis[n].set(xstrdup(gtk_entry_get_text(GTK_ENTRY(lwi_entry[n]))));
92 }
93
94 #if 0
95 static int modified_config(void) {
96   size_t n;
97
98   for(n = 0; n < NLWIS; ++n) {
99     const char *entered = gtk_entry_get_text(GTK_ENTRY(lwi_entry[n]));
100     const char *current = lwis[n].get();
101     if(strcmp(entered, current))
102       return 1;
103   }
104   return 0;
105 }
106 #endif
107
108 static void login_ok(GtkButton attribute((unused)) *button,
109                      gpointer attribute((unused)) userdata) {
110   update_config();
111   reset();
112 }
113
114 static void login_save(GtkButton attribute((unused)) *button,
115                        gpointer attribute((unused)) userdata) {
116   char *path = config_userconf(0, 0), *tmp;
117   FILE *fp;
118   GtkWidget *yorn = 0;
119
120   update_config();
121   /* See if the file already exists */
122   if(access(path, F_OK) == 0) {
123     yorn = gtk_message_dialog_new
124       (GTK_WINDOW(login_window),
125        GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
126        GTK_MESSAGE_QUESTION,
127        GTK_BUTTONS_NONE,
128        "File %s already exists.", path);
129     gtk_window_set_title(GTK_WINDOW(yorn),
130                          "Configuration file already exists");
131     gtk_dialog_add_buttons(GTK_DIALOG(yorn),
132                            "Overwrite it", GTK_RESPONSE_ACCEPT,
133                            "Don't save after all", GTK_RESPONSE_REJECT,
134                            (char *)0);
135     if(gtk_dialog_run(GTK_DIALOG(yorn)) != GTK_RESPONSE_ACCEPT)
136       goto done;
137     gtk_widget_destroy(yorn);
138     yorn = 0;
139   }
140   byte_xasprintf(&tmp, "%s.tmp", path);
141   /* Make sure the directory exists; don't care if it already exists. */
142   mkdir(d_dirname(tmp), 02700);
143   /* Write out the file */
144   if(!(fp = fopen(tmp, "w"))) {
145     fpopup_msg(GTK_MESSAGE_ERROR, "error opening %s: %s",
146                tmp, strerror(errno));
147     goto done;
148   }
149   if(fprintf(fp, "username %s\n"
150              "password %s\n"
151              "connect %s %s\n",
152              quoteutf8(config->username),
153              quoteutf8(config->password),
154              quoteutf8(config->connect.s[0]),
155              quoteutf8(config->connect.s[1])) < 0) {
156     fpopup_msg(GTK_MESSAGE_ERROR, "error writing to %s: %s",
157                tmp, strerror(errno));
158     fclose(fp);
159     goto done;
160   }
161   if(fclose(fp) < 0) {
162     fpopup_msg(GTK_MESSAGE_ERROR, "error closing %s: %s",
163                tmp, strerror(errno));
164     goto done;
165   }
166   /* Rename into place */
167   if(rename(tmp, path) < 0) {
168     fpopup_msg(GTK_MESSAGE_ERROR, "error renaming %s: %s",
169                tmp, strerror(errno));
170     goto done;
171   }
172   fpopup_msg(GTK_MESSAGE_INFO, "Saved login configuration to %s", path);
173   gtk_widget_destroy(login_window);
174 done:
175   if(yorn)
176     gtk_widget_destroy(yorn);
177 }
178
179 static void login_cancel(GtkButton attribute((unused)) *button,
180                          gpointer attribute((unused)) userdata) {
181   gtk_widget_destroy(login_window);
182 }
183
184 /* Buttons that appear at the bottom of the window */
185 static const struct button buttons[] = {
186   {
187     "Login",
188     login_ok,
189     "(Re-)connect using these settings",
190   },
191   {
192     GTK_STOCK_SAVE,
193     login_save,
194     "Save these settings and close window",
195   },
196   {
197     GTK_STOCK_CLOSE,
198     login_cancel,
199     "Discard changes and close window"
200   },
201 };
202
203 #define NBUTTONS (int)(sizeof buttons / sizeof *buttons)
204
205 /** @brief Pop up a login box */
206 void login_box(void) {
207   GtkWidget *table, *label, *entry,  *buttonbox, *vbox;
208   size_t n;
209
210   /* If there's one already then bring it to the front */
211   if(login_window) {
212     gtk_window_present(GTK_WINDOW(login_window));
213     return;
214   }
215   default_connect();
216   /* Create a new login window */
217   login_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
218   gtk_widget_set_style(login_window, tool_style);
219   g_signal_connect(login_window, "destroy",
220                    G_CALLBACK(gtk_widget_destroyed), &login_window);
221   gtk_window_set_title(GTK_WINDOW(login_window), "Login Details");
222   /* Construct the form */
223   table = gtk_table_new(NLWIS + 1/*rows*/, 2/*columns*/, FALSE/*homogenous*/);
224   gtk_widget_set_style(table, tool_style);
225   for(n = 0; n < NLWIS; ++n) {
226     label = gtk_label_new(lwis[n].description);
227     gtk_widget_set_style(label, tool_style);
228     gtk_misc_set_alignment(GTK_MISC(label), 1/*right*/, 0/*bottom*/);
229     gtk_table_attach(GTK_TABLE(table), label,
230                      0, 1,              /* left/right_attach */
231                      n, n+1,            /* top/bottom_attach */
232                      GTK_FILL, 0,       /* x/yoptions */
233                      1, 1);             /* x/ypadding */
234     entry = gtk_entry_new();
235     gtk_widget_set_style(entry, tool_style);
236     gtk_entry_set_visibility(GTK_ENTRY(entry),
237                              lwis[n].flags & LWI_HIDDEN ? FALSE : TRUE);
238     gtk_entry_set_text(GTK_ENTRY(entry), lwis[n].get());
239     gtk_table_attach(GTK_TABLE(table), entry,
240                      1, 2,              /* left/right_attach */
241                      n, n+1,            /* top/bottom_attach */
242                      GTK_EXPAND|GTK_FILL, 0, /* x/yoptions */
243                      1, 1);             /* x/ypadding */
244     lwi_entry[n] = entry;
245   }
246   buttonbox = create_buttons(buttons, NBUTTONS);
247   vbox = gtk_vbox_new(FALSE, 1);
248   gtk_box_pack_start(GTK_BOX(vbox), table, 
249                      TRUE/*expand*/, TRUE/*fill*/, 1/*padding*/);
250   gtk_box_pack_start(GTK_BOX(vbox), buttonbox,
251                      FALSE/*expand*/, FALSE/*fill*/, 1/*padding*/);
252   gtk_container_add(GTK_CONTAINER(login_window), vbox);
253   gtk_window_set_transient_for(GTK_WINDOW(login_window),
254                                GTK_WINDOW(toplevel));
255   gtk_widget_show_all(login_window);
256 }
257
258 /*
259 Local Variables:
260 c-basic-offset:2
261 comment-column:40
262 fill-column:79
263 indent-tabs-mode:nil
264 End:
265 */