1 /* $NetBSD: conf.c,v 1.3 1997/06/22 22:33:12 christos 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 REGENTS OR CONTRIBUTORS BE 30 * 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.3 1997/06/22 22:33:12 christos 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 char *types, *disable, *convcmd; 79 const char *infile; 80 int line; 81 unsigned int timeout; 82 struct ftpconv *conv, *cnext; 83 84 #define REASSIGN(X,Y) if (X) free(X); (X)=(Y) 85 #define NEXTWORD(W) while ((W = strsep(&buf, " \t")) != NULL && *W == '\0') 86 #define EMPTYSTR(W) (W == NULL || *W == '\0') 87 88 REASSIGN(curclass.classname, findclass); 89 for (conv = curclass.conversions; conv != NULL; conv=cnext) { 90 REASSIGN(conv->suffix, NULL); 91 REASSIGN(conv->types, NULL); 92 REASSIGN(conv->disable, NULL); 93 REASSIGN(conv->command, NULL); 94 cnext = conv->next; 95 free(conv); 96 } 97 curclass.conversions = NULL; 98 REASSIGN(curclass.display, NULL); 99 curclass.modify = 1; 100 curclass.maxtimeout = 7200; /* 2 hours */ 101 REASSIGN(curclass.notify, NULL); 102 curclass.timeout = 900; /* 15 minutes */ 103 curclass.umask = 027; 104 105 if (strcasecmp(findclass, "guest") == 0) { 106 curclass.umask = 0707; 107 curclass.modify = 0; 108 } 109 110 infile = _PATH_FTPDCONF; 111 if ((f = fopen(infile, "r")) == NULL) 112 return; 113 114 line = 0; 115 while ((buf = fgetln(f, &len)) != NULL) { 116 none = match = 0; 117 line++; 118 if (buf[len - 1] == '\n') 119 buf[--len] = '\0'; 120 if ((p = strchr(buf, '#')) != NULL) 121 *p = '\0'; 122 if (EMPTYSTR(buf)) 123 continue; 124 125 NEXTWORD(word); 126 NEXTWORD(class); 127 NEXTWORD(arg); 128 if (EMPTYSTR(word) || EMPTYSTR(class)) 129 continue; 130 if (strcasecmp(class, "none") == 0) 131 none = 1; 132 if (strcasecmp(class, findclass) != 0 && 133 !none && strcasecmp(class, "all") != 0) 134 continue; 135 136 if (strcasecmp(word, "conversion") == 0) { 137 if (EMPTYSTR(arg)) { 138 syslog(LOG_WARNING, 139 "%s line %d: %s requires a suffix", 140 infile, line, word); 141 continue; /* need a suffix */ 142 } 143 NEXTWORD(types); 144 NEXTWORD(disable); 145 convcmd = buf; 146 if (convcmd) 147 convcmd += strspn(convcmd, " \t"); 148 if (none || EMPTYSTR(types) || 149 EMPTYSTR(disable) || EMPTYSTR(convcmd)) { 150 types = NULL; 151 disable = NULL; 152 convcmd = NULL; 153 } else { 154 types = strdup(types); 155 disable = strdup(disable); 156 convcmd = strdup(convcmd); 157 } 158 for (conv = curclass.conversions; conv != NULL; 159 conv = conv->next) { 160 if (strcmp(conv->suffix, arg) == 0) 161 break; 162 } 163 if (conv == NULL) { 164 conv = (struct ftpconv *) 165 calloc(1, sizeof(struct ftpconv)); 166 if (conv == NULL) { 167 syslog(LOG_WARNING, "can't malloc"); 168 continue; 169 } 170 conv->next = curclass.conversions; 171 curclass.conversions = conv; 172 } 173 REASSIGN(conv->suffix, arg); 174 REASSIGN(conv->types, types); 175 REASSIGN(conv->disable, disable); 176 REASSIGN(conv->command, convcmd); 177 } else if (strcasecmp(word, "display") == 0) { 178 if (none || EMPTYSTR(arg)) 179 arg = NULL; 180 else 181 arg = strdup(arg); 182 REASSIGN(curclass.display, arg); 183 } else if (strcasecmp(word, "maxtimeout") == 0) { 184 if (none || EMPTYSTR(arg)) 185 continue; 186 timeout = (unsigned int)strtoul(arg, &endp, 10); 187 if (*endp != 0) { 188 syslog(LOG_WARNING, 189 "%s line %d: invalid maxtimeout %s", 190 infile, line, arg); 191 continue; 192 } 193 if (timeout < 30) { 194 syslog(LOG_WARNING, 195 "%s line %d: maxtimeout %d < 30 seconds", 196 infile, line, timeout); 197 continue; 198 } 199 if (timeout < curclass.timeout) { 200 syslog(LOG_WARNING, 201 "%s line %d: maxtimeout %d < timeout (%d)", 202 infile, line, timeout, curclass.timeout); 203 continue; 204 } 205 curclass.maxtimeout = timeout; 206 } else if (strcasecmp(word, "modify") == 0) { 207 if (none || 208 (!EMPTYSTR(arg) && strcasecmp(arg, "off") == 0)) 209 curclass.modify = 0; 210 else 211 curclass.modify = 1; 212 } else if (strcasecmp(word, "notify") == 0) { 213 if (none || EMPTYSTR(arg)) 214 arg = NULL; 215 else 216 arg = strdup(arg); 217 REASSIGN(curclass.notify, arg); 218 } else if (strcasecmp(word, "timeout") == 0) { 219 if (none || EMPTYSTR(arg)) 220 continue; 221 timeout = (unsigned int)strtoul(arg, &endp, 10); 222 if (*endp != 0) { 223 syslog(LOG_WARNING, 224 "%s line %d: invalid timeout %s", 225 infile, line, arg); 226 continue; 227 } 228 if (timeout < 30) { 229 syslog(LOG_WARNING, 230 "%s line %d: timeout %d < 30 seconds", 231 infile, line, timeout); 232 continue; 233 } 234 if (timeout > curclass.maxtimeout) { 235 syslog(LOG_WARNING, 236 "%s line %d: timeout %d > maxtimeout (%d)", 237 infile, line, timeout, curclass.maxtimeout); 238 continue; 239 } 240 curclass.timeout = timeout; 241 } else if (strcasecmp(word, "umask") == 0) { 242 mode_t umask; 243 244 if (none || EMPTYSTR(arg)) 245 continue; 246 umask = (mode_t)strtoul(arg, &endp, 8); 247 if (*endp != 0 || umask > 0777) { 248 syslog(LOG_WARNING, 249 "%s line %d: invalid umask %s", 250 infile, line, arg); 251 continue; 252 } 253 curclass.umask = umask; 254 } else { 255 syslog(LOG_WARNING, 256 "%s line %d: unknown directive '%s'", 257 infile, line, word); 258 continue; 259 } 260 } 261 #undef REASSIGN 262 #undef NEXTWORD 263 #undef EMPTYSTR 264 fclose(f); 265 } 266 267 /* 268 * Show file listed in curclass.display first time in, and list all the 269 * files named in curclass.notify in the current directory. Send back 270 * responses with the "reply" prefix. 271 */ 272 void 273 show_chdir_messages(code) 274 int code; 275 { 276 static StringList *slist = NULL; 277 278 struct stat st; 279 struct tm *t; 280 glob_t gl; 281 time_t now, then; 282 int age; 283 char cwd[MAXPATHLEN + 1]; 284 char line[BUFSIZ]; 285 char *cp, **rlist; 286 FILE *f; 287 288 /* Setup list for directory cache */ 289 if (slist == NULL) 290 slist = sl_init(); 291 292 /* Check if this directory has already been visited */ 293 if (getcwd(cwd, sizeof(cwd) - 1) == NULL) { 294 syslog(LOG_WARNING, "show_chdir_messages: can't malloc"); 295 return; 296 } 297 if (sl_find(slist, cwd) != NULL) 298 return; 299 300 if ((cp = strdup(cwd)) == NULL) { 301 syslog(LOG_WARNING, "show_chdir_messages: can't strdup"); 302 return; 303 } 304 sl_add(slist, cp); 305 306 /* First check for a display file */ 307 if (curclass.display != NULL && curclass.display[0] && 308 (f = fopen(curclass.display, "r")) != NULL) { 309 while (fgets(line, BUFSIZ, f)) { 310 if ((cp = strchr(line, '\n')) != NULL) 311 *cp = '\0'; 312 lreply(code, "%s", line); 313 } 314 fclose(f); 315 lreply(code, ""); 316 } 317 318 /* Now see if there are any notify files */ 319 if (curclass.notify == NULL || curclass.notify[0] == '\0') 320 return; 321 322 if (glob(curclass.notify, 0, NULL, &gl) != 0 || gl.gl_matchc == 0) 323 return; 324 time(&now); 325 for (rlist = gl.gl_pathv; *rlist != NULL; rlist++) { 326 if (stat(*rlist, &st) != 0) 327 continue; 328 if ((st.st_mode & S_IFMT) != S_IFREG) 329 continue; 330 then = st.st_mtime; 331 lreply(code, "Please read the file %s", *rlist); 332 t = localtime(&now); 333 age = 365 * t->tm_year + t->tm_yday; 334 t = localtime(&then); 335 age -= 365 * t->tm_year + t->tm_yday; 336 lreply(code, " it was last modified on %.24s - %d day%s ago", 337 ctime(&then), age, age == 1 ? "" : "s"); 338 } 339 globfree(&gl); 340 } 341 342 /* 343 * Find s2 at the end of s1. If found, return a string up and up (but 344 * not including) s2, otherwise returns NULL. 345 */ 346 static char * 347 strend(s1, s2) 348 const char *s1; 349 char *s2; 350 { 351 static char buf[MAXPATHLEN + 1]; 352 353 char *start; 354 size_t l1, l2; 355 356 l1 = strlen(s1); 357 l2 = strlen(s2); 358 359 if (l2 >= l1) 360 return(NULL); 361 362 strncpy(buf, s1, MAXPATHLEN); 363 start = buf + (l1 - l2); 364 365 if (strcmp(start, s2) == 0) { 366 *start = '\0'; 367 return(buf); 368 } else 369 return(NULL); 370 } 371 372 static int 373 filetypematch(types, mode) 374 char *types; 375 int mode; 376 { 377 for ( ; types[0] != '\0'; types++) 378 switch (*types) { 379 case 'd': 380 if (S_ISDIR(mode)) 381 return(1); 382 break; 383 case 'f': 384 if (S_ISREG(mode)) 385 return(1); 386 break; 387 } 388 return(0); 389 } 390 391 /* 392 * Look for a conversion. If we succeed, return a pointer to the 393 * command to execute for the conversion. 394 * 395 * The command is stored in a static array so there's no memory 396 * leak problems, and not too much to change in ftpd.c. This 397 * routine doesn't need to be re-entrant unless we start using a 398 * multi-threaded ftpd, and that's not likely for a while... 399 */ 400 char * 401 do_conversion(fname) 402 const char *fname; 403 { 404 static char cmd[LINE_MAX]; 405 406 struct ftpconv *cp; 407 struct stat st; 408 int o_errno; 409 char *base = NULL; 410 411 o_errno = errno; 412 for (cp = curclass.conversions; cp != NULL; cp = cp->next) { 413 if ((base = strend(fname, cp->suffix)) == NULL) 414 continue; 415 if (cp->suffix == NULL || cp->types == NULL || 416 cp->command == NULL) 417 continue; 418 /* Is it enabled? */ 419 if (strcmp(cp->disable, ".") != 0 && 420 stat(cp->disable, &st) == 0) 421 continue; 422 /* Does the base exist? */ 423 if (stat(base, &st) < 0) 424 continue; 425 /* Is the file type ok */ 426 if (!filetypematch(cp->types, st.st_mode)) 427 continue; 428 break; /* "We have a winner!" */ 429 } 430 431 /* If we got through the list, no conversion */ 432 if (cp == NULL) { 433 errno = o_errno; 434 return(NULL); 435 } 436 437 snprintf(cmd, LINE_MAX, cp->command, base); 438 syslog(LOG_INFO, "get command is: %s", cmd); 439 return(cmd); 440 } 441