PageRenderTime 113ms CodeModel.GetById 20ms app.highlight 85ms RepoModel.GetById 2ms app.codeStats 0ms

/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 */