chiark / gitweb /
Switch to GPL v3
[disorder] / disobedience / login.c
index 0c50a9cf7c96c78f3489d72aaca5eac5f9f5b350..1c6df79c57107c834dd9bc21fd74ff136e5f23e9 100644 (file)
@@ -1,29 +1,39 @@
 /*
  * This file is part of DisOrder
- * Copyright (C) 2007 Richard Kettlewell
+ * Copyright (C) 2007, 2008 Richard Kettlewell
  *
- * This program is free software; you can redistribute it and/or modify
+ * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 /** @file disobedience/login.c
  * @brief Login box for Disobedience
+ *
+ * As of 2.1 we have only two buttons: Login and Cancel.
+ *
+ * If you hit Login then a login is attempted.  If it works the window
+ * disappears and the settings are saved, otherwise they are NOT saved and the
+ * window remains.
+ *
+ * It you hit Cancel then the window disappears without saving anything.
+ *
+ * TODO
+ * - cancel/close should be consistent with properties
  */
 
 #include "disobedience.h"
 #include "split.h"
 #include "filepart.h"
+#include "client.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -37,11 +47,12 @@ struct login_window_item {
   const char *(*get)(void);
 
   /** @brief Set a new value */
-  void (*set)(const char *value);
+  void (*set)(struct config *c, const char *value);
 
   /** @brief Flags
    * 
    * - @ref LWI_HIDDEN - this is a password
+   * - @ref LWI_REMOTE - this is for remote connections
    */
   unsigned flags;
 
