chiark / gitweb /
'cookie' response now includes the username.
[disorder] / lib / client.c
index 0c12c90167aed0efa160812111c1d42bc7d7e793..fcb0dba6740ac0774b3a0ebab8c77011bb72217d 100644 (file)
@@ -66,19 +66,13 @@ struct disorder_client {
   int verbose;
 };
 
-static int disorder_connect_sock(disorder_client *c,
-                                const struct sockaddr *sa,
-                                socklen_t len,
-                                const char *username,
-                                const char *password,
-                                const char *ident);
-
 /** @brief Create a new client
  * @param verbose If nonzero, write extra junk to stderr
  * @return Pointer to new client
  *
- * You must call disorder_connect() to connect it.  Use
- * disorder_close() to dispose of the client when finished with it.
+ * You must call disorder_connect() or disorder_connect_cookie() to
+ * connect it.  Use disorder_close() to dispose of the client when
+ * finished with it.
  */
 disorder_client *disorder_new(int verbose) {
   disorder_client *c = xmalloc(sizeof (struct disorder_client));
@@ -208,68 +202,58 @@ static int disorder_simple(disorder_client *c,
   return ret;
 }
 
-static int connect_sock(void *vc,
-                       const struct sockaddr *sa,
-                       socklen_t len,
-                       const char *ident) {
-  const char *username, *password;
-  disorder_client *c = vc;
-  
-  if(!(username = config->username)) {
-    error(0, "no username configured");
-    return -1;
-  }
-  password = config->password;
-  if(!password) {
-    /* Maybe we can read the database */
-    /* TODO failure to open the database should not be fatal */
-    trackdb_init(TRACKDB_NO_RECOVER|TRACKDB_NO_UPGRADE);
-    trackdb_open(TRACKDB_READ_ONLY);
-    password = trackdb_get_password(username);
-    trackdb_close();
-  }
-  if(!password) {
-    /* Oh well */
-    error(0, "no password configured");
-    return -1;
+/** @brief Dequote a result string
+ * @param rc 0 on success, non-0 on error
+ * @param rp Where result string is stored (UTF-8)
+ * @return @p rc
+ *
+ * This is used as a wrapper around disorder_simple() to dequote
+ * results in place.
+ */
+static int dequote(int rc, char **rp) {
+  char **rr;
+  if(!rc) {
+    if((rr = split(*rp, 0, SPLIT_QUOTES, 0, 0)) && *rr) {
+      *rp = *rr;
+      return 0;
+    }
+    error(0, "invalid reply: %s", *rp);
   }
-  return disorder_connect_sock(c, sa, len, username, password, ident);
+  return -1;
 }
 
-/** @brief Connect a client
+/** @brief Generic connection routine
  * @param c Client
+ * @param username Username to log in with or NULL
+ * @param password Password to log in with or NULL
+ * @param cookie Cookie to log in with or NULL
  * @return 0 on success, non-0 on error
  *
- * The connection will use the username and password found in @ref
- * config.
+ * @p cookie is tried first if not NULL.  If it is NULL then @p
+ * username must not be.  If @p username is not NULL then nor may @p
+ * password be.
  */
-int disorder_connect(disorder_client *c) {
-  return with_sockaddr(c, connect_sock);
-}
-
-static int disorder_connect_sock(disorder_client *c,
-                                const struct sockaddr *sa,
-                                socklen_t len,
-                                const char *username,
-                                const char *password,
-                                const char *ident) {
+static int disorder_connect_generic(disorder_client *c,
+                                   const char *username,
+                                   const char *password,
+                                   const char *cookie) {
   int fd = -1, fd2 = -1, nrvec;
   unsigned char *nonce;
   size_t nl;
   const char *res;
   char *r, **rvec;
   const char *protocol, *algorithm, *challenge;
+  struct sockaddr *sa;
+  socklen_t salen;
 
-  if(!password) {
-    error(0, "no password found");
+  if((salen = find_server(&sa, &c->ident)) == (socklen_t)-1)
     return -1;
-  }
   c->fpin = c->fpout = 0;
   if((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
     error(errno, "error calling socket");
     return -1;
   }
-  if(connect(fd, sa, len) < 0) {
+  if(connect(fd, sa, salen) < 0) {
     error(errno, "error calling connect");
     goto error;
   }
@@ -287,7 +271,6 @@ static int disorder_connect_sock(disorder_client *c,
     goto error;
   }
   fd2 = -1;
-  c->ident = xstrdup(ident);
   if(disorder_simple(c, &r, 0, (const char *)0))
     return -1;
   if(!(rvec = split(r, &nrvec, SPLIT_QUOTES, 0, 0)))
@@ -305,6 +288,15 @@ static int disorder_connect_sock(disorder_client *c,
   challenge = *rvec++;
   if(!(nonce = unhex(challenge, &nl)))
     return -1;
+  if(cookie) {
+    if(!dequote(disorder_simple(c, &c->user, "cookie", cookie, (char *)0),
+               &c->user))
+      return 0;                                /* success */
+    if(!username) {
+      error(0, "cookie did not work and no username available");
+      return -1;
+    }
+  }
   if(!(res = authhash(nonce, nl, password, algorithm))) goto error;
   if(disorder_simple(c, 0, "user", username, res, (char *)0))
     return -1;
@@ -318,6 +310,58 @@ error:
   return -1;
 }
 
+/** @brief Connect a client
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ *
+ * The connection will use the username and password found in @ref
+ * config, or directly from the database if no password is found and
+ * the database is readable (usually only for root).
+ */
+int disorder_connect(disorder_client *c) {
+  const char *username, *password;
+
+  if(!(username = config->username)) {
+    error(0, "no username configured");
+    return -1;
+  }
+  password = config->password;
+  if(!password) {
+    /* Maybe we can read the database */
+    /* TODO failure to open the database should not be fatal */
+    trackdb_init(TRACKDB_NO_RECOVER|TRACKDB_NO_UPGRADE);
+    trackdb_open(TRACKDB_READ_ONLY);
+    password = trackdb_get_password(username);
+    trackdb_close();
+  }
+  if(!password) {
+    /* Oh well */
+    error(0, "no password configured");
+    return -1;
+  }
+  return disorder_connect_generic(c,
+                                 username,
+                                 password,
+                                 0);
+}
+
+/** @brief Connect a client
+ * @param c Client
+ * @param cookie Cookie to log in with, or NULL
+ * @return 0 on success, non-0 on error
+ *
+ * If @p cookie is NULL or does not work then we attempt to log in as
+ * guest instead (so when the cookie expires only an extra round trip
+ * is needed rathre than a complete new login).
+ */
+int disorder_connect_cookie(disorder_client *c,
+                           const char *cookie) {
+  return disorder_connect_generic(c,
+                                 "guest",
+                                 "",
+                                 cookie);
+}
+
 /** @brief Close a client
  * @param c Client
  * @return 0 on succcess, non-0 on errior
@@ -342,6 +386,8 @@ int disorder_close(disorder_client *c) {
     }
     c->fpout = 0;
   }
+  c->ident = 0;
+  c->user = 0;
   return 0;
 }
 
@@ -431,26 +477,6 @@ int disorder_rescan(disorder_client *c) {
   return disorder_simple(c, 0, "rescan", (char *)0);
 }
 
-/** @brief Dequote a result string
- * @param rc 0 on success, non-0 on error
- * @param rp Where result string is stored (UTF-8)
- * @return @p rc
- *
- * This is used as a wrapper around disorder_simple() to dequote
- * results in place.
- */
-static int dequote(int rc, char **rp) {
-  char **rr;
-  if(!rc) {
-    if((rr = split(*rp, 0, SPLIT_QUOTES, 0, 0)) && *rr) {
-      *rp = *rr;
-      return 0;
-    }
-    error(0, "invalid reply: %s", *rp);
-  }
-  return -1;
-}
-
 /** @brief Get server version number
  * @param c Client
  * @param rp Where to store version string (UTF-8)
@@ -593,7 +619,7 @@ static int disorder_simple_list(disorder_client *c,
 
 /** @brief List directories below @p dir
  * @param c Client
- * @param dir Directory to list (UTF-8)
+ * @param dir Directory to list, or NULL for root (UTF-8)
  * @param re Regexp that results must match, or NULL (UTF-8)
  * @param vecp Where to store list (UTF-8)
  * @param nvecp Where to store number of items, or NULL
@@ -606,7 +632,7 @@ int disorder_directories(disorder_client *c, const char *dir, const char *re,
 
 /** @brief List files below @p dir
  * @param c Client
- * @param dir Directory to list (UTF-8)
+ * @param dir Directory to list, or NULL for root (UTF-8)
  * @param re Regexp that results must match, or NULL (UTF-8)
  * @param vecp Where to store list (UTF-8)
  * @param nvecp Where to store number of items, or NULL
@@ -619,7 +645,7 @@ int disorder_files(disorder_client *c, const char *dir, const char *re,
 
 /** @brief List files and directories below @p dir
  * @param c Client
- * @param dir Directory to list (UTF-8)
+ * @param dir Directory to list, or NULL for root (UTF-8)
  * @param re Regexp that results must match, or NULL (UTF-8)
  * @param vecp Where to store list (UTF-8)
  * @param nvecp Where to store number of items, or NULL