Re local X server auth, clarify that it's _us_ you should mail, and link
[u/mdw/putty] / unix / uxstore.c
index b049c28..3d2e241 100644 (file)
@@ -5,7 +5,9 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <assert.h>
+#include <errno.h>
 #include <ctype.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -17,7 +19,7 @@
 #include "tree234.h"
 
 enum {
-    INDEX_DIR, INDEX_HOSTKEYS, INDEX_RANDSEED,
+    INDEX_DIR, INDEX_HOSTKEYS, INDEX_HOSTKEYS_TMP, INDEX_RANDSEED,
     INDEX_SESSIONDIR, INDEX_SESSION,
 };
 
@@ -27,7 +29,7 @@ static char *mungestr(const char *in)
 {
     char *out, *ret;
 
-    if (!in)
+    if (!in || !*in)
         in = "Default Settings";
 
     ret = out = snewn(3*strlen(in)+1, char);
@@ -96,6 +98,7 @@ static void make_filename(char *filename, int index, const char *subname)
                 index == INDEX_DIR ? "/.putty" :
                 index == INDEX_SESSIONDIR ? "/.putty/sessions" :
                 index == INDEX_HOSTKEYS ? "/.putty/sshhostkeys" :
+                index == INDEX_HOSTKEYS_TMP ? "/.putty/sshhostkeys.tmp" :
                 index == INDEX_RANDSEED ? "/.putty/randomseed" :
                 "/.putty/ERROR", FILENAME_MAX - len);
     }
@@ -125,31 +128,32 @@ static char *fgetline(FILE *fp)
     return ret;
 }
 
-/*
- * For the moment, the only existing Unix utility is pterm and that
- * has no GUI configuration at all, so our write routines need do
- * nothing. Eventually I suppose these will read and write an rc
- * file somewhere or other.
- */
-
-void *open_settings_w(const char *sessionname)
+void *open_settings_w(const char *sessionname, char **errmsg)
 {
     char filename[FILENAME_MAX];
     FILE *fp;
 
+    *errmsg = NULL;
+
     /*
-     * Start by making sure the sessions subdir exists. Ignore the
-     * error return from mkdir since it's perfectly likely to be
-     * `already exists', and any other error will trip us up later
-     * on so there's no real need to catch it now.
+     * Start by making sure the .putty directory and its sessions
+     * subdir actually exist. Ignore error returns from mkdir since
+     * they're perfectly likely to be `already exists', and any
+     * other error will trip us up later on so there's no real need
+     * to catch it now.
      */
+    make_filename(filename, INDEX_DIR, sessionname);
+    mkdir(filename, 0700);
     make_filename(filename, INDEX_SESSIONDIR, sessionname);
     mkdir(filename, 0700);
 
     make_filename(filename, INDEX_SESSION, sessionname);
     fp = fopen(filename, "w");
-    if (!fp)
-       return NULL;                   /* can't open */
+    if (!fp) {
+        *errmsg = dupprintf("Unable to create %s: %s",
+                            filename, strerror(errno));
+       return NULL;                   /* can't open */
+    }
     return fp;
 }
 
@@ -178,6 +182,7 @@ void close_settings_w(void *handle)
  * PuTTY's inbuilt defaults, but that the disk files will then
  * override those. This isn't optimal, but it's the best I can
  * immediately work out.
+ * FIXME: the above comment is a bit out of date. Did it happen?
  */
 
 struct keyval {
@@ -484,32 +489,55 @@ int verify_host_key(const char *hostname, int port,
            break;
     }
 
+    fclose(fp);
     return ret;
 }
 
 void store_host_key(const char *hostname, int port,
                    const char *keytype, const char *key)
 {
-    FILE *fp;
-    int fd;
-    char filename[FILENAME_MAX];
+    FILE *rfp, *wfp;
+    char *newtext, *line;
+    int headerlen;
+    char filename[FILENAME_MAX], tmpfilename[FILENAME_MAX];
 
-    make_filename(filename, INDEX_HOSTKEYS, NULL);
-    fd = open(filename, O_CREAT | O_APPEND | O_RDWR, 0600);
-    if (fd < 0) {
-       char dir[FILENAME_MAX];
+    newtext = dupprintf("%s@%d:%s %s\n", keytype, port, hostname, key);
+    headerlen = 1 + strcspn(newtext, " ");   /* count the space too */
 
-       make_filename(dir, INDEX_DIR, NULL);
-       mkdir(dir, 0700);
-       fd = open(filename, O_CREAT | O_APPEND | O_RDWR, 0600);
+    /*
+     * Open both the old file and a new file.
+     */
+    make_filename(filename, INDEX_HOSTKEYS, NULL);
+    rfp = fopen(filename, "r");
+    if (!rfp)
+       return;
+    make_filename(tmpfilename, INDEX_HOSTKEYS_TMP, NULL);
+    wfp = fopen(tmpfilename, "w");
+    if (!wfp) {
+       fclose(rfp);
+       return;
     }
-    if (fd < 0) {
-       perror(filename);
-       exit(1);
+
+    /*
+     * Copy all lines from the old file to the new one that _don't_
+     * involve the same host key identifier as the one we're adding.
+     */
+    while ( (line = fgetline(rfp)) ) {
+       if (strncmp(line, newtext, headerlen))
+           fputs(line, wfp);
     }
-    fp = fdopen(fd, "a");
-    fprintf(fp, "%s@%d:%s %s\n", keytype, port, hostname, key);
-    fclose(fp);
+
+    /*
+     * Now add the new line at the end.
+     */
+    fputs(newtext, wfp);
+
+    fclose(rfp);
+    fclose(wfp);
+
+    rename(tmpfilename, filename);
+
+    sfree(newtext);
 }
 
 void read_random_seed(noise_consumer_t consumer)