chiark / gitweb /
friendlier login details saving
[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_dialog_add_buttons(GTK_DIALOG(yorn),
130                            "Overwrite", GTK_RESPONSE_ACCEPT,
131                            "Cancel", GTK_RESPONSE_REJECT,
132                            (char *)0);
133     if(gtk_dialog_run(GTK_DIALOG(yorn)) != GTK_RESPONSE_ACCEPT)
134       goto done;
135     gtk_widget_destroy(yorn);
136     yorn = 0;
137   }
138   byte_xasprintf(&tmp, "%s.tmp", path);
139   /* Make sure the directory exists; don't care if it already exists. */
140   mkdir(d_dirname(tmp), 02700);
141   /* Write out the file */
142   if(!(fp = fopen(tmp, "w"))) {
143     fpopup_msg(GTK_MESSAGE_ERROR, "error opening %s: %s",
144                tmp, strerror(errno));
145     goto done;
146   }
147   if(fprintf(fp, "username %s\n"
148              "password %s\n"
149              "connect %s %s\n",
150              quoteutf8(config->username),
151              quoteutf8(config->password),
152              quoteutf8(config->connect.s[0]),
153              quoteutf8(config->connect.s[1])) < 0) {
154     fpopup_msg(GTK_MESSAGE_ERROR, "error writing to %s: %s",
155                tmp, strerror(errno));
156     fclose(fp);
157     goto done;
158   }
159   if(fclose(fp) < 0) {
160     fpopup_msg(GTK_MESSAGE_ERROR, "error closing %s: %s",
161                tmp, strerror(errno));
162     goto done;
163   }
164   /* Rename into place */
165   if(rename(tmp, path) < 0) {
166     fpopup_msg(GTK_MESSAGE_ERROR, "error renaming %s: %s",
167                tmp, strerror(errno));
168     goto done;
169   }
170   fpopup_msg(GTK_MESSAGE_INFO, "Saved login configuration to %s", path);
171   gtk_widget_destroy(login_window);
172 done:
173   if(yorn)
174     gtk_widget_destroy(yorn);
175   /* OS X WM likes to hide it */
176   if(login_window)
177     gtk_window_present(GTK_WINDOW(login_window));
178 }
179
180 static void login_cancel(GtkButton attribute((unused)) *button,
181                          gpointer attribute((unused)) userdata) {
182   gtk_widget_destroy(login_window);
183 }
184
185 /* Buttons that appear at the bottom of the window */
186 static const struct button buttons[] = {
187   {
188     "Login",
189     login_ok,
190     "Login with these settings",
191   },
192   {
193     GTK_STOCK_SAVE,
194     login_save,
195     "Save these settings",
196   },
197   {
198     GTK_STOCK_CANCEL,
199     login_cancel,
200     "Discard all changes and close window"
201   },
202 };
203
204 #define NBUTTONS (int)(sizeof buttons / sizeof *buttons)
205
206 /** @brief Pop up a login box */
207 void login_box(void) {
208   GtkWidget *table, *label, *entry,  *buttonbox, *vbox;
209   size_t n;
210
211   /* If there's one already then bring it to the front */
212   if(login_window) {
213     gtk_window_present(GTK_WINDOW(login_window));
214     return;
215   }
216   default_connect();
217   /* Create a new login window */
218   login_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
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   for(n = 0; n < NLWIS; ++n) {
225     label = gtk_label_new(lwis[n].description);
226     gtk_misc_set_alignment(GTK_MISC(label), 1/*right*/, 0/*bottom*/);
227     gtk_table_attach(GTK_TABLE(table), label,
228                      0, 1,              /* left/right_attach */
229                      n, n+1,            /* top/bottom_attach */
230                      GTK_FILL, 0,       /* x/yoptions */
231                      1, 1);             /* x/ypadding */
232     entry = gtk_entry_new();
233     gtk_entry_set_visibility(GTK_ENTRY(entry),
234                              lwis[n].flags & LWI_HIDDEN ? FALSE : TRUE);
235     gtk_entry_set_text(GTK_ENTRY(entry), lwis[n].get());
236     gtk_table_attach(GTK_TABLE(table), entry,
237                      1, 2,              /* left/right_attach */
238                      n, n+1,            /* top/bottom_attach */
239                      GTK_EXPAND|GTK_FILL, 0, /* x/yoptions */
240                      1, 1);             /* x/ypadding */
241     lwi_entry[n] = entry;
242   }
243   buttonbox = create_buttons(buttons, NBUTTONS);
244   vbox = gtk_vbox_new(FALSE, 1);
245   gtk_box_pack_start(GTK_BOX(vbox), table, 
246                      TRUE/*expand*/, TRUE/*fill*/, 1/*padding*/);
247   gtk_box_pack_start(GTK_BOX(vbox), buttonbox,
248                      FALSE/*expand*/, FALSE/*fill*/, 1/*padding*/);
249   gtk_container_add(GTK_CONTAINER(login_window), vbox);
250   gtk_widget_show_all(login_window);
251 }
252
253 /*
254 Local Variables:
255 c-basic-offset:2
256 comment-column:40
257 fill-column:79
258 indent-tabs-mode:nil
259 End:
260 */