Some hosts don't have TIOCSCTTY. Don't try to use it on them.
[sgt/putty] / unix / uxstore.c
index ecd3ff1..85d72ca 100644 (file)
@@ -19,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,
 };
 
@@ -85,6 +85,8 @@ static void make_filename(char *filename, int index, const char *subname)
     char *home;
     int len;
     home = getenv("HOME");
+    if (!home)
+        home="/";
     strncpy(filename, home, FILENAME_MAX);
     len = strlen(filename);
     if (index == INDEX_SESSION) {
@@ -98,35 +100,13 @@ 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);
     }
     filename[FILENAME_MAX-1] = '\0';
 }
 
-/*
- * Read an entire line of text from a file. Return a buffer
- * malloced to be as big as necessary (caller must free).
- */
-static char *fgetline(FILE *fp)
-{
-    char *ret = snewn(512, char);
-    int size = 512, len = 0;
-    while (fgets(ret + len, size - len, fp)) {
-       len += strlen(ret + len);
-       if (ret[len-1] == '\n')
-           break;                     /* got a newline, we're done */
-       size = len + 512;
-       ret = sresize(ret, size, char);
-    }
-    if (len == 0) {                   /* first fgets returned NULL */
-       sfree(ret);
-       return NULL;
-    }
-    ret[len] = '\0';
-    return ret;
-}
-
 void *open_settings_w(const char *sessionname, char **errmsg)
 {
     char filename[FILENAME_MAX];
@@ -488,32 +468,60 @@ 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(tmpfilename, INDEX_HOSTKEYS_TMP, NULL);
+    wfp = fopen(tmpfilename, "w");
+    if (!wfp) {
+        char dir[FILENAME_MAX];
+
+        make_filename(dir, INDEX_DIR, NULL);
+        mkdir(dir, 0700);
+        wfp = fopen(tmpfilename, "w");
     }
-    if (fd < 0) {
-       perror(filename);
-       exit(1);
+    if (!wfp)
+       return;
+    make_filename(filename, INDEX_HOSTKEYS, NULL);
+    rfp = fopen(filename, "r");
+
+    /*
+     * 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.
+     */
+    if (rfp) {
+        while ( (line = fgetline(rfp)) ) {
+            if (strncmp(line, newtext, headerlen))
+                fputs(line, wfp);
+        }
+        fclose(rfp);
     }
-    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(wfp);
+
+    rename(tmpfilename, filename);
+
+    sfree(newtext);
 }
 
 void read_random_seed(noise_consumer_t consumer)