home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Networking / ncftp-2.4.2-MIHS / src / Bookmark.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-16  |  19.3 KB  |  836 lines

  1. /* Bookmark.c */
  2.  
  3. #include "Sys.h"
  4.  
  5. #include <ctype.h>
  6.  
  7. /* TO-DO: If I decide to implement strstr matching, watch out for
  8.  * local domain hosts, with simple names.
  9.  */
  10.  
  11. #include "Util.h"
  12. #include "Bookmark.h"
  13. #include "FTP.h"
  14.  
  15. /* We keep an array of structures of all the sites we know about in
  16.  * a contigious block of memory.
  17.  */
  18. BookmarkPtr gHosts = (BookmarkPtr) 0;
  19.  
  20. /* But we also keep an array of pointers to the structures, so we can
  21.  * use that for sorting purposes.  We don't want to have to shuffle
  22.  * large structures around when sorting.  As the name hints, we have
  23.  * this array sorted by nickname, for quick searching.
  24.  */
  25. BookmarkPtrList gBookmarks = (BookmarkPtrList) 0;
  26.  
  27. /* This is the number of elements in both gHosts and gBookmarks. */
  28. int gNumBookmarks = 0;
  29.  
  30. /* We don't want the other portions of the program overwriting the
  31.  * current entry in the gHosts list, because we may want to save our
  32.  * changes under a new entry.  So if the host was in our list, we make
  33.  * a copy of the data, and let them write into that.  Then we can write
  34.  * the changed structure under a new entry, or overwrite the old one.
  35.  * This also works for entirely new entries.  We just give the caller
  36.  * this, initialized to the default values.
  37.  */
  38. Bookmark gRmtInfo = { NULL, NULL, 0, "", "" };
  39.  
  40. /* Used to tell if we got the information from the host information list,
  41.  * or we were using a new entry.
  42.  */
  43. int gRmtInfoIsNew;
  44.  
  45. /* We use this outside of this module to tell if we should actually
  46.  * save the information collected.  We don't want to save it if the
  47.  * stuff wasn't really valid, so we won't save unless you logged in
  48.  * successfully.
  49.  */
  50. int gWantRmtInfoSaved;
  51.  
  52. /* Used to tell if the host file needs to be written back out.
  53.  * If we haven't changed anything, then don't waste the time to
  54.  * write the file.
  55.  */
  56. int gModifiedBookmarks = 0;
  57.  
  58. /* These are the first and last nodes in the linked-list of remote
  59.  * site information structures.
  60.  */
  61. BookmarkPtr gFirstRsi = NULL, gLastRsi = NULL;
  62.  
  63. /* If greater than zero, we will only save the most recent sites, up
  64.  * to this number.
  65.  */
  66. int gMaxBookmarks = kNoBookmarkLimit;
  67.  
  68. extern string gEmailAddress, gAnonPassword;
  69. extern int gPreferredDataPortMode;
  70. extern string gOurDirectoryPath;
  71. extern longstring gRemoteCWD;
  72.  
  73. static
  74. int BookmarkSortProc(const BookmarkPtr *a, const BookmarkPtr *b)
  75. {
  76.     return (ISTRCMP((**a).bookmarkName, (**b).bookmarkName));    
  77. }    /* BookmarkSortProc */
  78.  
  79.  
  80.  
  81. static
  82. int BookmarkSortTimeProc(const BookmarkPtr *a, const BookmarkPtr *b)
  83. {
  84.     return ((**b).lastCall - (**a).lastCall);    
  85. }    /* BookmarkSortTimeProc */
  86.  
  87.  
  88.  
  89. static
  90. int BookmarkSearchProc(char *key, const BookmarkPtr *b)
  91. {
  92.     return (ISTRCMP(key, (**b).bookmarkName));    
  93. }    /* BookmarkSearchProc */
  94.  
  95.  
  96.  
  97.  
  98.  
  99. void SortBookmarks(void)
  100. {
  101.     int i;
  102.     BookmarkPtr p;
  103.  
  104.     if (gBookmarks != (BookmarkPtrList) 0)
  105.         free(gBookmarks);
  106.     gBookmarks = (BookmarkPtrList) malloc(
  107.         sizeof(BookmarkPtr) * (gNumBookmarks + 1)
  108.     );    
  109.     if (gBookmarks == (BookmarkPtrList) 0)
  110.         OutOfMemory();
  111.  
  112.     for (p = gFirstRsi, i=0; p != NULL; i++, p = p->next) {
  113.         gBookmarks[i] = p;
  114.     }
  115.     gBookmarks[gNumBookmarks] = NULL;
  116.  
  117.     QSORT(gBookmarks,
  118.         gNumBookmarks, sizeof(BookmarkPtr), BookmarkSortProc);
  119.     
  120.     for (i=0; i<gNumBookmarks; i++) {
  121.         p = gBookmarks[i];
  122.         p->index = i;
  123.     }
  124. }    /* SortBookmarks */
  125.  
  126.  
  127.  
  128.  
  129. void UpdateBookmarkPtr(BookmarkPtr dst, BookmarkPtr src)
  130. {
  131.     BookmarkPtr next, prev;
  132.     int idx;
  133.     
  134.     /* Need to preserve dst's links, but copy all of src's stuff. */
  135.     next = dst->next;
  136.     prev = dst->prev;
  137.     idx = dst->index;
  138.     *dst = *src;
  139.     dst->next = next;
  140.     dst->prev = prev;
  141.     dst->index = idx;
  142. }    /* UpdateBookmarkPtr */
  143.  
  144.  
  145.  
  146.  
  147.  
  148. BookmarkPtr AddBookmarkPtr(BookmarkPtr buf)
  149. {
  150.     BookmarkPtr newRsip;
  151.  
  152.     newRsip = (BookmarkPtr) malloc(sizeof(Bookmark));
  153.     if (newRsip != NULL) {
  154.         memcpy(newRsip, buf, sizeof(Bookmark));
  155.         newRsip->next = NULL;
  156.         if (gFirstRsi == NULL) {
  157.             gFirstRsi = gLastRsi = newRsip;
  158.             newRsip->prev = NULL;
  159.         } else {
  160.             newRsip->prev = gLastRsi;
  161.             gLastRsi->next = newRsip;
  162.             gLastRsi = newRsip;
  163.         }
  164.         ++gNumBookmarks;
  165.         /* Just need to know if we should write out the host file later. */
  166.         gModifiedBookmarks++;
  167.     } else {
  168.         OutOfMemory();
  169.     }
  170.     return newRsip;
  171. }    /* AddBookmarkPtr */
  172.  
  173.  
  174.  
  175.  
  176.  
  177. BookmarkPtr RemoveBookmarkPtr(BookmarkPtr killMe)
  178. {
  179.     BookmarkPtr nextRsi, prevRsi;
  180.     
  181.     nextRsi = killMe->next;    
  182.     prevRsi = killMe->prev;    
  183.     
  184.     if (gFirstRsi == killMe)
  185.         gFirstRsi = nextRsi;
  186.     if (gLastRsi == killMe)
  187.         gLastRsi = prevRsi;
  188.  
  189.     if (nextRsi != NULL)
  190.         nextRsi->prev = prevRsi;
  191.     if (prevRsi != NULL)
  192.         prevRsi->next = nextRsi;
  193.  
  194.     PTRZERO(killMe, sizeof(Bookmark));
  195.     free(killMe);
  196.     --gNumBookmarks;
  197.     ++gModifiedBookmarks;
  198.     return (nextRsi);
  199. }    /* RemoveBookmarkPtr */
  200.  
  201.  
  202.  
  203.  
  204.  
  205. void MakeBookmarkUnique(char *dst, size_t siz)
  206. {
  207.     int i;
  208.     string s2, s3;
  209.     BookmarkPtr *bmpp;
  210.     char *cp;
  211.  
  212.     /* Make sure we can concat 3 more characters if necessary. */
  213.     Strncpy(s2, dst, siz - 3);
  214.     for (cp = s2 + strlen(s2) - 1; cp > s2; ) {
  215.         if (isdigit(*cp))
  216.             *cp-- = '\0';
  217.         else
  218.             break;
  219.     }
  220.  
  221.     /* Make a copy of the original. */
  222.     STRNCPY(s3, dst);
  223.     
  224.     for (i=1; i<=999; i++) {
  225.         if (i > 1)
  226.             sprintf(dst, "%s%d", s2, i);
  227.         else
  228.             Strncpy(dst, s3, siz);
  229.     
  230.         /* See if there is already a nickname by this name. */
  231.         if (gNumBookmarks == 0)
  232.             break;
  233.         bmpp = (BookmarkPtr *) BSEARCH(dst, gBookmarks, gNumBookmarks,
  234.             sizeof(BookmarkPtr), BookmarkSearchProc);
  235.         if (bmpp == NULL)
  236.             break;
  237.     }
  238. }    /* MakeBookmarkUnique */
  239.  
  240.  
  241.  
  242.  
  243. void MakeUpABookmarkName(char *dst, size_t siz, char *src)
  244. {
  245.     string str;
  246.     char *token;
  247.     char *cp;
  248.  
  249.     STRNCPY(str, src);
  250.     
  251.     /* Pick the first "significant" part of the hostname.  Usually
  252.      * this is the first word in the name, but if it's something like
  253.      * ftp.unl.edu, we would want to choose "unl" and not "ftp."
  254.      */
  255.     token = str;
  256.     if ((token = strtok(token, ".")) == NULL)
  257.         token = "misc";
  258.     else if (ISTREQ(token, "ftp")) {
  259.         if ((token = strtok(NULL, ".")) == NULL)
  260.             token = "misc";
  261.     }
  262.     for (cp = token; ; cp++) {
  263.         if (*cp == '\0') {
  264.             /* Token was all digits, like an IP address perhaps. */
  265.             token = "misc";
  266.         }
  267.         if (!isdigit(*cp))
  268.             break;
  269.     }
  270.     Strncpy(dst, token, siz);
  271.     MakeBookmarkUnique(dst, siz);
  272. }    /* MakeUpABookmarkName */
  273.  
  274.  
  275.  
  276.  
  277. void SetBookmarkDefaults(BookmarkPtr bmp)
  278. {
  279.     PTRZERO(bmp, sizeof(Bookmark));
  280.  
  281.     bmp->xferType = 'I';
  282.     bmp->xferMode = 'S';    /* Use FTP protocol default as ours too. */
  283.     bmp->port = kPortUnset;
  284.     bmp->hasSIZE = 1;    /* Assume we have it until proven otherwise. */
  285.     bmp->hasMDTM = 1;    /* Assume we have it until proven otherwise. */
  286.     if (gPreferredDataPortMode >= kPassiveMode) {
  287.         /* Assume we have it until proven otherwise. */
  288.         bmp->hasPASV = 1;
  289.     } else {
  290.         /* If default is PORT, then make the user explicitly set this. */
  291.         bmp->hasPASV = 0;
  292.     }    
  293.     bmp->isUnix = 1;
  294.     bmp->lastCall = (time_t) 0;
  295. }    /* SetBookmarkDefaults */
  296.  
  297.  
  298.  
  299. void SetNewBookmarkDefaults(BookmarkPtr bmp)
  300. {
  301.     /* Return a pointer to a new entry, initialized to
  302.      * all the defaults, except for name and nickname.
  303.      */
  304.     SetBookmarkDefaults(bmp);
  305.     STRNCPY(bmp->name, "foobar.snafu.gov");
  306.     STRNCPY(bmp->bookmarkName, "NEW");
  307.  
  308.     /* That will make a unique "NEW" nickname. */
  309.     MakeBookmarkUnique(bmp->bookmarkName, sizeof(bmp->bookmarkName));
  310. }    /* SetNewBookmarkDefaults */
  311.  
  312.  
  313.  
  314.  
  315. int GetBookmark(char *host, size_t siz)
  316. {
  317.     BookmarkPtr *bmpp;
  318.     int i;
  319.     size_t len;
  320.     
  321.     if (gNumBookmarks == 0)
  322.         bmpp = NULL;
  323.     else {
  324.         bmpp = (BookmarkPtr *)
  325.             BSEARCH(host, gBookmarks, gNumBookmarks,
  326.                 sizeof(BookmarkPtr), BookmarkSearchProc);
  327.         if (bmpp == NULL) {
  328.             /* No exact match, but the user doesn't have to type the
  329.              * whole nickname, just the first few letters of it.
  330.              */
  331.             /* This could probably be done in a bsearch proc too... */
  332.             len = strlen(host);
  333.             for (i=0; i<gNumBookmarks; i++) {
  334.                 if (ISTRNEQ(gBookmarks[i]->bookmarkName, host, len)) {
  335.                     bmpp = &gBookmarks[i];
  336.                     break;
  337.                 }
  338.             }
  339.         }
  340.         if ((bmpp == NULL) && (strchr(host, '.') != NULL)) {
  341.             /* If thing we were given looks like a full hostname (has at
  342.              * least one period), see if we have an exact match on the
  343.              * hostname.
  344.              *
  345.              * This is actually not recommended -- you should try to use
  346.              * the nicknames only since they are unique.  We can have more
  347.              * than one entry for the same hostname!
  348.              */
  349.             for (i=0; i<gNumBookmarks; i++) {
  350.                 if (ISTREQ(gBookmarks[i]->name, host)) {
  351.                     bmpp = &gBookmarks[i];
  352.                     break;
  353.                 }
  354.             }
  355.         }
  356.     }
  357.  
  358.     gWantRmtInfoSaved = 0;
  359.     if (bmpp != NULL) {
  360.         gRmtInfo = **bmpp;
  361.         
  362.         /* So we know that this isn't in the list, but just a copy
  363.          * of someone else's data.
  364.          */
  365.         gRmtInfo.next = gRmtInfo.prev = NULL;
  366.         
  367.         gRmtInfoIsNew = 0;
  368.         /* gHost needs to be set here, since the caller wasn't using
  369.          * a real host name.
  370.          */
  371.         Strncpy(host, gRmtInfo.name, siz);
  372.         return (1);
  373.     }
  374.     
  375.     SetNewBookmarkDefaults(&gRmtInfo);    
  376.     STRNCPY(gRmtInfo.name, host);
  377.     MakeUpABookmarkName(gRmtInfo.bookmarkName, sizeof(gRmtInfo.bookmarkName), host);
  378.     
  379.     gRmtInfoIsNew = 1;
  380.     return (0);
  381. }    /* GetBookmark */
  382.  
  383.  
  384.  
  385.  
  386. int ParseHostLine(char *line, BookmarkPtr bmp)
  387. {
  388.     string token;
  389.     char *s, *d;
  390.     char *tokenend;
  391.     long L;
  392.     int i;
  393.     int result;
  394.  
  395.     SetBookmarkDefaults(bmp);
  396.     s = line;
  397.     tokenend = token + sizeof(token) - 1;
  398.     result = -1;
  399.     for (i=0; ; i++) {
  400.         if (*s == '\0')
  401.             break;
  402.         /* Some tokens may need to have a comma in them.  Since this is a
  403.          * field delimiter, these fields use \, to represent a comma, and
  404.          * \\ for a backslash.  This chunk gets the next token, paying
  405.          * attention to the escaped stuff.
  406.          */
  407.         for (d = token; *s != '\0'; ) {
  408.             if ((*s == '\\') && (s[1] != '\0')) {
  409.                 if (d < tokenend)
  410.                     *d++ = s[1];
  411.                 s += 2;
  412.             } else if (*s == ',') {
  413.                 ++s;
  414.                 break;
  415.             } else {
  416.                 if (d < tokenend)
  417.                     *d++ = *s;
  418.                 ++s;
  419.             }
  420.         }
  421.         *d = '\0';
  422.         switch(i) {
  423.             case 0: (void) STRNCPY(bmp->bookmarkName, token); break;
  424.             case 1: (void) STRNCPY(bmp->name, token); break;
  425.             case 2: (void) STRNCPY(bmp->user, token); break;
  426.             case 3: (void) STRNCPY(bmp->pass, token); break;
  427.             case 4: (void) STRNCPY(bmp->acct, token); break;
  428.             case 5: (void) STRNCPY(bmp->dir, token);
  429.                     result = 0;        /* Good enough to have these fields. */
  430.                     break;
  431.             case 6: bmp->xferType = token[0]; break;
  432.             case 7:
  433.                 /* Most of the time, we won't have a port. */
  434.                 if (token[0] == '\0')
  435.                     bmp->port = (unsigned int) kDefaultFTPPort;
  436.                 else
  437.                     bmp->port = (unsigned int) atoi(token);
  438.                 break;
  439.             case 8:
  440.                 sscanf(token, "%lx", &L);
  441.                 bmp->lastCall = (time_t) L;
  442.                 break;
  443.             case 9: bmp->hasSIZE = atoi(token); break;
  444.             case 10: bmp->hasMDTM = atoi(token); break;
  445.             case 11: bmp->hasPASV = atoi(token); break;
  446.             case 12: bmp->isUnix = atoi(token);
  447.                     result = 3;        /* Version 3 had all fields to here. */
  448.                     break;
  449.             case 13: (void) STRNCPY(bmp->lastIP, token); break;
  450.             case 14: (void) STRNCPY(bmp->comment, token); break;
  451.             case 15: sscanf(token, "%ld", &bmp->xferKbytes); break;
  452.             case 16: sscanf(token, "%ld", &bmp->xferHSeconds);
  453.                     result = 4;        /* Version 4 had all fields up to here. */
  454.                     break;
  455.             case 17: bmp->nCalls = atoi(token);
  456.                     result = 5;        /* Version 5 has all fields to here. */
  457.                     break;
  458.             case 18: bmp->noSaveDir = atoi(token);
  459.                     result = 6;        /* Version 6 has all fields to here. */
  460.                     break;
  461.             case 19: bmp->xferMode = token[0];
  462.                     result = 7;        /* Version 7 has all fields to here. */
  463.                     break;
  464.             default:
  465.                     result = 99;    /* Version >7 ? */
  466.                     goto done;
  467.         }
  468.     }
  469. done:
  470.     return (result);
  471. }    /* ParseHostLine */
  472.  
  473.  
  474.  
  475.  
  476. void ReadBookmarkFile(void)
  477. {
  478.     string pathName;
  479.     string path2;
  480.     FILE *fp;
  481.     longstring line;
  482.     int version;
  483.     Bookmark newRsi;
  484.  
  485.     if (gOurDirectoryPath[0] == '\0')
  486.         return;        /* Don't create in root directory. */
  487.     OurDirectoryPath(pathName, sizeof(pathName), kBookmarkFileName);
  488.     fp = fopen(pathName, "r");
  489.     if (fp == NULL) {
  490.         OurDirectoryPath(path2, sizeof(path2), kOldBookmarkFileName);
  491.         if (rename(path2, pathName) == 0) {
  492.             /* Rename succeeded, now open it. */
  493.             fp = fopen(pathName, "r");
  494.             if (fp == NULL)
  495.                 return;
  496.         }
  497.         return;        /* Okay to not have one yet. */
  498.     }
  499.  
  500.     if (FGets(line, sizeof(line), fp) == NULL)
  501.         goto badFmt;
  502.     
  503.     /* Sample line we're looking for:
  504.      * "NcFTP bookmark-file version: 2"
  505.      */
  506.     version = -1;
  507.     (void) sscanf(line, "%*s %*s %*s %d", &version);
  508.     if (version < kBookmarkMinVersion) {
  509.         if (version < 0)
  510.             goto badFmt;
  511.         STRNCPY(path2, pathName);
  512.         sprintf(line, ".v%d", version);
  513.         STRNCAT(path2, line);
  514.         (void) rename(pathName, path2);
  515.         Error(kDontPerror, "%s: old version.\n", pathName);
  516.         fclose(fp);
  517.         return;
  518.     }
  519.         
  520.     if (FGets(line, sizeof(line), fp) == NULL)
  521.         goto badFmt;
  522.     
  523.     /* Sample line we're looking for:
  524.      * "Number of entries: 28"
  525.      */
  526.     gNumBookmarks = -1;
  527.     
  528.     /* At the moment, we don't really care about the number stored in the
  529.      * file.  It's there for future use.
  530.      */
  531.     (void) sscanf(line, "%*s %*s %*s %d", &gNumBookmarks);
  532.     if (gNumBookmarks < 0)
  533.         goto badFmt;
  534.     
  535.     gHosts = (BookmarkPtr) 0;
  536.     gBookmarks = (BookmarkPtrList) 0;
  537.     gNumBookmarks = 0;
  538.     
  539.     while (FGets(line, sizeof(line), fp) != NULL) {
  540.         if (ParseHostLine(line, &newRsi) >= 0) {
  541.             AddBookmarkPtr(&newRsi);
  542.         }
  543.     }
  544.     fclose(fp);
  545.  
  546.     SortBookmarks();
  547.     DebugMsg("Read %d entries from %s.\n", gNumBookmarks, pathName);
  548.     return;
  549.     
  550. badFmt:
  551.     Error(kDontPerror, "%s: invalid format.\n", pathName);
  552.     fclose(fp);
  553. }    /* ReadBookmarkFile */
  554.  
  555.  
  556.  
  557.  
  558. BookmarkPtr DuplicateBookmark(BookmarkPtr origbmp)
  559. {
  560.     Bookmark newRsi;
  561.     BookmarkPtr newRsip;
  562.     string str;
  563.  
  564.     STRNCPY(str, origbmp->bookmarkName);
  565.     MakeBookmarkUnique(str, sizeof(origbmp->bookmarkName));
  566.  
  567.     newRsi = *origbmp;
  568.     STRNCPY(newRsi.bookmarkName, str);
  569.     newRsip = AddBookmarkPtr(&newRsi);
  570.  
  571.     /* Have to re-sort now so our bsearches will work. */
  572.     SortBookmarks();
  573.     return (newRsip);
  574. }    /* DuplicateBookmark */
  575.  
  576.  
  577.  
  578.  
  579. void DeleteBookmark(BookmarkPtr bmp)
  580. {
  581.     if (gNumBookmarks < 1)
  582.         return;
  583.     
  584.     RemoveBookmarkPtr(bmp);
  585.     SortBookmarks();
  586. }    /* DuplicateBookmark */
  587.  
  588.  
  589.  
  590.  
  591. void SaveBookmark(char *asNick)
  592. {
  593.     BookmarkPtr *bmpp;
  594.     Bookmark rm;
  595.  
  596.     memcpy(&rm, &gRmtInfo, sizeof(rm));
  597.     STRNCPY(rm.bookmarkName, asNick);
  598.     STRNCPY(rm.dir, gRemoteCWD);
  599.     rm.xferKbytes = 0L;
  600.     rm.xferHSeconds = 0L;
  601.     rm.nCalls = 0;
  602.  
  603.     /* Don't update dir if you move around the next time you use it. */
  604.     rm.noSaveDir = 1;
  605.  
  606.     bmpp = (BookmarkPtr *) BSEARCH(asNick,
  607.         gBookmarks, gNumBookmarks,
  608.         sizeof(BookmarkPtr), BookmarkSearchProc);
  609.     if (bmpp == NULL) {
  610.         /* Add a new entry. */
  611.         rm.comment[0] = '\0';
  612.         (void) AddBookmarkPtr(&rm);
  613.  
  614.         /* Have to re-sort now so our bsearches will work. */
  615.         SortBookmarks();
  616.         PrintF("Saving new bookmark named \"%s\" in your host file, pointing\nto <URL:ftp://%s/%s/>.\n",
  617.             rm.bookmarkName,
  618.             rm.name,
  619.             rm.dir
  620.         );
  621.     } else {
  622.         /* Copy over an existing one. */
  623.         UpdateBookmarkPtr(*bmpp, &rm);
  624.         PrintF("Updated bookmark named \"%s\" in your host file, so it now points\nto <URL:ftp://%s/%s/>.\n",
  625.             rm.bookmarkName,
  626.             rm.name,
  627.             rm.dir
  628.         );
  629.     }
  630.  
  631.     /* Just need to know if we should write out the host file later. */
  632.     gModifiedBookmarks++;
  633. }    /* SaveBookmark */
  634.  
  635.  
  636.  
  637.  
  638. void SaveCurHostBookmark(char *asNick)
  639. {
  640.     BookmarkPtr *bmpp;
  641.  
  642.     if (gRmtInfoIsNew) {
  643.         (void) AddBookmarkPtr(&gRmtInfo);
  644.         PrintF("Saving new bookmark named \"%s\" in your host file, pointing\nto <URL:ftp://%s/%s/>.\n",
  645.             gRmtInfo.bookmarkName,
  646.             gRmtInfo.name,
  647.             gRmtInfo.dir
  648.         );
  649.  
  650.         /* Have to re-sort now so our bsearches will work. */
  651.         SortBookmarks();
  652.     } else {
  653.         /* We were working with an existing entry.
  654.          * If the nickname given to us as the parameter is different
  655.          * from the existing bookmarkName, then we're supposed to save
  656.          * this as a new entry.
  657.          */
  658.         if ((asNick == NULL) || ISTREQ(asNick, gRmtInfo.bookmarkName)) {
  659.             /* Save over old entry. */
  660.             bmpp = (BookmarkPtr *) BSEARCH(gRmtInfo.bookmarkName,
  661.                 gBookmarks, gNumBookmarks,
  662.                 sizeof(BookmarkPtr), BookmarkSearchProc);
  663.             /* This had better be in there, since we did this before
  664.              * and it was in there.
  665.              */
  666.             if (bmpp == NULL) {
  667.                 Error(kDontPerror,
  668.                 "Programmer's error: couldn't re-find host info entry.\n");
  669.                 return;
  670.             }
  671.             /* Copy over the old stuff. */
  672.             UpdateBookmarkPtr(*bmpp, &gRmtInfo);
  673.  
  674.             /* Just need to know if we should write out the host file later. */
  675.             gModifiedBookmarks++;
  676.         } else {
  677.             /* Add a new entry. */
  678.             STRNCPY(gRmtInfo.bookmarkName, asNick);
  679.             MakeBookmarkUnique(gRmtInfo.bookmarkName, sizeof(gRmtInfo.bookmarkName));
  680.             (void) AddBookmarkPtr(&gRmtInfo);
  681.  
  682.             /* Have to re-sort now so our bsearches will work. */
  683.             SortBookmarks();
  684.         }
  685.     }
  686.  
  687.     gRmtInfoIsNew = 0;
  688. }    /* SaveCurHostBookmark */
  689.  
  690.  
  691.  
  692. static
  693. void EscapeString(char *d, char *s)
  694. {
  695.     if (s != NULL) {
  696.         while (*s != '\0') {
  697.             if (*s == ',' || *s == '\\')
  698.                 *d++ = '\\';
  699.             *d++ = *s++;
  700.         }
  701.     }
  702.     *d = '\0';
  703. }    /* EscapeString */
  704.  
  705.  
  706.  
  707.  
  708. void WriteBookmarkFile(void)
  709. {
  710.     string pathName;
  711.     string bupPathName;
  712.     longstring escapedStr;
  713.     FILE *fp;
  714.     char portStr[16];
  715.     int i;
  716.     int nPasswds;
  717.     BookmarkPtr bmp;
  718.  
  719.     if (!gModifiedBookmarks)
  720.         return;
  721.  
  722.     OurDirectoryPath(pathName, sizeof(pathName), kBookmarkFileName);
  723.  
  724.     if ((gMaxBookmarks != kNoBookmarkLimit) && (gNumBookmarks > gMaxBookmarks)) {
  725.         DebugMsg("Purging %d old remote sites from %s.\n",
  726.             gNumBookmarks - gMaxBookmarks,
  727.             pathName
  728.         );
  729.  
  730.         /* Sort sites by last time we called.  We want the older sites to
  731.          * float to the bottom.
  732.          */
  733.         QSORT(gBookmarks,
  734.             gNumBookmarks, sizeof(BookmarkPtr), BookmarkSortTimeProc);
  735.  
  736.         gNumBookmarks = gMaxBookmarks;
  737.     }
  738.     
  739.     /* See if we can move the existing file to a new name, in case
  740.      * something happens while we write this out.  Host files are
  741.      * valuable enough that people would be pissed off if their
  742.      * host file got nuked.
  743.      */
  744.     OurDirectoryPath(bupPathName, sizeof(bupPathName), kBookmarkBupFileName);
  745.     (void) UNLINK(bupPathName);
  746.     (void) rename(pathName, bupPathName);
  747.     
  748.     fp = fopen(pathName, "w");
  749.     if (fp == NULL)
  750.         goto err;
  751.     
  752.     if (fprintf(fp, "NcFTP bookmark-file version: %d\nNumber of entries: %d\n",
  753.         kBookmarkVersion,
  754.         gNumBookmarks
  755.     ) < 0)
  756.         goto err;
  757.     if (fflush(fp) < 0)
  758.         goto err;
  759.  
  760.     for (i=0, nPasswds=0; i<gNumBookmarks; i++) {
  761.         *portStr = '\0';
  762.         bmp = gBookmarks[i];
  763.         if (bmp->port != kDefaultFTPPort)
  764.             sprintf(portStr, "%u", bmp->port);
  765.         if ((bmp->pass[0] != '\0')
  766.             && (!STREQ(bmp->pass, gEmailAddress))
  767.             && (!STREQ(bmp->pass, gAnonPassword)))
  768.             nPasswds++;
  769.         if (bmp->acct[0] != '\0')
  770.             nPasswds++;        /* Don't publicize accounts, either. */
  771.  
  772.         /* Insert the quote character '\' for strings that can have
  773.          * commas or backslashes in them.
  774.          */
  775.         EscapeString(escapedStr, bmp->pass);
  776.         if (fprintf(fp, "%s,%s,%s,%s,%s,",
  777.             bmp->bookmarkName,
  778.             bmp->name,
  779.             bmp->user,
  780.             escapedStr,
  781.             bmp->acct
  782.         ) < 0)
  783.             goto err;
  784.  
  785.         EscapeString(escapedStr, bmp->dir);
  786.         if (fprintf(fp, "%s,%c,%s,%lx,%d,%d,%d,%d,",
  787.             escapedStr,
  788.             bmp->xferType,
  789.             portStr,
  790.             (unsigned long) bmp->lastCall,
  791.             bmp->hasSIZE,
  792.             bmp->hasMDTM,
  793.             bmp->hasPASV,
  794.             bmp->isUnix
  795.         ) < 0)
  796.             goto err;
  797.  
  798.         EscapeString(escapedStr, bmp->comment);
  799.         if (fprintf(fp, "%s,%s,%ld,%ld,%d,%d,%c\n",
  800.             bmp->lastIP,
  801.             escapedStr,
  802.             bmp->xferKbytes,
  803.             bmp->xferHSeconds,
  804.             bmp->nCalls,
  805.             bmp->noSaveDir,
  806.             bmp->xferMode
  807.         ) < 0)
  808.             goto err;
  809.     }
  810.     if (fclose(fp) < 0) {
  811.         fp = NULL;
  812.         goto err;
  813.     }
  814.     (void) UNLINK(bupPathName);
  815.     if (nPasswds > 0) {
  816.         /* Set permissions so other users can't see the passwords.
  817.          * Of course this isn't really secure, which is why the program
  818.          * won't save passwords entered at the password prompt.  You must
  819.          * explicitly set them from the host editor.
  820.          */
  821.         (void) chmod(pathName, 0600);    /* Set it to -rw------- */
  822.     }
  823.     return;
  824.  
  825. err:
  826.     if (access(bupPathName, F_OK) < 0) {
  827.         Error(kDoPerror, "Could not write to %s.\n", pathName);
  828.     } else {
  829.         /* Move backup file back to the original. */
  830.         rename(bupPathName, pathName);
  831.         Error(kDoPerror, "Could not update %s.\n", pathName);
  832.     }
  833.     if (fp != NULL)
  834.         fclose(fp);
  835. }    /* WriteBookmarkFile */
  836.