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