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