1 /* pref.c
2  *
3  * Copyright (c) 1992-2016 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 #ifdef ncftp
14 #include "progress.h"
15 #endif
16 
17 #include "pref.h"
18 #include "util.h"
19 
20 /* How many times they've run this program. */
21 extern int gNumProgramRuns;
22 
23 /* Their $PAGER. */
24 char gPager[128];
25 
26 /* These correspond to the various timeouts from LibNcFTP. */
27 int gConnTimeout, gXferTimeout, gCtrlTimeout;
28 
29 /* Active or passive FTP?  (PORT or PASV?)  Or both? */
30 extern int gDataPortMode, gFwDataPortMode;
31 
32 /* When the destination file already exists, resume transfer or ask user? */
33 int gAutoResume;
34 
35 /* "Save a bookmark to this site before closing?" */
36 int gConfirmClose;
37 
38 /* Should we update the bookmark for the user? */
39 int gAutoSaveChangesToExistingBookmarks;
40 
41 /* "Save your password with the bookmark?" */
42 int gSavePasswords;
43 
44 int gMaySetXtermTitle;
45 
46 /* Number of seconds between connection attempts. */
47 int gRedialDelay;
48 
49 /* Some messages we only want to bug the user about once, ever. */
50 char gOneTimeMessagesSeen[256];
51 
52 /* Tune the size of the socket buffer using SO_RCVBUF or SO_SNDBUF? */
53 int gSOBufsize;
54 
55 /* Size of the user log before we trim it.  0 means do not log at all. */
56 int gMaxLogSize;
57 
58 /* Use ASCII mode automatically for files with these extensions. */
59 char gAutoAscii[512];
60 
61 #ifdef ncftp
62 /* Which meter to use. */
63 FTPProgressMeterProc gProgressMeter;
64 #endif
65 
66 /* Allow us to plug our other products? */
67 int gDoNotDisplayAds;
68 
69 /* Do we need to save the prefs, or can we skip it? */
70 int gPrefsDirty = 0;
71 
72 extern FTPLibraryInfo gLib;
73 extern FTPConnectionInfo gConn;
74 extern char gOurDirectoryPath[];
75 
76 PrefOpt gPrefOpts[] = {
77 	{ "anonopen",				PREFOBSELETE },
78 	{ "anonpass", 				SetAnonPass, kPrefOptObselete },
79 	{ "anon-password",			SetAnonPass, 1 },
80 	{ "auto-ascii",				SetAutoAscii, 1 },
81 	{ "auto-resume",			SetAutoResume, 1 },
82 	{ "autosave-bookmark-changes",		SetAutoSaveChangesToExistingBookmarks, 1 },
83 	{ "blank-lines",			PREFOBSELETE },
84 	{ "confirm-close",			SetConfirmClose, 1 },
85 	{ "connect-timeout",			SetConnTimeout, 1 },
86 	{ "control-timeout",			SetCtrlTimeout, 1 },
87 	{ "logsize",				SetLogSize, 1 },
88 	{ "maxbookmarks",			PREFOBSELETE },
89 	{ "one-time-messages-seen",		SetOneTimeMessages, 0 },
90 	{ "pager",				SetPager, 1 },
91 	{ "passive",				SetPassive, 1 },
92 	{ "progress-meter",			SetProgressMeter, 1 },
93 	{ "redial-delay",			SetRedialDelay, 1 },
94 	{ "remote-msgs", 			PREFOBSELETE },
95 	{ "restore-lcwd", 			PREFOBSELETE },
96 	{ "save-passwords",			SetSavePasswords, 1 },
97 	{ "show-trailing-space",		PREFOBSELETE },
98 	{ "show-status-in-xterm-titlebar",	SetXtTitle, 1 },
99 #ifdef SO_RCVBUF
100 	{ "so-bufsize",				SetSOBufsize, 1 },
101 #endif
102 	{ "startup-lcwd", 			PREFOBSELETE },
103 	{ "startup-msgs", 			PREFOBSELETE },
104 	{ "timeout",				PREFOBSELETE },
105 	{ "total-runs", 			PREFOBSELETE },
106 	{ "total-xfer-hundredths-of-seconds", 	PREFOBSELETE },
107 	{ "total-xfer-kbytes", 			PREFOBSELETE },
108 	{ "trace",				PREFOBSELETE },
109 	{ "utime",				PREFOBSELETE },
110 	{ "visual",				PREFOBSELETE },
111 	{ "xfer-timeout",			SetXferTimeout, 1 },
112 	{ "yes-i-know-about-NcFTPd",		SetNoAds, 1 },
113 	{ NULL,					(PrefProc) 0, kPrefOptInvisible, },
114 };
115 
116 int gNumPrefOpts = ((int)(sizeof(gPrefOpts) / sizeof(PrefOpt)) - 1);
117 
118 
119 
120 void
SetAnonPass(const char * const val,FILE * const fp)121 SetAnonPass(const char *const val, FILE *const fp)
122 {
123 	if (fp != NULL) {
124 		(void) fprintf(fp, "%s", gLib.defaultAnonPassword);
125 	} else {
126 		(void) STRNCPY(gLib.defaultAnonPassword, val);
127 	}
128 }	/* SetAnonPass */
129 
130 
131 
132 void
SetAutoAscii(const char * const val,FILE * const fp)133 SetAutoAscii(const char *const val, FILE *const fp)
134 {
135 	if (fp != NULL) {
136 		(void) fprintf(fp, "%s", gAutoAscii);
137 	} else {
138 		(void) STRNCPY(gAutoAscii, val);
139 		if ((gAutoAscii[0] == '\0') || (ISTREQ(gAutoAscii, "no")) || (ISTREQ(gAutoAscii, "off")) || (ISTREQ(gAutoAscii, "false"))) {
140 			gConn.asciiFilenameExtensions = NULL;
141 		} else {
142 			gConn.asciiFilenameExtensions = gAutoAscii;
143 		}
144 	}
145 }	/* SetAutoAscii */
146 
147 
148 
149 void
SetAutoResume(const char * const val,FILE * const fp)150 SetAutoResume(const char *const val, FILE *const fp)
151 {
152 	if (fp != NULL) {
153 		(void) fprintf(fp, "%s", YESNO(gAutoResume));
154 	} else {
155 		gAutoResume = StrToBool(val);
156 	}
157 }	/* SetAutoResume */
158 
159 
160 
161 void
SetAutoSaveChangesToExistingBookmarks(const char * const val,FILE * const fp)162 SetAutoSaveChangesToExistingBookmarks(const char *const val, FILE *const fp)
163 {
164 	if (fp != NULL) {
165 		(void) fprintf(fp, "%s", YESNO(gAutoSaveChangesToExistingBookmarks));
166 	} else {
167 		gAutoSaveChangesToExistingBookmarks = StrToBool(val);
168 	}
169 }	/* SetAutoSaveChangesToExistingBookmarks */
170 
171 
172 
173 void
SetConfirmClose(const char * const val,FILE * const fp)174 SetConfirmClose(const char *const val, FILE *const fp)
175 {
176 	if (fp != NULL) {
177 		(void) fprintf(fp, "%s", YESNO(gConfirmClose));
178 	} else {
179 		gConfirmClose = StrToBool(val);
180 	}
181 }	/* SetConfirmClose */
182 
183 
184 
185 void
SetConnTimeout(const char * const val,FILE * const fp)186 SetConnTimeout(const char *const val, FILE *const fp)
187 {
188 	if (fp != NULL) {
189 		(void) fprintf(fp, "%d", gConnTimeout);
190 	} else {
191 		gConn.connTimeout = (unsigned int) atoi(val);
192         gConnTimeout = atoi(val);
193 	}
194 }	/* SetConnTimeout */
195 
196 
197 
198 void
SetCtrlTimeout(const char * const val,FILE * const fp)199 SetCtrlTimeout(const char *const val, FILE *const fp)
200 {
201 	if (fp != NULL) {
202 		(void) fprintf(fp, "%d", gCtrlTimeout);
203 	} else {
204         gConn.ctrlTimeout = (unsigned int) atoi(val);
205         gCtrlTimeout = atoi(val);
206 	}
207 }	/* SetCtrlTimeout */
208 
209 
210 
211 void
SetLogSize(const char * const val,FILE * const fp)212 SetLogSize(const char *const val, FILE *const fp)
213 {
214 	if (fp != NULL) {
215 		(void) fprintf(fp, "%d", gMaxLogSize);
216 	} else {
217 		gMaxLogSize = atoi(val);
218 	}
219 }	/* SetLogSize */
220 
221 
222 
223 void
SetNoAds(const char * const val,FILE * const fp)224 SetNoAds(const char *const val, FILE *const fp)
225 {
226 	if (fp != NULL) {
227 		(void) fprintf(fp, "%s", YESNO(gDoNotDisplayAds));
228 	} else {
229 		gDoNotDisplayAds = StrToBool(val);
230 	}
231 }	/* SetNoAds */
232 
233 
234 
235 void
SetOneTimeMessages(const char * const val,FILE * const fp)236 SetOneTimeMessages(const char *const val, FILE *const fp)
237 {
238 	if (fp != NULL) {
239 		(void) fprintf(fp, "%s", gOneTimeMessagesSeen);
240 	} else {
241 		(void) STRNCPY(gOneTimeMessagesSeen, val);
242 	}
243 }	/* SetOneTimeMessages */
244 
245 
246 
247 void
SetPager(const char * const val,FILE * const fp)248 SetPager(const char *const val, FILE *const fp)
249 {
250 	if (fp != NULL) {
251 		(void) fprintf(fp, "%s", gPager);
252 	} else {
253 		(void) STRNCPY(gPager, val);
254 	}
255 }	/* SetPager */
256 
257 
258 
259 void
SetPassive(const char * const val,FILE * const fp)260 SetPassive(const char *const val, FILE *const fp)
261 {
262 	int m;
263 
264 	if (fp != NULL) {
265 		m = (gFwDataPortMode >= 0) ? gFwDataPortMode : gDataPortMode;
266 		if (m == kSendPortMode) {
267 			(void) fprintf(fp, "%s", "off");
268 		} else if (m == kPassiveMode) {
269 			(void) fprintf(fp, "%s", "on");
270 		} else {
271 			(void) fprintf(fp, "%s", "optional");
272 		}
273 	} else {
274 		if (gFwDataPortMode >= 0) {
275 			gDataPortMode = gFwDataPortMode;
276 			return;
277 		}
278 		if (ISTRNEQ(val, "opt", 3))
279 			gDataPortMode = kFallBackToSendPortMode;
280 		else if (ISTREQ(val, "on"))
281 			gDataPortMode = kPassiveMode;
282 		else if (isdigit((int) val[0]))
283 			gDataPortMode = atoi(val);
284 		else
285 			gDataPortMode = kSendPortMode;
286 		gConn.dataPortMode = gDataPortMode;
287 	}
288 }	/* SetPassive */
289 
290 
291 
292 #ifdef ncftp
293 void
SetProgressMeter(const char * const val,FILE * const fp)294 SetProgressMeter(const char *const val, FILE *const fp)
295 {
296 	if (fp != NULL) {
297 		if (gProgressMeter == PrStatBar) {
298 			(void) fprintf(fp, "%s", "2 (statbar)");
299 		} else if (gProgressMeter == PrPhilBar) {
300 			(void) fprintf(fp, "%s", "1 (philbar)");
301 		} else {
302 			(void) fprintf(fp, "%s", "0 (simple)");
303 		}
304 	} else {
305 		if ((val[0] == '0') || (ISTRNEQ(val, "simple", 6)))
306 			gProgressMeter = PrSizeAndRateMeter;
307 		else if ((val[0] == '1') || (ISTRNEQ(val, "phil", 4)))
308 			gProgressMeter = PrPhilBar;
309 		else
310 			gProgressMeter = PrStatBar;
311 		gConn.progress = gProgressMeter;
312 	}
313 }	/* SetProgressMeter */
314 #else
315 void
SetProgressMeter(const char * const UNUSED (val),FILE * const UNUSED (fp))316 SetProgressMeter(const char *const UNUSED(val), FILE *const UNUSED(fp))
317 {
318 	LIBNCFTP_USE_VAR(val);
319 	LIBNCFTP_USE_VAR(fp);
320 }	/* SetProgressMeter */
321 #endif
322 
323 
324 
325 void
SetRedialDelay(const char * const val,FILE * const fp)326 SetRedialDelay(const char *const val, FILE *const fp)
327 {
328 	int i;
329 
330 	if (fp != NULL) {
331 		(void) fprintf(fp, "%d", gRedialDelay);
332 	} else {
333 		i = atoi(val);
334 		if (i < 10)
335 			i = 10;
336 		gRedialDelay = atoi(val);
337 	}
338 }	/* SetRedialDelay */
339 
340 
341 
342 void
SetSavePasswords(const char * const val,FILE * const fp)343 SetSavePasswords(const char *const val, FILE *const fp)
344 {
345 	if (fp != NULL) {
346 		if (gSavePasswords < 0)
347 			(void) fprintf(fp, "%s", "ask");
348 		else
349 			(void) fprintf(fp, "%s", YESNO(gSavePasswords));
350 	} else {
351 		if (ISTREQ(val, "ask"))
352 			gSavePasswords = -1;
353 		else
354 			gSavePasswords = StrToBool(val);
355 	}
356 }	/* SetSavePasswords */
357 
358 
359 
360 void
SetSOBufsize(const char * const val,FILE * const fp)361 SetSOBufsize(const char *const val, FILE *const fp)
362 {
363 	if (fp != NULL) {
364 		(void) fprintf(fp, "%d", gSOBufsize);
365 		if (gSOBufsize <= 0)
366 			(void) fprintf(fp, "%s", " (use system default)");
367 	} else {
368 		gConn.dataSocketRBufSize = gConn.dataSocketSBufSize = (size_t) atoi(val);
369         gSOBufsize = atoi(val);
370 	}
371 }	/* SetSOBufsize */
372 
373 
374 
375 
376 void
SetXferTimeout(const char * const val,FILE * const fp)377 SetXferTimeout(const char *const val, FILE *const fp)
378 {
379 	if (fp != NULL) {
380 		(void) fprintf(fp, "%d", gXferTimeout);
381 	} else {
382 		gConn.xferTimeout = (unsigned int) atoi(val);
383         gXferTimeout = atoi(val);
384 	}
385 }	/* SetXferTimeout */
386 
387 
388 
389 void
SetXtTitle(const char * const val,FILE * const fp)390 SetXtTitle(const char *const val, FILE *const fp)
391 {
392 	if (fp != NULL) {
393 		(void) fprintf(fp, "%s", YESNO(gMaySetXtermTitle));
394 	} else {
395 		gMaySetXtermTitle = StrToBool(val);
396 	}
397 }	/* SetXtTitle */
398 
399 
400 
401 
402 static void
Show1(int t)403 Show1(int t)
404 {
405 	PrefOpt *p = &gPrefOpts[t];
406 
407 	(void) printf("%-30s ", p->varname);
408 	if (p->proc != (PrefProc) 0)
409 		(*p->proc)(NULL, stdout);
410 	(void) printf("\n");
411 }	/* Show1 */
412 
413 
414 
415 
416 /* Modify or display the program's configuration. */
417 void
Set(const char * const tok1,const char * const tok2)418 Set(const char *const tok1, const char *const tok2)
419 {
420 	int t;
421 
422 	if ((tok1 == NULL) || (ISTREQ(tok1, "all"))) {
423 		/* Show all. */
424 		for (t=0; t<gNumPrefOpts; t++) {
425 			if (gPrefOpts[t].visible == kPrefOptVisible)
426 				Show1(t);
427 		}
428 	} else if (tok2 == NULL) {
429 		/* Show one. */
430 		for (t=0; t<gNumPrefOpts; t++) {
431 			if (ISTREQ(tok1, gPrefOpts[t].varname)) {
432 				if (gPrefOpts[t].visible == kPrefOptObselete) {
433 					(void) printf("The \"%s\" option is obselete or not implemented.\n", tok1);
434 				} else {
435 					Show1(t);
436 				}
437 				break;
438 			}
439 		}
440 		if (t >= gNumPrefOpts) {
441 			(void) printf("Unknown option \"%s\" -- try \"show all\" to list available options.\n", tok1);
442 		}
443 	} else {
444 		/* Set one. */
445 		for (t=0; t<gNumPrefOpts; t++) {
446 			if (ISTREQ(tok1, gPrefOpts[t].varname)) {
447 				if (gPrefOpts[t].visible == kPrefOptObselete) {
448 					(void) printf("The \"%s\" option is obselete or not implemented.\n", tok1);
449 				} else if (gPrefOpts[t].proc != (PrefProc) 0) {
450 					(*gPrefOpts[t].proc)(tok2, NULL);
451 					gPrefsDirty++;
452 				}
453 				break;
454 			}
455 		}
456 		if (t >= gNumPrefOpts) {
457 			(void) printf("Unknown option \"%s\" -- try \"show all\" to list available options.\n", tok1);
458 		}
459 	}
460 }	/* Set */
461 
462 
463 
464 int
HasSeenOneTimeMessage(const char * const msg)465 HasSeenOneTimeMessage(const char *const msg)
466 {
467 	char buf[256];
468 	char *a, *b;
469 
470 	memcpy(buf, gOneTimeMessagesSeen, sizeof(buf));
471 	for (a = buf; (b = strtok(a, ",\n")) != NULL; a = NULL) {
472 		if (strcmp(msg, b) == 0)
473 			return (1);
474 	}
475 	return (0);
476 }	/* HasSeenOneTimeMessage */
477 
478 
479 
480 
481 void
SetSeenOneTimeMessage(const char * const msg)482 SetSeenOneTimeMessage(const char *const msg)
483 {
484 	gPrefsDirty++;
485 	if (gOneTimeMessagesSeen[0] == '\0')
486 		STRNCPY(gOneTimeMessagesSeen, msg);
487 	else {
488 		STRNCAT(gOneTimeMessagesSeen, ",");
489 		STRNCAT(gOneTimeMessagesSeen, msg);
490 	}
491 }	/* SetSeenOneTimeMessage */
492 
493 
494 
495 int
OneTimeMessage(const char * const msg)496 OneTimeMessage(const char *const msg)
497 {
498 	if (HasSeenOneTimeMessage(msg))
499 		return (0);
500 	SetSeenOneTimeMessage(msg);
501 	return (1);
502 }	/* OneTimeMessage */
503 
504 
505 
506 
507 void
ProcessPrefsFile(FILE * const fp)508 ProcessPrefsFile(FILE *const fp)
509 {
510 	char line[1024];
511 	char *tok1, *tok2;
512 	int t;
513 
514 	line[sizeof(line) - 1] = '\0';
515 	while (fgets(line, sizeof(line) - 1, fp) != NULL) {
516 		tok1 = strtok(line, " =\t\r\n");
517 		if ((tok1 == NULL) || (tok1[0] == '#'))
518 			continue;
519 		tok2 = strtok(NULL, "\r\n");
520 		if (tok2 == NULL)
521 			continue;
522 
523 		for (t=0; t<gNumPrefOpts; t++) {
524 			if (ISTREQ(tok1, gPrefOpts[t].varname)) {
525 				if (gPrefOpts[t].visible == kPrefOptObselete) {
526 					/* Probably converting an
527 					 * old 2.4.2 file.
528 					 */
529 					gPrefsDirty++;
530 				} else if (gPrefOpts[t].proc != (PrefProc) 0) {
531 					(*gPrefOpts[t].proc)(tok2, NULL);
532 				}
533 			}
534 		}
535 	}
536 }	/* ProcessPrefsFile */
537 
538 
539 
540 
541 /* Read the saved configuration settings from a preferences file. */
542 void
LoadPrefs(void)543 LoadPrefs(void)
544 {
545 	FILE *fp;
546 	char pathName[256];
547 
548 	/* As with the firewall preference file, there can be
549 	 * site-wide preferences and user-specific preferences.
550 	 * The user pref file is of course kept in the user's
551 	 * NcFTP home directory.
552 	 *
553 	 * The way we do this is we first look for a global
554 	 * preferences file.  We then process the user's pref
555 	 * file, which could override the global prefs.  Finally,
556 	 * we open a "global fixed" prefs file which then
557 	 * overrides anything the user may have done.
558 	 */
559 
560 	fp = fopen(kGlobalPrefFileName, FOPEN_READ_TEXT);
561 	if (fp != NULL) {
562 		/* Opened the global (but user-overridable) prefs file. */
563 		ProcessPrefsFile(fp);
564 		(void) fclose(fp);
565 	}
566 
567 	if (gOurDirectoryPath[0] != '\0') {
568 		(void) OurDirectoryPath(pathName, sizeof(pathName), kPrefFileName);
569 
570 		fp = fopen(pathName, FOPEN_READ_TEXT);
571 		if (fp == NULL) {
572 			/* Try loading the version 2 prefs.
573 			 * There will be options we no longer recognize, but
574 			 * we'd like to import the prefs when possible.
575 			 */
576 			gPrefsDirty++;
577 			(void) OurDirectoryPath(pathName, sizeof(pathName), kPrefFileNameV2);
578 			fp = fopen(pathName, FOPEN_READ_TEXT);
579 		}
580 
581 		if (fp == NULL) {
582 			/* Write a new one when we're done. */
583 			gPrefsDirty++;
584 		} else {
585 			/* Opened the preferences file. */
586 			ProcessPrefsFile(fp);
587 			(void) fclose(fp);
588 		}
589 	}
590 
591 	fp = fopen(kGlobalFixedPrefFileName, FOPEN_READ_TEXT);
592 	if (fp != NULL) {
593 		/* Opened the global (and not overridable) prefs file. */
594 		ProcessPrefsFile(fp);
595 		(void) fclose(fp);
596 	}
597 }	/* LoadPrefs */
598 
599 
600 
601 
602 /* Initialize the configuration settings, in case the user does not set them. */
603 void
InitPrefs(void)604 InitPrefs(void)
605 {
606 	char *tok1;
607 
608 	/* Set default values. */
609 	gPager[0] = '\0';
610 	memset(gOneTimeMessagesSeen, 0, sizeof(gOneTimeMessagesSeen));
611 	gXferTimeout = 3600;
612 	gConnTimeout = 20;
613 	gCtrlTimeout = 135;
614 	gDataPortMode = kFallBackToSendPortMode;
615 	gConn.dataPortMode = gDataPortMode;
616 	gAutoResume = 0;
617 	gSOBufsize = 0;
618 	gMaxLogSize = 10240;
619 	gConfirmClose = 0;
620 	gAutoSaveChangesToExistingBookmarks = 0;
621 	gRedialDelay = kDefaultRedialDelay;
622 	STRNCPY(gAutoAscii, "|.txt|.asc|.html|.htm|.css|.xml|.ini|.pl|.hqx|.cfg|.c|.h|.cpp|.hpp|.bat|.m3u|.pls|");
623 
624 	/* PLEASE do not change the default from 0, and please
625 	 * don't hack out the portion in main.c which displays
626 	 * a plug every 7th time you run the program.  This is
627 	 * not much to ask for all the work I've put into this
628 	 * since 1991.
629 	 */
630 	gDoNotDisplayAds = 0;
631 
632 #if ( (defined(WIN32) || defined(_WINDOWS)) && !defined(__CYGWIN__) )\
633 	&& defined(_CONSOLE)
634 	gMaySetXtermTitle = 1;
635 #else
636 	gMaySetXtermTitle = 0;
637 #endif
638 
639 	gSavePasswords = -1;
640 #ifdef ncftp
641 	gProgressMeter = PrStatBar;
642 #endif
643 
644 	tok1 = getenv("PAGER");
645 	if ((tok1 != NULL) && (tok1[0] != '\0')) {
646 #ifdef HAVE_STRSTR
647 		/* I prefer "less", but it doesn't work well here
648 		 * because it clears the screen after it finishes,
649 		 * and the default at EOF is to stay in less
650 		 * instead of exiting.
651 		 */
652 		if (strstr(gPager, "less") != NULL)
653 			(void) STRNCPY(gPager, "more");
654 		else
655 			(void) STRNCPY(gPager, tok1);
656 #else
657 		(void) STRNCPY(gPager, tok1);
658 #endif
659 	} else {
660 		(void) STRNCPY(gPager, "more");
661 	}
662 }	/* InitPrefs */
663 
664 
665 
666 
667 /* After reading the preferences, do some additional initialization. */
668 void
PostInitPrefs(void)669 PostInitPrefs(void)
670 {
671 	if (gLib.defaultAnonPassword[0] == '\0') {
672 		FTPInitializeAnonPassword(&gLib);
673 		gPrefsDirty++;
674 	}
675 	if (gFwDataPortMode >= 0)
676 		gConn.dataPortMode = gFwDataPortMode;
677 }	/* PostInitPrefs */
678 
679 
680 
681 
682 /* Write the configuration settings to a preferences file. */
683 void
SavePrefs(void)684 SavePrefs(void)
685 {
686 	char pathName[256];
687 	char pathName2[256];
688 	char tName[32];
689 	int t;
690 	FILE *fp;
691 
692 	if (gPrefsDirty == 0)
693 		return;		/* Don't need to save -- no changes made. */
694 
695 	(void) OurDirectoryPath(pathName, sizeof(pathName), kPrefFileName);
696 
697 	(void) sprintf(tName, "tpref%06u.txt", (unsigned int) getpid());
698 	(void) OurDirectoryPath(pathName2, sizeof(pathName2), tName);
699 
700 	fp = fopen(pathName2, FOPEN_WRITE_TEXT);
701 	if (fp == NULL) {
702 		perror("could not save preferences file");
703 	} else {
704 		(void) fprintf(fp, "%s", "# NcFTP 3 preferences file\n# This file is loaded and overwritten each time NcFTP is run.\n#\n");
705 		for (t=0; t<gNumPrefOpts; t++) {
706 			if (gPrefOpts[t].visible != kPrefOptObselete) {
707 				(void) fprintf(fp, "%s=", gPrefOpts[t].varname);
708 				(*gPrefOpts[t].proc)(NULL, fp);
709 				(void) fprintf(fp, "\n");
710 			}
711 		}
712 		(void) fclose(fp);
713 		(void) unlink(pathName);
714 		if (rename(pathName2, pathName) < 0) {
715 			perror("could not finish saving preferences file");
716 			(void) unlink(pathName2);
717 		};
718 	}
719 }	/* SavePrefs */
720 
721 
722 
723 /* This maintains the little counter file that is used by version 3.0
724  * to do things based on how many times the program was run.
725  */
726 void
CheckForNewV3User(void)727 CheckForNewV3User(void)
728 {
729 	FILE *fp;
730 	struct stat st;
731 	char pathName[256];
732 	char line[256];
733 
734 	gNumProgramRuns = 0;
735 
736 	/* Don't create in root directory. */
737 	if (gOurDirectoryPath[0] != '\0') {
738 		(void) OurDirectoryPath(pathName, sizeof(pathName), kFirstFileName);
739 
740 		if ((stat(pathName, &st) < 0) && (errno == ENOENT)) {
741 			gNumProgramRuns = 1;
742 			gPrefsDirty++;
743 
744 			/* Create a blank one. */
745 			fp = fopen(pathName, FOPEN_WRITE_TEXT);
746 			if (fp == NULL)
747 				return;
748 			(void) fprintf(fp, "# NcFTP uses this file to mark that you have run it before, and that you do not\n# need any special first-time instructions or setup.\n#\nruns=%d\n", gNumProgramRuns);
749 			(void) fclose(fp);
750 		} else {
751 			fp = fopen(pathName, FOPEN_READ_TEXT);
752 			if (fp != NULL) {
753 				while (fgets(line, sizeof(line) - 1, fp) != NULL) {
754 					if (strncmp(line, "runs=", 5) == 0) {
755 						(void) sscanf(line + 5, "%d",
756 							&gNumProgramRuns);
757 						break;
758 					}
759 				}
760 				(void) fclose(fp);
761 			}
762 
763 			/* Increment the count of program runs. */
764 			gNumProgramRuns++;
765 			if (gNumProgramRuns == 1)
766 				gPrefsDirty++;
767 
768 			/* Race condition between other ncftp processes.
769 			 * This isn't a big deal because this counter isn't
770 			 * critical.
771 			 */
772 
773 			fp = fopen(pathName, FOPEN_WRITE_TEXT);
774 			if (fp != NULL) {
775 				(void) fprintf(fp, "# NcFTP uses this file to mark that you have run it before, and that you do not\n# need any special first-time instructions or setup.\n#\nruns=%d\n", gNumProgramRuns);
776 				(void) fclose(fp);
777 			}
778 		}
779 	}
780 }	/* CheckForNewV3User */
781