xref: /netbsd-src/libexec/ftpd/conf.c (revision f5d3fbbc6ff4a77159fb268d247bd94cb7d7e332)
1 /*	$NetBSD: conf.c,v 1.7 1997/10/19 18:15:23 mycroft Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Simon Burge and Luke Mewburn.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 __RCSID("$NetBSD: conf.c,v 1.7 1997/10/19 18:15:23 mycroft Exp $");
42 #endif /* not lint */
43 
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 
48 #include <errno.h>
49 #include <glob.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <string.h>
54 #include <stringlist.h>
55 #include <syslog.h>
56 
57 #include "extern.h"
58 #include "pathnames.h"
59 
60 struct ftpclass curclass;
61 static char *strend __P((const char *, char *));
62 static int filetypematch __P((char *, int));
63 
64 /*
65  * Parse the configuration file, looking for the named class, and
66  * define curclass to contain the appropriate settings.
67  */
68 void
69 parse_conf(findclass)
70 	char *findclass;
71 {
72 	FILE		*f;
73 	char		*buf, *p;
74 	size_t		 len;
75 	int		 none, match;
76 	char		*endp;
77 	char		*class, *word, *arg;
78 	const char	*infile;
79 	int		 line;
80 	unsigned int	 timeout;
81 	struct ftpconv	*conv, *cnext;
82 
83 #define REASSIGN(X,Y)	if (X) free(X); (X)=(Y)
84 #define NEXTWORD(W)	while ((W = strsep(&buf, " \t")) != NULL && *W == '\0')
85 #define EMPTYSTR(W)	(W == NULL || *W == '\0')
86 
87 	REASSIGN(curclass.classname, findclass);
88 	for (conv = curclass.conversions; conv != NULL; conv=cnext) {
89 		REASSIGN(conv->suffix, NULL);
90 		REASSIGN(conv->types, NULL);
91 		REASSIGN(conv->disable, NULL);
92 		REASSIGN(conv->command, NULL);
93 		cnext = conv->next;
94 		free(conv);
95 	}
96 	curclass.conversions =	NULL;
97 	REASSIGN(curclass.display, NULL);
98 	curclass.modify =	1;
99 	curclass.maxtimeout =	7200;		/* 2 hours */
100 	REASSIGN(curclass.notify, NULL);
101 	curclass.timeout =	900;		/* 15 minutes */
102 	curclass.umask =	027;
103 
104 	if (strcasecmp(findclass, "guest") == 0) {
105 		curclass.umask = 0707;
106 		curclass.modify = 0;
107 	}
108 
109 	infile = conffilename(_PATH_FTPDCONF);
110 	if ((f = fopen(infile, "r")) == NULL)
111 		return;
112 
113 	line = 0;
114 	while ((buf = fgetln(f, &len)) != NULL) {
115 		none = match = 0;
116 		line++;
117 		if (len < 1)
118 			continue;
119 		if (buf[len - 1] != '\n') {
120 			syslog(LOG_WARNING,
121 			    "%s line %d is partially truncated?", infile, line);
122 			continue;
123 		}
124 		buf[--len] = '\0';
125 		if ((p = strchr(buf, '#')) != NULL)
126 			*p = '\0';
127 		if (EMPTYSTR(buf))
128 			continue;
129 
130 		NEXTWORD(word);
131 		NEXTWORD(class);
132 		NEXTWORD(arg);
133 		if (EMPTYSTR(word) || EMPTYSTR(class))
134 			continue;
135 		if (strcasecmp(class, "none") == 0)
136 			none = 1;
137 		if (strcasecmp(class, findclass) != 0 &&
138 		    !none && strcasecmp(class, "all") != 0)
139 			continue;
140 
141 		if (strcasecmp(word, "conversion") == 0) {
142 			char *suffix, *types, *disable, *convcmd;
143 
144 			if (EMPTYSTR(arg)) {
145 				syslog(LOG_WARNING,
146 				    "%s line %d: %s requires a suffix",
147 				    infile, line, word);
148 				continue;	/* need a suffix */
149 			}
150 			NEXTWORD(types);
151 			NEXTWORD(disable);
152 			convcmd = buf;
153 			if (convcmd)
154 				convcmd += strspn(convcmd, " \t");
155 			suffix = strdup(arg);
156 			if (suffix == NULL) {
157 				syslog(LOG_WARNING, "can't strdup");
158 				continue;
159 			}
160 			if (none || EMPTYSTR(types) ||
161 			    EMPTYSTR(disable) || EMPTYSTR(convcmd)) {
162 				types = NULL;
163 				disable = NULL;
164 				convcmd = NULL;
165 			} else {
166 				types = strdup(types);
167 				disable = strdup(disable);
168 				convcmd = strdup(convcmd);
169 				if (types == NULL || disable == NULL ||
170 				    convcmd == NULL) {
171 					syslog(LOG_WARNING, "can't strdup");
172 					if (types)
173 						free(types);
174 					if (disable)
175 						free(disable);
176 					if (convcmd)
177 						free(convcmd);
178 					continue;
179 				}
180 			}
181 			for (conv = curclass.conversions; conv != NULL;
182 			    conv = conv->next) {
183 				if (strcmp(conv->suffix, suffix) == 0)
184 					break;
185 			}
186 			if (conv == NULL) {
187 				conv = (struct ftpconv *)
188 				    calloc(1, sizeof(struct ftpconv));
189 				if (conv == NULL) {
190 					syslog(LOG_WARNING, "can't malloc");
191 					continue;
192 				}
193 				conv->next = curclass.conversions;
194 				curclass.conversions = conv;
195 			}
196 			REASSIGN(conv->suffix, suffix);
197 			REASSIGN(conv->types, types);
198 			REASSIGN(conv->disable, disable);
199 			REASSIGN(conv->command, convcmd);
200 		} else if (strcasecmp(word, "display") == 0) {
201 			if (none || EMPTYSTR(arg))
202 				arg = NULL;
203 			else
204 				arg = strdup(arg);
205 			REASSIGN(curclass.display, arg);
206 		} else if (strcasecmp(word, "maxtimeout") == 0) {
207 			if (none || EMPTYSTR(arg))
208 				continue;
209 			timeout = (unsigned int)strtoul(arg, &endp, 10);
210 			if (*endp != 0) {
211 				syslog(LOG_WARNING,
212 				    "%s line %d: invalid maxtimeout %s",
213 				    infile, line, arg);
214 				continue;
215 			}
216 			if (timeout < 30) {
217 				syslog(LOG_WARNING,
218 				    "%s line %d: maxtimeout %d < 30 seconds",
219 				    infile, line, timeout);
220 				continue;
221 			}
222 			if (timeout < curclass.timeout) {
223 				syslog(LOG_WARNING,
224 				    "%s line %d: maxtimeout %d < timeout (%d)",
225 				    infile, line, timeout, curclass.timeout);
226 				continue;
227 			}
228 			curclass.maxtimeout = timeout;
229 		} else if (strcasecmp(word, "modify") == 0) {
230 			if (none ||
231 			    (!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0))
232 				curclass.modify = 0;
233 			else
234 				curclass.modify = 1;
235 		} else if (strcasecmp(word, "notify") == 0) {
236 			if (none || EMPTYSTR(arg))
237 				arg = NULL;
238 			else
239 				arg = strdup(arg);
240 			REASSIGN(curclass.notify, arg);
241 		} else if (strcasecmp(word, "timeout") == 0) {
242 			if (none || EMPTYSTR(arg))
243 				continue;
244 			timeout = (unsigned int)strtoul(arg, &endp, 10);
245 			if (*endp != 0) {
246 				syslog(LOG_WARNING,
247 				    "%s line %d: invalid timeout %s",
248 				    infile, line, arg);
249 				continue;
250 			}
251 			if (timeout < 30) {
252 				syslog(LOG_WARNING,
253 				    "%s line %d: timeout %d < 30 seconds",
254 				    infile, line, timeout);
255 				continue;
256 			}
257 			if (timeout > curclass.maxtimeout) {
258 				syslog(LOG_WARNING,
259 				    "%s line %d: timeout %d > maxtimeout (%d)",
260 				    infile, line, timeout, curclass.maxtimeout);
261 				continue;
262 			}
263 			curclass.timeout = timeout;
264 		} else if (strcasecmp(word, "umask") == 0) {
265 			mode_t umask;
266 
267 			if (none || EMPTYSTR(arg))
268 				continue;
269 			umask = (mode_t)strtoul(arg, &endp, 8);
270 			if (*endp != 0 || umask > 0777) {
271 				syslog(LOG_WARNING,
272 				    "%s line %d: invalid umask %s",
273 				    infile, line, arg);
274 				continue;
275 			}
276 			curclass.umask = umask;
277 		} else {
278 			syslog(LOG_WARNING,
279 			    "%s line %d: unknown directive '%s'",
280 			    infile, line, word);
281 			continue;
282 		}
283 	}
284 #undef REASSIGN
285 #undef NEXTWORD
286 #undef EMPTYSTR
287 	fclose(f);
288 }
289 
290 /*
291  * Show file listed in curclass.display first time in, and list all the
292  * files named in curclass.notify in the current directory.  Send back
293  * responses with the "reply" prefix.
294  */
295 void
296 show_chdir_messages(code)
297 	int	code;
298 {
299 	static StringList *slist = NULL;
300 
301 	struct stat st;
302 	struct tm *t;
303 	glob_t	 gl;
304 	time_t	 now, then;
305 	int	 age;
306 	char	 cwd[MAXPATHLEN + 1];
307 	char	 line[BUFSIZ];
308 	char	*cp, **rlist;
309 	FILE	*f;
310 
311 		/* Setup list for directory cache */
312 	if (slist == NULL)
313 		slist = sl_init();
314 
315 		/* Check if this directory has already been visited */
316 	if (getcwd(cwd, sizeof(cwd) - 1) == NULL) {
317 		syslog(LOG_WARNING, "can't malloc");
318 		return;
319 	}
320 	if (sl_find(slist, cwd) != NULL)
321 		return;
322 
323 	cp = strdup(cwd);
324 	if (cp == NULL) {
325 		syslog(LOG_WARNING, "can't strdup");
326 		return;
327 	}
328 	sl_add(slist, cp);
329 
330 		/* First check for a display file */
331 	if (curclass.display != NULL && curclass.display[0] &&
332 	    (f = fopen(curclass.display, "r")) != NULL) {
333 		while (fgets(line, BUFSIZ, f)) {
334 			if ((cp = strchr(line, '\n')) != NULL)
335 				*cp = '\0';
336 			lreply(code, "%s", line);
337 		}
338 		fclose(f);
339 		lreply(code, "");
340 	}
341 
342 		/* Now see if there are any notify files */
343 	if (curclass.notify == NULL || curclass.notify[0] == '\0')
344 		return;
345 
346 	if (glob(curclass.notify, 0, NULL, &gl) != 0 || gl.gl_matchc == 0)
347 		return;
348 	time(&now);
349 	for (rlist = gl.gl_pathv; *rlist != NULL; rlist++) {
350 		if (stat(*rlist, &st) != 0)
351 			continue;
352 		if (!S_ISREG(st.st_mode))
353 			continue;
354 		then = st.st_mtime;
355 		lreply(code, "Please read the file %s", *rlist);
356 		t = localtime(&now);
357 		age = 365 * t->tm_year + t->tm_yday;
358 		t = localtime(&then);
359 		age -= 365 * t->tm_year + t->tm_yday;
360 		lreply(code, "  it was last modified on %.24s - %d day%s ago",
361 		    ctime(&then), age, age == 1 ? "" : "s");
362 	}
363 	globfree(&gl);
364 }
365 
366 /*
367  * Find s2 at the end of s1.  If found, return a string up and up (but
368  * not including) s2, otherwise returns NULL.
369  */
370 static char *
371 strend(s1, s2)
372 	const char *s1;
373 	char *s2;
374 {
375 	static	char buf[MAXPATHLEN + 1];
376 
377 	char	*start;
378 	size_t	l1, l2;
379 
380 	l1 = strlen(s1);
381 	l2 = strlen(s2);
382 
383 	if (l2 >= l1)
384 		return(NULL);
385 
386 	strncpy(buf, s1, MAXPATHLEN);
387 	start = buf + (l1 - l2);
388 
389 	if (strcmp(start, s2) == 0) {
390 		*start = '\0';
391 		return(buf);
392 	} else
393 		return(NULL);
394 }
395 
396 static int
397 filetypematch(types, mode)
398 	char	*types;
399 	int	mode;
400 {
401 	for ( ; types[0] != '\0'; types++)
402 		switch (*types) {
403 		  case 'd':
404 			if (S_ISDIR(mode))
405 				return(1);
406 			break;
407 		  case 'f':
408 			if (S_ISREG(mode))
409 				return(1);
410 			break;
411 		}
412 	return(0);
413 }
414 
415 /*
416  * Look for a conversion.  If we succeed, return a pointer to the
417  * command to execute for the conversion.
418  *
419  * The command is stored in a static array so there's no memory
420  * leak problems, and not too much to change in ftpd.c.  This
421  * routine doesn't need to be re-entrant unless we start using a
422  * multi-threaded ftpd, and that's not likely for a while...
423  */
424 char *
425 do_conversion(fname)
426 	const char *fname;
427 {
428 	static char	 cmd[LINE_MAX];
429 
430 	struct ftpconv	*cp;
431 	struct stat	 st;
432 	int		 o_errno;
433 	char		*base = NULL;
434 
435 	o_errno = errno;
436 	for (cp = curclass.conversions; cp != NULL; cp = cp->next) {
437 		if (cp->suffix == NULL) {
438 			syslog(LOG_WARNING,
439 			    "cp->suffix==NULL in conv list; SHOULDN'T HAPPEN!");
440 			continue;
441 		}
442 		if ((base = strend(fname, cp->suffix)) == NULL)
443 			continue;
444 		if (cp->types == NULL || cp->disable == NULL ||
445 		    cp->command == NULL)
446 			continue;
447 					/* Is it enabled? */
448 		if (strcmp(cp->disable, ".") != 0 &&
449 		    stat(cp->disable, &st) == 0)
450 				continue;
451 					/* Does the base exist? */
452 		if (stat(base, &st) < 0)
453 			continue;
454 					/* Is the file type ok */
455 		if (!filetypematch(cp->types, st.st_mode))
456 			continue;
457 		break;			/* "We have a winner!" */
458 	}
459 
460 	/* If we got through the list, no conversion */
461 	if (cp == NULL) {
462 		errno = o_errno;
463 		return(NULL);
464 	}
465 
466 	snprintf(cmd, LINE_MAX, cp->command, base);
467 	syslog(LOG_INFO, "get command is: %s", cmd);
468 	return(cmd);
469 }
470