1 /* bookmark.c
2 *
3 * Copyright (c) 1992-2001 by Mike Gleason.
4 * All rights reserved.
5 *
6 */
7
8 #include "syshdrs.h"
9
10 #include "bookmark.h"
11 #include "util.h"
12
13 /*
14 * The ~/.ncftp/bookmarks file contains a list of sites
15 * the user wants to remember.
16 *
17 * Unlike previous versions of the program, we now open/close
18 * the file every time we need it; That way we can have
19 * multiple ncftp processes changing the file. There is still
20 * a possibility that two different processes could be modifying
21 * the file at the same time.
22 */
23
24 Bookmark gBm;
25 int gLoadedBm = 0;
26 int gBookmarkMatchMode = 0;
27 int gNumBookmarks = 0;
28 BookmarkPtr gBookmarkTable = NULL;
29
30 extern char gOurDirectoryPath[];
31
32 /* Converts a pre-loaded Bookmark structure into a RFC 1738
33 * Uniform Resource Locator.
34 */
35 void
BookmarkToURL(BookmarkPtr bmp,char * url,size_t urlsize)36 BookmarkToURL(BookmarkPtr bmp, char *url, size_t urlsize)
37 {
38 char pbuf[32];
39
40 /* //<user>:<password>@<host>:<port>/<url-path> */
41 /* Note that if an absolute path is given,
42 * you need to escape the first entry, i.e. /pub -> %2Fpub
43 */
44 (void) Strncpy(url, "ftp://", urlsize);
45 if (bmp->user[0] != '\0') {
46 (void) Strncat(url, bmp->user, urlsize);
47 if (bmp->pass[0] != '\0') {
48 (void) Strncat(url, ":", urlsize);
49 (void) Strncat(url, "PASSWORD", urlsize);
50 }
51 (void) Strncat(url, "@", urlsize);
52 }
53 (void) Strncat(url, bmp->name, urlsize);
54 if (bmp->port != 21) {
55 (void) sprintf(pbuf, ":%u", (unsigned int) bmp->port);
56 (void) Strncat(url, pbuf, urlsize);
57 }
58 if (bmp->dir[0] == '/') {
59 /* Absolute URL path, must escape first slash. */
60 (void) Strncat(url, "/%2F", urlsize);
61 (void) Strncat(url, bmp->dir + 1, urlsize);
62 (void) Strncat(url, "/", urlsize);
63 } else if (bmp->dir[0] != '\0') {
64 (void) Strncat(url, "/", urlsize);
65 (void) Strncat(url, bmp->dir, urlsize);
66 (void) Strncat(url, "/", urlsize);
67 }
68 } /* BookmarkToURL */
69
70
71
72
73 void
SetBookmarkDefaults(BookmarkPtr bmp)74 SetBookmarkDefaults(BookmarkPtr bmp)
75 {
76 (void) memset(bmp, 0, sizeof(Bookmark));
77
78 bmp->xferType = 'I';
79 bmp->xferMode = 'S'; /* Use FTP protocol default as ours too. */
80 bmp->hasSIZE = kCommandAvailabilityUnknown;
81 bmp->hasMDTM = kCommandAvailabilityUnknown;
82 bmp->hasUTIME = kCommandAvailabilityUnknown;
83 bmp->hasPASV = kCommandAvailabilityUnknown;
84 bmp->isUnix = 1;
85 bmp->lastCall = (time_t) 0;
86 bmp->deleted = 0;
87 } /* SetBookmarkDefaults */
88
89
90
91
92 /* Used when converting hex strings to integral types. */
93 static int
HexCharToNibble(int c)94 HexCharToNibble(int c)
95 {
96 switch (c) {
97 case '0':
98 case '1':
99 case '2':
100 case '3':
101 case '4':
102 case '5':
103 case '6':
104 case '7':
105 case '8':
106 case '9':
107 return (c - '0');
108 case 'a':
109 case 'b':
110 case 'c':
111 case 'd':
112 case 'e':
113 case 'f':
114 return (c - 'a' + 10);
115 case 'A':
116 case 'B':
117 case 'C':
118 case 'D':
119 case 'E':
120 case 'F':
121 return (c - 'A' + 10);
122
123 }
124 return (-1); /* Error. */
125 } /* HexCharToNibble */
126
127
128
129
130
131 /* Fills in a Bookmark structure based off of a line from the NcFTP
132 * "bookmarks" file.
133 */
134 int
ParseHostLine(char * line,BookmarkPtr bmp)135 ParseHostLine(char *line, BookmarkPtr bmp)
136 {
137 char token[128];
138 char pass[128];
139 char *s, *d;
140 char *tokenend;
141 long L;
142 int i;
143 int result;
144 int n, n1, n2;
145
146 SetBookmarkDefaults(bmp);
147 s = line;
148 tokenend = token + sizeof(token) - 1;
149 result = -1;
150 for (i=1; ; i++) {
151 if (*s == '\0')
152 break;
153 /* Some tokens may need to have a comma in them. Since this is a
154 * field delimiter, these fields use \, to represent a comma, and
155 * \\ for a backslash. This chunk gets the next token, paying
156 * attention to the escaped stuff.
157 */
158 for (d = token; *s != '\0'; ) {
159 if ((*s == '\\') && (s[1] != '\0')) {
160 if (d < tokenend)
161 *d++ = s[1];
162 s += 2;
163 } else if (*s == ',') {
164 ++s;
165 break;
166 } else if ((*s == '$') && (s[1] != '\0') && (s[2] != '\0')) {
167 n1 = HexCharToNibble(s[1]);
168 n2 = HexCharToNibble(s[2]);
169 if ((n1 >= 0) && (n2 >= 0)) {
170 n = (n1 << 4) | n2;
171 if (d < tokenend)
172 *(unsigned char *)d++ = (unsigned int) n;
173 }
174 s += 3;
175 } else {
176 if (d < tokenend)
177 *d++ = *s;
178 ++s;
179 }
180 }
181 *d = '\0';
182 switch(i) {
183 case 1: (void) STRNCPY(bmp->bookmarkName, token); break;
184 case 2: (void) STRNCPY(bmp->name, token); break;
185 case 3: (void) STRNCPY(bmp->user, token); break;
186 case 4: (void) STRNCPY(bmp->pass, token); break;
187 case 5: (void) STRNCPY(bmp->acct, token); break;
188 case 6: (void) STRNCPY(bmp->dir, token);
189 result = 0; /* Good enough to have these fields. */
190 break;
191 case 7:
192 if (token[0] != '\0')
193 bmp->xferType = (int) token[0];
194 break;
195 case 8:
196 /* Most of the time, we won't have a port. */
197 if (token[0] == '\0')
198 bmp->port = (unsigned int) kDefaultFTPPort;
199 else
200 bmp->port = (unsigned int) atoi(token);
201 break;
202 case 9:
203 (void) sscanf(token, "%lx", &L);
204 bmp->lastCall = (time_t) L;
205 break;
206 case 10: bmp->hasSIZE = atoi(token); break;
207 case 11: bmp->hasMDTM = atoi(token); break;
208 case 12: bmp->hasPASV = atoi(token); break;
209 case 13: bmp->isUnix = atoi(token);
210 result = 3; /* Version 3 had all fields to here. */
211 break;
212 case 14: (void) STRNCPY(bmp->lastIP, token); break;
213 case 15: (void) STRNCPY(bmp->comment, token); break;
214 case 16:
215 case 17:
216 case 18:
217 case 19:
218 break;
219 case 20: bmp->xferMode = token[0];
220 result = 7; /* Version 7 has all fields to here. */
221 break;
222 case 21: bmp->hasUTIME = atoi(token);
223 break;
224 case 22: (void) STRNCPY(bmp->ldir, token);
225 result = 8; /* Version 8 has all fields to here. */
226 break;
227 default:
228 result = 99; /* Version >8 ? */
229 goto done;
230 }
231 }
232 done:
233
234 /* Decode password, if it was base-64 encoded. */
235 if (strncmp(bmp->pass, kPasswordMagic, kPasswordMagicLen) == 0) {
236 FromBase64(pass, bmp->pass + kPasswordMagicLen, strlen(bmp->pass + kPasswordMagicLen), 1);
237 (void) STRNCPY(bmp->pass, pass);
238 }
239 return (result);
240 } /* ParseHostLine */
241
242
243
244
245 void
CloseBookmarkFile(FILE * fp)246 CloseBookmarkFile(FILE *fp)
247 {
248 if (fp != NULL)
249 (void) fclose(fp);
250 } /* CloseBookmarkFile */
251
252
253
254
255
256 int
GetNextBookmark(FILE * fp,Bookmark * bmp)257 GetNextBookmark(FILE *fp, Bookmark *bmp)
258 {
259 char line[512];
260
261 while (FGets(line, sizeof(line), fp) != NULL) {
262 if (ParseHostLine(line, bmp) >= 0)
263 return (0);
264 }
265 return (-1);
266 } /* GetNextBookmark */
267
268
269
270
271 /* Opens a NcFTP 2.x or 3.x style bookmarks file, and sets the file pointer
272 * so that it is ready to read the first data line.
273 */
274 FILE *
OpenBookmarkFile(int * numBookmarks0)275 OpenBookmarkFile(int *numBookmarks0)
276 {
277 char pathName[256], path2[256];
278 char line[256];
279 FILE *fp;
280 int version;
281 int numBookmarks;
282 Bookmark junkbm;
283
284 if (gOurDirectoryPath[0] == '\0')
285 return NULL; /* Don't create in root directory. */
286 (void) OurDirectoryPath(pathName, sizeof(pathName), kBookmarkFileName);
287 fp = fopen(pathName, FOPEN_READ_TEXT);
288 if (fp == NULL) {
289 /* See if it exists under the old name. */
290 (void) OurDirectoryPath(path2, sizeof(path2), kOldBookmarkFileName);
291 if (rename(path2, pathName) == 0) {
292 /* Rename succeeded, now open it. */
293 fp = fopen(pathName, FOPEN_READ_TEXT);
294 if (fp == NULL)
295 return NULL;
296 }
297 return NULL; /* Okay to not have one yet. */
298 }
299
300 (void) _chmod(pathName, 00600);
301 if (FGets(line, sizeof(line), fp) == NULL) {
302 (void) fprintf(stderr, "%s: invalid format.\n", pathName);
303 (void) fclose(fp);
304 return NULL;
305 }
306
307 /* Sample line we're looking for:
308 * "NcFTP bookmark-file version: 8"
309 */
310 version = -1;
311 (void) sscanf(line, "%*s %*s %*s %d", &version);
312 if (version < kBookmarkMinVersion) {
313 if (version < 0) {
314 (void) fprintf(stderr, "%s: invalid format, or bad version.\n", pathName);
315 (void) fclose(fp);
316 return NULL;
317 }
318 (void) STRNCPY(path2, pathName);
319 (void) sprintf(line, ".v%d", version);
320 (void) STRNCAT(path2, line);
321 (void) rename(pathName, path2);
322 (void) fprintf(stderr, "%s: old version.\n", pathName);
323 (void) fclose(fp);
324 return NULL;
325 }
326
327 /* Sample line we're looking for:
328 * "Number of entries: 28" or "# # # 1"
329 */
330 numBookmarks = -1;
331
332 /* At the moment, we can't trust the number stored in the
333 * file. It's there for future use.
334 */
335 if (FGets(line, sizeof(line), fp) == NULL) {
336 (void) fprintf(stderr, "%s: invalid format.\n", pathName);
337 (void) fclose(fp);
338 return NULL;
339 }
340
341 if (numBookmarks0 == (int *) 0) {
342 /* If the caller doesn't care how many bookmarks are *really*
343 * in the file, then we can return now.
344 */
345 return(fp);
346 }
347
348 /* Otherwise, we have to read through the whole file because
349 * unfortunately the header line can't be trusted.
350 */
351 for (numBookmarks = 0; ; numBookmarks++) {
352 if (GetNextBookmark(fp, &junkbm) < 0)
353 break;
354 }
355
356 /* Now we have to re-open and re-position the file.
357 * We don't use rewind() because it doesn't always work.
358 * This introduces a race condition, but the bookmark
359 * functionality wasn't designed to be air-tight.
360 */
361 CloseBookmarkFile(fp);
362 fp = fopen(pathName, FOPEN_READ_TEXT);
363 if (fp == NULL)
364 return (NULL);
365 if (FGets(line, sizeof(line), fp) == NULL) {
366 (void) fprintf(stderr, "%s: invalid format.\n", pathName);
367 (void) fclose(fp);
368 return NULL;
369 }
370
371 if (FGets(line, sizeof(line), fp) == NULL) {
372 (void) fprintf(stderr, "%s: invalid format.\n", pathName);
373 (void) fclose(fp);
374 return NULL;
375 }
376
377 /* NOW we're done. */
378 *numBookmarks0 = numBookmarks;
379 return (fp);
380 } /* OpenBookmarkFile */
381
382
383
384
385 /* Looks for a saved bookmark by the abbreviation given. */
386 int
GetBookmark(const char * const bmabbr,Bookmark * bmp)387 GetBookmark(const char *const bmabbr, Bookmark *bmp)
388 {
389 FILE *fp;
390 char line[512];
391 Bookmark byHostName;
392 Bookmark byHostAbbr;
393 Bookmark byBmAbbr;
394 size_t byBmNameFlag = 0;
395 size_t byBmAbbrFlag = 0;
396 size_t byHostNameFlag = 0;
397 size_t byHostAbbrFlag = 0;
398 int result = -1;
399 int exactMatch = 0;
400 size_t bmabbrLen;
401 char *cp;
402
403 fp = OpenBookmarkFile(NULL);
404 if (fp == NULL)
405 return (-1);
406
407 bmabbrLen = strlen(bmabbr);
408 while (FGets(line, sizeof(line), fp) != NULL) {
409 if (ParseHostLine(line, bmp) < 0)
410 continue;
411 if (ISTREQ(bmp->bookmarkName, bmabbr)) {
412 /* Exact match, done. */
413 byBmNameFlag = bmabbrLen;
414 exactMatch = 1;
415 break;
416 } else if (ISTRNEQ(bmp->bookmarkName, bmabbr, bmabbrLen)) {
417 /* Remember this one, it matched an abbreviated
418 * bookmark name.
419 */
420 byBmAbbr = *bmp;
421 byBmAbbrFlag = bmabbrLen;
422 } else if (ISTREQ(bmp->name, bmabbr)) {
423 /* Remember this one, it matched a full
424 * host name.
425 */
426 byHostName = *bmp;
427 byHostNameFlag = bmabbrLen;
428 } else if ((cp = strchr(bmp->name, '.')) != NULL) {
429 /* See if it matched part of the hostname. */
430 if (ISTRNEQ(bmp->name, "ftp", 3)) {
431 cp = cp + 1;
432 } else if (ISTRNEQ(bmp->name, "www", 3)) {
433 cp = cp + 1;
434 } else {
435 cp = bmp->name;
436 }
437 if (ISTRNEQ(cp, bmabbr, bmabbrLen)) {
438 /* Remember this one, it matched a full
439 * host name.
440 */
441 byHostAbbr = *bmp;
442 byHostAbbrFlag = bmabbrLen;
443 }
444 }
445 }
446
447 if (gBookmarkMatchMode == 0) {
448 /* Only use a bookmark when the exact
449 * bookmark name was used.
450 */
451 if (exactMatch != 0) {
452 result = 0;
453 }
454 } else {
455 /* Pick the best match, if any. */
456 if (byBmNameFlag != 0) {
457 /* *bmp is already set. */
458 result = 0;
459 } else if (byBmAbbrFlag != 0) {
460 result = 0;
461 *bmp = byBmAbbr;
462 } else if (byHostNameFlag != 0) {
463 result = 0;
464 *bmp = byHostName;
465 } else if (byHostAbbrFlag != 0) {
466 result = 0;
467 *bmp = byHostAbbr;
468 }
469 }
470
471 if (result != 0)
472 memset(bmp, 0, sizeof(Bookmark));
473
474 CloseBookmarkFile(fp);
475 return (result);
476 } /* GetBookmark */
477
478
479
480
481 static int
BookmarkSortProc(const void * a,const void * b)482 BookmarkSortProc(const void *a, const void *b)
483 {
484 return (ISTRCMP((*(Bookmark *)a).bookmarkName, (*(Bookmark *)b).bookmarkName));
485 } /* BookmarkSortProc */
486
487
488
489 static int
BookmarkSearchProc(const void * key,const void * b)490 BookmarkSearchProc(const void *key, const void *b)
491 {
492 return (ISTRCMP((char *) key, (*(Bookmark *)b).bookmarkName));
493 } /* BookmarkSearchProc */
494
495
496
497 BookmarkPtr
SearchBookmarkTable(const char * key)498 SearchBookmarkTable(const char *key)
499 {
500 return ((BookmarkPtr) bsearch(key, gBookmarkTable, (size_t) gNumBookmarks, sizeof(Bookmark), BookmarkSearchProc));
501 } /* SearchBookmarkTable */
502
503
504
505
506 void
SortBookmarks(void)507 SortBookmarks(void)
508 {
509 if ((gBookmarkTable == NULL) || (gNumBookmarks < 2))
510 return;
511
512 /* Sorting involves swapping entire Bookmark structures.
513 * Normally the proper thing to do is to use an array
514 * of pointers to Bookmarks and sort them, but even
515 * these days a large bookmark list can be sorted in
516 * the blink of an eye.
517 */
518 qsort(gBookmarkTable, (size_t) gNumBookmarks, sizeof(Bookmark), BookmarkSortProc);
519 } /* SortBookmarks */
520
521
522
523 int
LoadBookmarkTable(void)524 LoadBookmarkTable(void)
525 {
526 int i, nb;
527 FILE *infp;
528
529 infp = OpenBookmarkFile(&nb);
530 if (infp == NULL) {
531 nb = 0;
532 }
533 if ((nb != gNumBookmarks) && (gBookmarkTable != NULL)) {
534 /* Re-loading the table from disk. */
535 gBookmarkTable = (Bookmark *) realloc(gBookmarkTable, (size_t) (nb + 1) * sizeof(Bookmark));
536 memset(gBookmarkTable, 0, (nb + 1) * sizeof(Bookmark));
537 } else {
538 gBookmarkTable = calloc((size_t) (nb + 1), (size_t) sizeof(Bookmark));
539 }
540
541 if (gBookmarkTable == NULL) {
542 CloseBookmarkFile(infp);
543 return (-1);
544 }
545
546 for (i=0; i<nb; i++) {
547 if (GetNextBookmark(infp, gBookmarkTable + i) < 0) {
548 break;
549 }
550 }
551 gNumBookmarks = i;
552
553 CloseBookmarkFile(infp);
554 SortBookmarks();
555 return (0);
556 } /* LoadBookmarkTable */
557
558
559
560
561 /* Some characters need to be escaped so the file is editable and can
562 * be parsed correctly the next time it is read.
563 */
564 static char *
BmEscapeTok(char * dst,size_t dsize,char * src)565 BmEscapeTok(char *dst, size_t dsize, char *src)
566 {
567 char *dlim = dst + dsize - 1;
568 char *dst0 = dst;
569 int c;
570
571 while ((c = *src) != '\0') {
572 src++;
573 if ((c == '\\') || (c == ',') || (c == '$')) {
574 /* These need to be escaped. */
575 if ((dst + 1) < dlim) {
576 *dst++ = '\\';
577 *dst++ = c;
578 }
579 } else if (!isprint(c)) {
580 /* Escape non-printing characters. */
581 if ((dst + 2) < dlim) {
582 (void) sprintf(dst, "$%02x", c);
583 dst += 3;
584 }
585 } else {
586 if (dst < dlim)
587 *dst++ = c;
588 }
589 }
590 *dst = '\0';
591 return (dst0);
592 } /* BmEscapeTok */
593
594
595
596
597 /* Converts a Bookmark into a text string, and writes it to the saved
598 * bookmarks file.
599 */
600 static int
WriteBmLine(Bookmark * bmp,FILE * outfp,int savePassword)601 WriteBmLine(Bookmark *bmp, FILE *outfp, int savePassword)
602 {
603 char tok[256];
604 char pass[160];
605
606 if (fprintf(outfp, "%s", bmp->bookmarkName) < 0) return (-1) ;/*1*/
607 if (fprintf(outfp, ",%s", BmEscapeTok(tok, sizeof(tok), bmp->name)) < 0) return (-1) ;/*2*/
608 if (fprintf(outfp, ",%s", BmEscapeTok(tok, sizeof(tok), bmp->user)) < 0) return (-1) ;/*3*/
609 if ((bmp->pass[0] != '\0') && (savePassword == 1)) {
610 (void) memcpy(pass, kPasswordMagic, kPasswordMagicLen);
611 ToBase64(pass + kPasswordMagicLen, bmp->pass, strlen(bmp->pass), 1);
612 if (fprintf(outfp, ",%s", pass) < 0) return (-1) ;/*4*/
613 } else {
614 if (fprintf(outfp, ",%s", "") < 0) return (-1) ;/*4*/
615 }
616 if (fprintf(outfp, ",%s", BmEscapeTok(tok, sizeof(tok), bmp->acct)) < 0) return (-1) ;/*5*/
617 if (fprintf(outfp, ",%s", BmEscapeTok(tok, sizeof(tok), bmp->dir)) < 0) return (-1) ;/*6*/
618 if (fprintf(outfp, ",%c", bmp->xferType) < 0) return (-1) ;/*7*/
619 if (fprintf(outfp, ",%u", (unsigned int) bmp->port) < 0) return (-1) ;/*8*/
620 if (fprintf(outfp, ",%lu", (unsigned long) bmp->lastCall) < 0) return (-1) ;/*9*/
621 if (fprintf(outfp, ",%d", bmp->hasSIZE) < 0) return (-1) ;/*10*/
622 if (fprintf(outfp, ",%d", bmp->hasMDTM) < 0) return (-1) ;/*11*/
623 if (fprintf(outfp, ",%d", bmp->hasPASV) < 0) return (-1) ;/*12*/
624 if (fprintf(outfp, ",%d", bmp->isUnix) < 0) return (-1) ;/*13*/
625 if (fprintf(outfp, ",%s", bmp->lastIP) < 0) return (-1) ;/*14*/
626 if (fprintf(outfp, ",%s", BmEscapeTok(tok, sizeof(tok), bmp->comment)) < 0) return (-1) ;/*15*/
627 if (fprintf(outfp, ",%s", "") < 0) return (-1) ;/*16*/
628 if (fprintf(outfp, ",%s", "") < 0) return (-1) ;/*17*/
629 if (fprintf(outfp, ",%s", "") < 0) return (-1) ;/*18*/
630 if (fprintf(outfp, ",%s", "") < 0) return (-1) ;/*19*/
631 if (fprintf(outfp, ",%c", bmp->xferMode) < 0) return (-1) ;/*20*/
632 if (fprintf(outfp, ",%d", bmp->hasUTIME) < 0) return (-1) ;/*21*/
633 if (fprintf(outfp, ",%s", BmEscapeTok(tok, sizeof(tok), bmp->ldir)) < 0) return (-1) ;/*22*/
634 if (fprintf(outfp, "\n") < 0) return (-1) ;
635 if (fflush(outfp) < 0) return (-1);
636 return (0);
637 } /* WriteBmLine */
638
639
640
641 static int
SwapBookmarkFiles(void)642 SwapBookmarkFiles(void)
643 {
644 char pidStr[32];
645 char pathName[256], path2[256];
646
647 (void) OurDirectoryPath(path2, sizeof(path2), kBookmarkFileName);
648 (void) OurDirectoryPath(pathName, sizeof(pathName), kTmpBookmarkFileName);
649 (void) sprintf(pidStr, "-%u.txt", (unsigned int) getpid());
650 (void) STRNCAT(pathName, pidStr);
651
652 (void) remove(path2);
653 if (rename(pathName, path2) < 0) {
654 return (-1);
655 }
656 return (0);
657 } /* SwapBookmarkFiles */
658
659
660
661
662
663
664 /* Saves a Bookmark structure into the bookmarks file. */
665 FILE *
OpenTmpBookmarkFile(int nb)666 OpenTmpBookmarkFile(int nb)
667 {
668 FILE *outfp;
669 char pidStr[32];
670 char pathName[256], path2[256];
671
672 if (gOurDirectoryPath[0] == '\0')
673 return (NULL); /* Don't create in root directory. */
674
675 (void) OurDirectoryPath(path2, sizeof(path2), kBookmarkFileName);
676 (void) OurDirectoryPath(pathName, sizeof(pathName), kTmpBookmarkFileName);
677 (void) sprintf(pidStr, "-%u.txt", (unsigned int) getpid());
678 (void) STRNCAT(pathName, pidStr);
679
680 outfp = fopen(pathName, FOPEN_WRITE_TEXT);
681 if (outfp == NULL) {
682 (void) fprintf(stderr, "Could not save bookmark.\n");
683 perror(pathName);
684 return (NULL);
685 }
686 (void) _chmod(pathName, 00600);
687 if (nb > 0) {
688 if (fprintf(outfp, "NcFTP bookmark-file version: %d\nNumber of bookmarks: %d\n", kBookmarkVersion, nb) < 0) {
689 (void) fprintf(stderr, "Could not save bookmark.\n");
690 perror(pathName);
691 (void) fclose(outfp);
692 return (NULL);
693 }
694 } else {
695 if (fprintf(outfp, "NcFTP bookmark-file version: %d\nNumber of bookmarks: ??\n", kBookmarkVersion) < 0) {
696 (void) fprintf(stderr, "Could not save bookmark.\n");
697 perror(pathName);
698 (void) fclose(outfp);
699 return (NULL);
700 }
701 }
702
703 return (outfp);
704 } /* OpenTmpBookmarkFile */
705
706
707
708
709 int
SaveBookmarkTable(void)710 SaveBookmarkTable(void)
711 {
712 int i;
713 FILE *outfp;
714 int nb;
715
716 if ((gNumBookmarks < 1) || (gBookmarkTable == NULL))
717 return (0); /* Nothing to save. */
718
719 /* Get a count of live bookmarks. */
720 for (i=0, nb=0; i<gNumBookmarks; i++) {
721 if (gBookmarkTable[i].deleted == 0)
722 nb++;
723 }
724 outfp = OpenTmpBookmarkFile(nb);
725 if (outfp == NULL) {
726 return (-1);
727 }
728
729 for (i=0; i<gNumBookmarks; i++) {
730 if (gBookmarkTable[i].deleted == 0) {
731 if (WriteBmLine(gBookmarkTable + i, outfp, 1) < 0) {
732 CloseBookmarkFile(outfp);
733 return (-1);
734 }
735 }
736 }
737 CloseBookmarkFile(outfp);
738 if (SwapBookmarkFiles() < 0) {
739 return (-1);
740 }
741 return (0);
742 } /* SaveBookmarkTable */
743
744
745
746 /* Saves a Bookmark structure into the bookmarks file. */
747 int
PutBookmark(Bookmark * bmp,int savePassword)748 PutBookmark(Bookmark *bmp, int savePassword)
749 {
750 FILE *infp, *outfp;
751 char line[256];
752 char bmAbbr[64];
753 int replaced = 0;
754 size_t len;
755
756 outfp = OpenTmpBookmarkFile(0);
757 if (outfp == NULL)
758 return (-1);
759
760 (void) STRNCPY(bmAbbr, bmp->bookmarkName);
761 (void) STRNCAT(bmAbbr, ",");
762 len = strlen(bmAbbr);
763
764 /* This may fail the first time we ever save a bookmark. */
765 infp = OpenBookmarkFile(NULL);
766 if (infp != NULL) {
767 while (FGets(line, sizeof(line), infp) != NULL) {
768 if (strncmp(line, bmAbbr, len) == 0) {
769 /* Replace previous entry. */
770 if (WriteBmLine(bmp, outfp, savePassword) < 0) {
771 (void) fprintf(stderr, "Could not save bookmark.\n");
772 perror("reason");
773 (void) fclose(outfp);
774 }
775 replaced = 1;
776 } else {
777 if (fprintf(outfp, "%s\n", line) < 0) {
778 (void) fprintf(stderr, "Could not save bookmark.\n");
779 perror("reason");
780 (void) fclose(outfp);
781 return (-1);
782 }
783 }
784 }
785 CloseBookmarkFile(infp);
786 }
787
788 if (replaced == 0) {
789 /* Add it as a new bookmark. */
790 if (WriteBmLine(bmp, outfp, savePassword) < 0) {
791 (void) fprintf(stderr, "Could not save bookmark.\n");
792 perror("reason");
793 (void) fclose(outfp);
794 return (-1);
795 }
796 }
797
798 if (fclose(outfp) < 0) {
799 (void) fprintf(stderr, "Could not save bookmark.\n");
800 perror("reason");
801 return (-1);
802 }
803
804 if (SwapBookmarkFiles() < 0) {
805 (void) fprintf(stderr, "Could not rename bookmark file.\n");
806 perror("reason");
807 return (-1);
808 }
809 return (0);
810 } /* PutBookmark */
811
812
813
814
815 /* Tries to generate a bookmark abbreviation based off of the hostname. */
816 void
DefaultBookmarkName(char * dst,size_t siz,char * src)817 DefaultBookmarkName(char *dst, size_t siz, char *src)
818 {
819 char str[128];
820 const char *token;
821 const char *cp;
822
823 (void) STRNCPY(str, src);
824
825 /* Pick the first "significant" part of the hostname. Usually
826 * this is the first word in the name, but if it's something like
827 * ftp.unl.edu, we would want to choose "unl" and not "ftp."
828 */
829 token = str;
830 if ((token = strtok(str, ".")) == NULL)
831 token = str;
832 else if ((ISTRNEQ(token, "ftp", 3)) || (ISTRNEQ(token, "www", 3))) {
833 if ((token = strtok(NULL, ".")) == NULL)
834 token = "";
835 }
836 for (cp = token; ; cp++) {
837 if (*cp == '\0') {
838 /* Token was all digits, like an IP address perhaps. */
839 token = "";
840 }
841 if (!isdigit((int) *cp))
842 break;
843 }
844 (void) Strncpy(dst, token, siz);
845 } /* DefaultBookmarkName */
846