@@ -50,91 +61,93 @@ struct login_window_item {
 /** @brief This is a password */
 #define LWI_HIDDEN 0x0001
 
+/** @brief This is for remote connections */
+#define LWI_REMOTE 0x0002
+
 /** @brief Current login window */
-static GtkWidget *login_window;
+GtkWidget *login_window;
 
 /** @brief Set connection defaults */
 static void default_connect(void) {
-  if(!config->connect.n) {
-    config->connect.n = 2;
-    config->connect.s = xcalloc(2, sizeof (char *));
-    config->connect.s[0] = xstrdup("localhost");
-    config->connect.s[1] = xstrdup("9999"); /* whatever */
-  }
+  /* If a password is set assume we're good */
+  if(config->password)
+    return;
+  /* If we already have a host and/or port that's good too */
+  if(config->connect.n)
+    return;
+  /* If there's a suitable socket that's probably what we wanted */
+  const char *s = config_get_file("socket");
+  struct stat st;
+  if(s && *s && stat(s, &st) == 0 && S_ISSOCK(st.st_mode))
+    return;
+  /* TODO can we use some mdns thing to find a DisOrder server? */
+}
+
+static const char *get_hostname(void) {
+  return config->connect.n >= 2 ? config->connect.s[0] : "";
+}
+
+static const char *get_service(void) {
+  return config->connect.n >= 2 ? config->connect.s[1] : "";
+}
+
+static const char *get_username(void) {
+  return config->username;
 }
 
-static const char *get_hostname(void) { return config->connect.s[0]; }
-static const char *get_service(void) { return config->connect.s[1]; }
-static const char *get_username(void) { return config->username; }
-static const char *get_password(void) { return config->password; }
+static const char *get_password(void) {
+  return config->password ? config->password : "";
+}
 
-static void set_hostname(const char *s) { config->connect.s[0] = (char *)s; }
-static void set_service(const char *s) { config->connect.s[1] = (char *)s; }
-static void set_username(const char *s) { config->username = s; }
-static void set_password(const char *s) { config->password = s; }
+static void set_hostname(struct config *c, const char *s) {
+  c->connect.s[0] = (char *)s;
+}
+
+static void set_service(struct config *c, const char *s) {
+  c->connect.s[1] = (char *)s;
+}
+
+static void set_username(struct config *c, const char *s) {
+  c->username = s;
+}
+
+static void set_password(struct config *c, const char *s) {
+  c->password = s;
+}
 
 /** @brief Table used to generate the form */
 static const struct login_window_item lwis[] = {
-  { "Hostname", get_hostname, set_hostname, 0 },
-  { "Service", get_service, set_service, 0 },
+  { "Hostname", get_hostname, set_hostname, LWI_REMOTE },
+  { "Service", get_service, set_service, LWI_REMOTE },
   { "User name", get_username, set_username, 0 },
   { "Password", get_password, set_password, LWI_HIDDEN },
 };
 #define NLWIS (sizeof lwis / sizeof *lwis)
 
+static GtkWidget *lwi_remote;
 static GtkWidget *lwi_entry[NLWIS];
 
-static void update_config(void) {
-  size_t n;
-
-  for(n = 0; n < NLWIS; ++n)
-    lwis[n].set(xstrdup(gtk_entry_get_text(GTK_ENTRY(lwi_entry[n]))));
-}
-
-#if 0
-static int modified_config(void) {
+static void login_update_config(struct config *c) {
   size_t n;
+  const gboolean remote = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lwi_remote));
 
-  for(n = 0; n < NLWIS; ++n) {
-    const char *entered = gtk_entry_get_text(GTK_ENTRY(lwi_entry[n]));
-    const char *current = lwis[n].get();
-    if(strcmp(entered, current))
-      return 1;
+  if(remote) {
+    c->connect.n = 2;
+    c->connect.s = xcalloc(2, sizeof (char *));
+  } else {
+    c->connect.n = 0;
+    c->connect.s = 0;
   }
-  return 0;
-}
-#endif
-
-static void login_ok(GtkButton attribute((unused)) *button,
-                     gpointer attribute((unused)) userdata) {
-  update_config();
-  reset();
+  for(n = 0; n < NLWIS; ++n)
+    if(remote || !(lwis[n].flags & LWI_REMOTE))
+      lwis[n].set(c, xstrdup(gtk_entry_get_text(GTK_ENTRY(lwi_entry[n]))));
 }
 
-static void login_save(GtkButton attribute((unused)) *button,
-                       gpointer attribute((unused)) userdata) {
+/** @brief Save current login details */
+static void login_save_config(void) {
   char *path = config_userconf(0, 0), *tmp;
   FILE *fp;
-  GtkWidget *yorn = 0;
-
-  update_config();
-  /* See if the file already exists */
-  if(access(path, F_OK) == 0) {
-    yorn = gtk_message_dialog_new
-      (GTK_WINDOW(login_window),
-       GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
-       GTK_MESSAGE_QUESTION,
-       GTK_BUTTONS_NONE,
-       "File %s already exists", path);
-    gtk_dialog_add_buttons(GTK_DIALOG(yorn),
-                           "Overwrite", GTK_RESPONSE_ACCEPT,
-                           "Cancel", GTK_RESPONSE_REJECT,
-                           (char *)0);
-    if(gtk_dialog_run(GTK_DIALOG(yorn)) != GTK_RESPONSE_ACCEPT)
-      goto done;
-    gtk_widget_destroy(yorn);
-    yorn = 0;
-  }
+
   byte_xasprintf(&tmp, "%s.tmp", path);
   /* Make sure the directory exists; don't care if it already exists. */
   mkdir(d_dirname(tmp), 02700);
@@ -144,13 +157,15 @@ static void login_save(GtkButton attribute((unused)) *button,
                tmp, strerror(errno));
     goto done;
   }
-  if(fprintf(fp, "username %s\n"
-             "password %s\n"
-             "connect %s %s\n",
-             quoteutf8(config->username),
-             quoteutf8(config->password),
-             quoteutf8(config->connect.s[0]),
-             quoteutf8(config->connect.s[1])) < 0) {
+  int rc = fprintf(fp, "username %s\n"
+                   "password %s\n",
+                   quoteutf8(config->username),
+                   quoteutf8(config->password));
+  if(rc >= 0 && config->connect.n)
+    rc = fprintf(fp, "connect %s %s\n",
+                 quoteutf8(config->connect.s[0]),
+                 quoteutf8(config->connect.s[1]));
+  if(rc < 0) {
     fpopup_msg(GTK_MESSAGE_ERROR, "error writing to %s: %s",
                tmp, strerror(errno));
     fclose(fp);
@@ -167,39 +182,92 @@ static void login_save(GtkButton attribute((unused)) *button,
                tmp, strerror(errno));
     goto done;
   }
-  fpopup_msg(GTK_MESSAGE_INFO, "Saved login configuration to %s", path);
-  gtk_widget_destroy(login_window);
 done:
-  if(yorn)
-    gtk_widget_destroy(yorn);
+  ;
+}
+
+/** @brief User pressed OK in login window */
+static void login_ok(GtkButton attribute((unused)) *button,
+                     gpointer attribute((unused)) userdata) {
+  disorder_client *c;
+  struct config *tmpconfig = xmalloc(sizeof *tmpconfig);
+  
+  tmpconfig->home = xstrdup(pkgstatedir);
+  /* Copy the new config into @ref config */
+  login_update_config(tmpconfig);
+  /* Attempt a login with the new details */
+  c = disorder_new(0);
+  if(!disorder_connect_generic(tmpconfig, c,
+                               tmpconfig->username, tmpconfig->password,
+                               NULL/*cookie*/)) {
+    /* Success; save the config and start using it */
+    login_update_config(config);
+    login_save_config();
+    logged_in();
+    /* Pop down login window */
+    gtk_widget_destroy(login_window);
+  } else {
+    /* Failed to connect - report the error */
+    popup_msg(GTK_MESSAGE_ERROR, disorder_last(c));
+  }
+  disorder_close(c);                    /* no use for this any more */
 }
 
+/** @brief User pressed cancel in the login window */
 static void login_cancel(GtkButton attribute((unused)) *button,
                          gpointer attribute((unused)) userdata) {
   gtk_widget_destroy(login_window);
 }
 
+/** @brief Keypress handler */
+static gboolean login_keypress(GtkWidget attribute((unused)) *widget,
+                               GdkEventKey *event,
+                               gpointer attribute((unused)) user_data) {
+  if(event->state)
+    return FALSE;
+  switch(event->keyval) {
+  case GDK_Return:
+    login_ok(0, 0);
+    return TRUE;
+  case GDK_Escape:
+    login_cancel(0, 0);
+    return TRUE;
+  default:
+    return FALSE;
+  }
+}
+
 /* Buttons that appear at the bottom of the window */
-static const struct button buttons[] = {
+static struct button buttons[] = {
   {
     "Login",
     login_ok,
-    "Login with these settings",
+    "(Re-)connect using these settings",
+    0
   },
   {
-    GTK_STOCK_SAVE,
-    login_save,
-    "Save these settings",
-  },
-  {
-    GTK_STOCK_CANCEL,
+    GTK_STOCK_CLOSE,
     login_cancel,
-    "Discard all changes and close window"
+    "Discard changes and close window",
+    0
   },
 };
 
 #define NBUTTONS (int)(sizeof buttons / sizeof *buttons)
 
+/** @brief Called when the remote/local button is toggled (and initially)
+ *
+ * Sets the sensitivity of the host/port entries.
+ */
+static void lwi_remote_toggled(GtkToggleButton *togglebutton,
+                               gpointer attribute((unused)) user_data) {
+  const gboolean remote = gtk_toggle_button_get_active(togglebutton);
+  
+  for(unsigned n = 0; n < NLWIS; ++n)
+    if(lwis[n].flags & LWI_REMOTE)
+      gtk_widget_set_sensitive(lwi_entry[n], remote);
+}
+
 /** @brief Pop up a login box */
 void login_box(void) {
   GtkWidget *table, *label, *entry,  *buttonbox, *vbox;
@@ -213,39 +281,71 @@ void login_box(void) {
   default_connect();
   /* Create a new login window */
   login_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_style(login_window, tool_style);
   g_signal_connect(login_window, "destroy",
                   G_CALLBACK(gtk_widget_destroyed), &login_window);
   gtk_window_set_title(GTK_WINDOW(login_window), "Login Details");
   /* Construct the form */
-  table = gtk_table_new(NLWIS + 1/*rows*/, 2/*columns*/, FALSE/*homogenous*/);
+  table = gtk_table_new(1 + NLWIS/*rows*/, 2/*columns*/, FALSE/*homogenous*/);
+  gtk_widget_set_style(table, tool_style);
+  label = gtk_label_new("Remote");
+  gtk_widget_set_style(label, tool_style);
+  gtk_misc_set_alignment(GTK_MISC(label), 1/*right*/, 0/*bottom*/);
+  gtk_table_attach(GTK_TABLE(table), label,
+                   0, 1,                /* left/right_attach */
+                   0, 1,                /* top/bottom_attach */
+                   GTK_FILL, 0,         /* x/yoptions */
+                   1, 1);               /* x/ypadding */
+  lwi_remote = gtk_check_button_new();
+  gtk_widget_set_style(lwi_remote, tool_style);
+  gtk_table_attach(GTK_TABLE(table), lwi_remote,
+                   1, 2,                   /* left/right_attach */
+                   0, 1,                   /* top/bottom_attach */
+                   GTK_EXPAND|GTK_FILL, 0, /* x/yoptions */
+                   1, 1);                  /* x/ypadding */
+  g_signal_connect(lwi_remote, "toggled", G_CALLBACK(lwi_remote_toggled), 0);
   for(n = 0; n < NLWIS; ++n) {
     label = gtk_label_new(lwis[n].description);
+    gtk_widget_set_style(label, tool_style);
     gtk_misc_set_alignment(GTK_MISC(label), 1/*right*/, 0/*bottom*/);
     gtk_table_attach(GTK_TABLE(table), label,
                     0, 1,              /* left/right_attach */
-                    n, n+1,            /* top/bottom_attach */
+                    n+1, n+2,          /* top/bottom_attach */
                     GTK_FILL, 0,       /* x/yoptions */
                     1, 1);             /* x/ypadding */
     entry = gtk_entry_new();
+    gtk_widget_set_style(entry, tool_style);
     gtk_entry_set_visibility(GTK_ENTRY(entry),
                              lwis[n].flags & LWI_HIDDEN ? FALSE : TRUE);
     gtk_entry_set_text(GTK_ENTRY(entry), lwis[n].get());
     gtk_table_attach(GTK_TABLE(table), entry,
                     1, 2,              /* left/right_attach */
-                    n, n+1,            /* top/bottom_attach */
+                    n+1, n+2,          /* top/bottom_attach */
                     GTK_EXPAND|GTK_FILL, 0, /* x/yoptions */
                     1, 1);             /* x/ypadding */
     lwi_entry[n] = entry;
   }
+  /* Initial settings */
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lwi_remote),
+                               config->connect.n >= 2);
+  lwi_remote_toggled(GTK_TOGGLE_BUTTON(lwi_remote), 0);
   buttonbox = create_buttons(buttons, NBUTTONS);
   vbox = gtk_vbox_new(FALSE, 1);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                     gtk_image_new_from_pixbuf(find_image("logo256.png")),
+                     TRUE/*expand*/,
+                     TRUE/*fill*/,
+                     4/*padding*/);
   gtk_box_pack_start(GTK_BOX(vbox), table, 
                      TRUE/*expand*/, TRUE/*fill*/, 1/*padding*/);
   gtk_box_pack_start(GTK_BOX(vbox), buttonbox,
                      FALSE/*expand*/, FALSE/*fill*/, 1/*padding*/);
-  gtk_container_add(GTK_CONTAINER(login_window), vbox);
+  gtk_container_add(GTK_CONTAINER(login_window), frame_widget(vbox, NULL));
   gtk_window_set_transient_for(GTK_WINDOW(login_window),
                                GTK_WINDOW(toplevel));
+  /* Keyboard shortcuts */
+  g_signal_connect(login_window, "key-press-event",
+                   G_CALLBACK(login_keypress), 0);
   gtk_widget_show_all(login_window);
 }