1 /*
   2  * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 
   5 /****************************************************************************  
   6  
   7   Copyright (c) 1999,2000 WU-FTPD Development Group.  
   8   All rights reserved.
   9   
  10   Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
  11     The Regents of the University of California.
  12   Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
  13   Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
  14   Portions Copyright (c) 1989 Massachusetts Institute of Technology.
  15   Portions Copyright (c) 1998 Sendmail, Inc.
  16   Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P.  Allman.
  17   Portions Copyright (c) 1997 by Stan Barber.
  18   Portions Copyright (c) 1997 by Kent Landfield.
  19   Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
  20     Free Software Foundation, Inc.  
  21  
  22   Use and distribution of this software and its source code are governed 
  23   by the terms and conditions of the WU-FTPD Software License ("LICENSE").
  24  
  25   If you did not receive a copy of the license, it may be obtained online
  26   at http://www.wu-ftpd.org/license.html.
  27  
  28   $Id: extensions.c,v 1.48 2000/07/01 18:17:38 wuftpd Exp $
  29  
  30 ****************************************************************************/
  31 #include "config.h"
  32 
  33 #include <stdio.h>
  34 #include <errno.h>
  35 #include <string.h>
  36 
  37 #ifdef HAVE_SYS_SYSLOG_H
  38 #include <sys/syslog.h>
  39 #endif
  40 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
  41 #include <syslog.h>
  42 #endif
  43 
  44 #ifdef TIME_WITH_SYS_TIME
  45 #include <time.h>
  46 #include <sys/time.h>
  47 #else
  48 #ifdef HAVE_SYS_TIME_H
  49 #include <sys/time.h>
  50 #else
  51 #include <time.h>
  52 #endif
  53 #endif
  54 #include <pwd.h>
  55 #include <setjmp.h>
  56 #include <grp.h>
  57 
  58 #include <sys/types.h>
  59 #include <sys/stat.h>
  60 #include <sys/file.h>
  61 #include <sys/param.h>
  62 
  63 #ifdef HAVE_SYS_FS_UFS_QUOTA_H
  64 #include <sys/fs/ufs_quota.h>
  65 #elif defined(HAVE_UFS_UFS_QUOTA_H)
  66 #include <ufs/ufs/quota.h>
  67 #elif defined(HAVE_UFS_QUOTA_H)
  68 #include <ufs/quota.h>
  69 #elif defined(HAVE_SYS_MNTENT_H)
  70 #include <sys/mntent.h>
  71 #elif defined(HAVE_SYS_MNTTAB_H)
  72 #include <sys/mnttab.h>
  73 #endif
  74 
  75 #if defined(HAVE_STATVFS)
  76 #include <sys/statvfs.h>
  77 #elif defined(HAVE_SYS_VFS)
  78 #include <sys/vfs.h>
  79 #elif defined(HAVE_SYS_MOUNT)
  80 #include <sys/mount.h>
  81 #endif
  82 
  83 #include <arpa/ftp.h>
  84 
  85 #ifdef HAVE_PATHS_H
  86 #include <paths.h>
  87 #endif
  88 #include "pathnames.h"
  89 #include "extensions.h"
  90 #include "wu_fnmatch.h"
  91 #include "proto.h"
  92 
  93 #if defined(HAVE_FTW)
  94 #include <ftw.h>
  95 #else
  96 #include "support/ftw.h"
  97 #endif
  98 
  99 #ifdef QUOTA
 100 struct dqblk quota;
 101 char *time_quota(long curstate, long softlimit, long timelimit, char *timeleft);
 102 #endif
 103 
 104 #ifdef HAVE_REGEX_H
 105 #include <regex.h>
 106 #endif
 107 
 108 #if defined(HAVE_REGEX) && defined(SVR4) && ! (defined(NO_LIBGEN))
 109 #include <libgen.h>
 110 #endif
 111 
 112 extern int type, transflag, ftwflag, authenticated, autospout_free, data,
 113     pdata, anonymous, guest;
 114 extern char chroot_path[], guestpw[];
 115 
 116 #ifdef TRANSFER_COUNT
 117 extern off_t data_count_in;
 118 extern off_t data_count_out;
 119 #ifdef TRANSFER_LIMIT  
 120 extern off_t data_limit_raw_in;
 121 extern off_t data_limit_raw_out;
 122 extern off_t data_limit_raw_total;
 123 extern off_t data_limit_data_in;  
 124 extern off_t data_limit_data_out; 
 125 extern off_t data_limit_data_total;
 126 #ifdef RATIO /* 1998/08/06 K.Wakui */
 127 #define TRUNC_KB(n)   ((n)/1024+(((n)%1024)?1:0))
 128 extern time_t   login_time;
 129 extern time_t   limit_time;
 130 extern off_t    total_free_dl;
 131 extern int      upload_download_rate;
 132 #endif /* RATIO */
 133 #endif
 134 #endif
 135 
 136 #ifdef OTHER_PASSWD
 137 #include "getpwnam.h"
 138 extern char _path_passwd[];
 139 #endif
 140 
 141 #ifdef LOG_FAILED
 142 extern char the_user[];
 143 #endif
 144 
 145 extern char *globerr, remotehost[];
 146 #ifdef THROUGHPUT
 147 extern char remoteaddr[];
 148 #endif
 149 
 150 #ifndef HAVE_REGEX
 151 char *re_comp(const char *regex);
 152 int re_exec(const char *p1);
 153 #endif
 154 
 155 char shuttime[30], denytime[30], disctime[30];
 156 
 157 FILE *dout;
 158 
 159 time_t newer_time;
 160 
 161 int show_fullinfo;
 162 
 163 /* This always was a bug, because neither st_size nor time_t were required to
 164    be compatible with int, but needs fixing properly for C9X. */
 165 
 166 /* Some systems use one format, some another.  This takes care of the garbage */
 167 /* Do the system specific stuff only if we aren't autoconfed */
 168 #if !defined(L_FORMAT)
 169 #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
 170 #define L_FORMAT "qd"
 171 #else
 172 #define L_FORMAT "d"
 173 #endif
 174 #endif
 175 #if !defined(T_FORMAT)
 176 #define T_FORMAT "d"
 177 #endif
 178 #if !defined(PW_UID_FORMAT)
 179 #define PW_UID_FORMAT "d"
 180 #endif
 181 #if !defined(GR_GID_FORMAT)
 182 #define GR_GID_FORMAT "d"
 183 #endif
 184 
 185 int snprintf(char *str, size_t count, const char *fmt,...);
 186 
 187 #ifdef SITE_NEWER
 188 int check_newer(const char *path, const struct stat *st, int flag)
 189 {
 190     if (st->st_mtime > newer_time) {
 191         if (show_fullinfo != 0) {
 192             if (flag == FTW_F || flag == FTW_D) {
 193                 fprintf(dout, "%s %" L_FORMAT " %" T_FORMAT " %s\n",
 194                         flag == FTW_F ? "F" : "D",
 195                         st->st_size, st->st_mtime, path);
 196             }
 197         }
 198         else if (flag == FTW_F)
 199             fprintf(dout, "%s\n", path);
 200     }
 201 
 202     /* When an ABOR has been received (which sets ftwflag > 1) return a
 203      * non-zero value which causes ftw to stop tree traversal and return. 
 204      */
 205 
 206     return (ftwflag > 1 ? 1 : 0);
 207 }
 208 #endif
 209 
 210 #if defined(HAVE_STATVFS)
 211 long getSize(char *s)
 212 {
 213     struct statvfs buf;
 214 
 215     if (statvfs(s, &buf) != 0)
 216         return (0);
 217 
 218     return (buf.f_bavail * buf.f_frsize / 1024);
 219 }
 220 #elif defined(HAVE_SYS_VFS) || defined (HAVE_SYS_MOUNT)
 221 long getSize(char *s)
 222 {
 223     struct statfs buf;
 224 
 225     if (statfs(s, &buf) != 0)
 226         return (0);
 227 
 228     return (buf.f_bavail * buf.f_bsize / 1024);
 229 }
 230 #endif
 231 
 232 /*************************************************************************/
 233 /* FUNCTION  : msg_massage                                               */
 234 /* PURPOSE   : Scan a message line for magic cookies, replacing them as  */
 235 /*             needed.                                                   */
 236 /* ARGUMENTS : pointer input and output buffers                          */
 237 /*************************************************************************/
 238 
 239 void msg_massage(const char *inbuf, char *outbuf, size_t outlen)
 240 {
 241     const char *inptr = inbuf;
 242     char *outptr = outbuf;
 243 #ifdef QUOTA
 244     char timeleft[80];
 245 #endif
 246     char buffer[MAXPATHLEN];
 247     time_t curtime;
 248     int limit;
 249 #ifndef LOG_FAILED
 250     extern struct passwd *pw;
 251 #endif
 252     struct aclmember *entry;
 253 
 254 #ifdef VIRTUAL
 255     extern int virtual_mode;
 256     extern int virtual_ftpaccess;
 257     extern char virtual_email[];
 258 #endif
 259     extern char hostname[];
 260     extern char authuser[];
 261 
 262     (void) acl_getclass(buffer);
 263     limit = acl_getlimit(buffer, NULL);
 264 
 265     while ((outlen > 1) && (*inptr != '\0')) {
 266         if (*inptr != '%') {
 267             *outptr++ = *inptr;
 268             outlen -= 1;
 269         }
 270         else {
 271             entry = NULL;
 272             switch (*++inptr) {
 273             case 'E':
 274 #ifdef VIRTUAL
 275                 if (virtual_mode && !virtual_ftpaccess && virtual_email[0] != '\0')
 276                     snprintf(outptr, outlen, "%s", virtual_email);
 277                 else
 278 #endif
 279                 if ((getaclentry("email", &entry)) && ARG0)
 280                     snprintf(outptr, outlen, "%s", ARG0);
 281                 else
 282                     *outptr = '\0';
 283                 break;
 284 
 285             case 'N':
 286                 snprintf(outptr, outlen, "%d", acl_countusers(buffer));
 287                 break;
 288 
 289             case 'M':
 290                 if (limit == -1)
 291                     strncpy(outptr, "unlimited", outlen);
 292                 else
 293                     snprintf(outptr, outlen, "%d", limit);
 294                 break;
 295 
 296             case 'T':
 297                 (void) time(&curtime);
 298                 strncpy(outptr, ctime(&curtime), outlen);
 299                 if (outlen > 24)
 300                     *(outptr + 24) = '\0';
 301                 break;
 302 
 303             case 'F':
 304 #if defined(HAVE_STATVFS) || defined(HAVE_SYS_VFS) || defined(HAVE_SYS_MOUNT)
 305                 snprintf(outptr, outlen, "%lu", (long) getSize("."));
 306 #else
 307                 *outptr = '\0';
 308 #endif
 309                 break;
 310 
 311             case 'C':
 312 #ifdef HAVE_GETCWD
 313                 (void) getcwd(outptr, outlen);
 314 #else
 315 #error  wu-ftpd on this platform has security deficiencies!!!
 316                 (void) getwd(outptr);
 317 #endif
 318                 break;
 319 
 320             case 'R':
 321                 strncpy(outptr, remotehost, outlen);
 322                 break;
 323 
 324             case 'L':
 325                 strncpy(outptr, hostname, outlen);
 326                 break;
 327 
 328             case 'U':
 329                 if (xferdone && anonymous)
 330                     strncpy(outptr, guestpw, outlen);
 331                 else
 332 #ifdef LOG_FAILED
 333                     strncpy(outptr, the_user, outlen);
 334 #else /* LOG_FAILED */
 335                     strncpy(outptr,
 336                             (pw == NULL) ? "[unknown]" : pw->pw_name, outlen);
 337 #endif /* LOG_FAILED */
 338                 break;
 339 
 340             case 's':
 341                 strncpy(outptr, shuttime, outlen);
 342                 if (outlen > 24)
 343                     *(outptr + 24) = '\0';
 344                 break;
 345 
 346             case 'd':
 347                 strncpy(outptr, disctime, outlen);
 348                 if (outlen > 24)
 349                     *(outptr + 24) = '\0';
 350                 break;
 351 
 352             case 'r':
 353                 strncpy(outptr, denytime, outlen);
 354                 if (outlen > 24)
 355                     *(outptr + 24) = '\0';
 356                 break;
 357 
 358 /* KH : cookie %u for RFC931 name */
 359             case 'u':
 360                 if (authenticated)
 361                     strncpy(outptr, authuser, outlen);
 362                 else {
 363                     if (xferdone)
 364                         snprintf(outptr, outlen, "%c", '*');
 365                     else
 366                         strncpy(outptr, "[unknown]", outlen);
 367                 }
 368                 break;
 369 
 370 #ifdef QUOTA
 371             case 'B':
 372 #ifdef QUOTA_BLOCKS             /* 1024-blocks instead of 512-blocks */
 373                 snprintf(outptr, outlen, "%ld", quota.dqb_bhardlimit % 2 ?
 374                          (long) (quota.dqb_bhardlimit / 2 + 1) : (long) (quota.dqb_bhardlimit / 2));
 375 #else
 376                 snprintf(outptr, outlen, "%ld", (long) quota.dqb_bhardlimit);
 377 #endif
 378                 break;
 379 
 380             case 'b':
 381 #ifdef QUOTA_BLOCKS             /* 1024-blocks instead of 512-blocks */
 382                 snprintf(outptr, outlen, "%ld", quota.dqb_bsoftlimit % 2 ?
 383                          (long) (quota.dqb_bsoftlimit / 2 + 1) : (long) (quota.dqb_bsoftlimit / 2));
 384 #else
 385                 snprintf(outptr, outlen, "%ld", (long) quota.dqb_bsoftlimit);
 386 #endif
 387                 break;
 388 
 389             case 'Q':
 390 #ifdef QUOTA_BLOCKS             /* 1024-blocks instead of 512-blocks */
 391                 snprintf(outptr, outlen, "%ld", quota.dqb_curblocks % 2 ?
 392                          (long) (quota.dqb_curblocks / 2 + 1) : (long) (quota.dqb_curblocks / 2));
 393 #else
 394                 snprintf(outptr, outlen, "%ld", quota.dqb_curblocks);
 395 #endif
 396                 break;
 397 
 398             case 'I':
 399 #if defined(QUOTA_INODE)
 400                 snprintf(outptr, outlen, "%d", quota.dqb_ihardlimit);
 401 #else
 402                 snprintf(outptr, outlen, "%ld", (long) quota.dqb_fhardlimit);
 403 #endif
 404                 break;
 405 
 406             case 'i':
 407 #if defined(QUOTA_INODE)
 408                 snprintf(outptr, outlen, "%d", quota.dqb_isoftlimit);
 409 #else
 410                 snprintf(outptr, outlen, "%ld", (long) quota.dqb_fsoftlimit);
 411 #endif
 412                 break;
 413 
 414             case 'q':
 415 #if defined(QUOTA_INODE)
 416                 snprintf(outptr, outlen, "%d", quota.dqb_curinodes);
 417 #else
 418                 snprintf(outptr, outlen, "%ld", (long) quota.dqb_curfiles);
 419 #endif
 420                 break;
 421 
 422             case 'H':
 423                 time_quota(quota.dqb_curblocks, quota.dqb_bsoftlimit,
 424 #if defined(QUOTA_INODE)
 425                            quota.dqb_btime, timeleft);
 426 #else
 427                            quota.dqb_btimelimit, timeleft);
 428 #endif
 429                 strncpy(outptr, timeleft, outlen);
 430                 break;
 431 
 432             case 'h':
 433 #if defined(QUOTA_INODE)
 434                 time_quota(quota.dqb_curinodes, quota.dqb_isoftlimit,
 435                            quota.dqb_itime, timeleft);
 436 #else
 437                 time_quota(quota.dqb_curfiles, quota.dqb_fsoftlimit,
 438                            quota.dqb_ftimelimit, timeleft);
 439 #endif
 440                 strncpy(outptr, timeleft, outlen);
 441                 break;
 442 #endif /* QUOTA */
 443 
 444             case '%':
 445                 *outptr++ = '%';
 446                 outlen -= 1;
 447                 *outptr = '\0';
 448                 break;
 449 
 450 #ifdef TRANSFER_COUNT
 451 #ifdef TRANSFER_LIMIT
 452 #ifdef RATIO
 453             case 'x':
 454                 switch (*++inptr) {
 455                 case 'u':       /* upload bytes */
 456                     sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_count_in) );
 457                     break;
 458                 case 'd':       /* download bytes */
 459                     sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_count_out) );
 460                     break;
 461                 case 'R':       /* rate 1:n */
 462                     if( upload_download_rate > 0 ) {
 463                         sprintf(outptr,"%d", upload_download_rate );
 464                     }
 465                     else {
 466                         strcpy(outptr,"free");
 467                     }
 468                     break;
 469                 case 'c':       /* credit bytes */
 470                     if( upload_download_rate > 0 ) {
 471                         off_t credit=( data_count_in * upload_download_rate) - (data_count_out - total_free_dl);
 472                         sprintf(outptr,"%" L_FORMAT, TRUNC_KB(credit) );
 473                     }
 474                     else {
 475                         strcpy(outptr,"unlimited");
 476                     }
 477                     break;
 478                 case 'T':       /* time limit (minutes) */
 479                     if( limit_time > 0 ) {
 480                         sprintf(outptr,"%d", limit_time );
 481                     }
 482                     else {
 483                         strcpy(outptr,"unlimited");
 484                     }
 485                     break;
 486                 case 'E':       /* elapsed time from loggedin (minutes) */
 487                     sprintf(outptr,"%d", (time(NULL)-login_time)/60 );
 488                     break;
 489                 case 'L':       /* times left until force logout (minutes) */
 490                     if( limit_time > 0 ) {
 491                         sprintf(outptr,"%d", limit_time-(time(NULL)-login_time)/60 );
 492                     }
 493                     else {
 494                         strcpy(outptr,"unlimited");
 495                     }
 496                     break;
 497                 case 'U':       /* upload limit */
 498                     if( data_limit_raw_in > 0 ) {
 499                         sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_in));
 500                     }
 501                     else if( data_limit_data_in > 0 ) {
 502                         sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_in));
 503                     }
 504                     else if( data_limit_raw_total > 0 ) {
 505                         sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_total));
 506                     }
 507                     else if( data_limit_data_total > 0 ) {
 508                         sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_total));
 509                     }
 510                     else {
 511                         strcpy(outptr, "unlimited");
 512                     }
 513                     break;
 514                 case 'D':       /* download limit */
 515                     if( data_limit_raw_out > 0 ) {
 516                         sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_out));
 517                     }
 518                     else if( data_limit_data_out > 0 ) {
 519                         sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_out));
 520                     }
 521                     else if( data_limit_raw_total > 0 ) {
 522                         sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_raw_total));
 523                     }
 524                     else if( data_limit_data_total > 0 ) {
 525                         sprintf(outptr,"%" L_FORMAT, TRUNC_KB(data_limit_data_total));
 526                     }
 527                     else {
 528                         strcpy(outptr, "unlimited");
 529                     }
 530                     break;
 531                 default:
 532                     strcpy(outptr,"%??");
 533                     break;
 534                 }
 535                 break;
 536 #endif /* RATIO */
 537 #endif
 538 #endif
 539                 /* File transfer logging (xferlog) */
 540                 case 'X':
 541                     if (xferdone) {  /* only if a transfer has just occurred */
 542                         switch (*++inptr) {
 543                         case 't':
 544                             snprintf(outptr, outlen, "%d", xfervalues.transfer_time);
 545                             break;
 546                         case 's':
 547                             snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.filesize);
 548                             break;
 549                         case 'n':
 550                             snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.transfer_bytes);
 551                             break;
 552                         case 'P': /* absolute pathname */
 553                             /* FALLTHROUGH */
 554                         case 'p': /* chroot-relative pathname */
 555                         {
 556                             char namebuf[MAXPATHLEN];
 557                             int loop;
 558 
 559                             if (*inptr == 'P')
 560                                 wu_realpath(xfervalues.filename, namebuf, chroot_path);
 561                             else
 562                                 fb_realpath(xfervalues.filename, namebuf);
 563                             for (loop = 0; namebuf[loop]; loop++) {
 564                                 if (isspace(namebuf[loop]) || iscntrl(namebuf[loop]))
 565                                     namebuf[loop] = '_';
 566                             }
 567                             snprintf(outptr, outlen, "%s", namebuf);
 568                             break;
 569                         }
 570                         case 'y':
 571                             snprintf(outptr, outlen, "%c", xfervalues.transfer_type);
 572                             break;
 573                         case 'f':
 574                             snprintf(outptr, outlen, "%s", xfervalues.special_action);
 575                             break;
 576                         case 'd':
 577                             snprintf(outptr, outlen, "%c", xfervalues.transfer_direction);
 578                             break;
 579                         case 'm':
 580                             snprintf(outptr, outlen, "%c", xfervalues.access_mode);
 581                             break;
 582                         case 'a':
 583                             snprintf(outptr, outlen, "%d", xfervalues.auth);
 584                             break;
 585                         case 'r':
 586                             snprintf(outptr, outlen, "%" L_FORMAT, xfervalues.restart_offset);
 587                             break;
 588                         case 'c':
 589                             snprintf(outptr, outlen, "%c", xfervalues.completion);
 590                             break;
 591                         default:
 592                             snprintf(outptr, outlen, "%%X%c", *inptr);
 593                             break;
 594                         }
 595                     }
 596                     else
 597                         snprintf(outptr, outlen, "%%%c", *inptr);
 598                     break;
 599 
 600             default:
 601                 *outptr++ = '%';
 602                 outlen -= 1;
 603                 if (outlen > 1) {
 604                     *outptr++ = *inptr;
 605                     outlen -= 1;
 606                 }
 607                 *outptr = '\0';
 608                 break;
 609             }
 610             outptr[outlen - 1] = '\0';
 611             while (*outptr) {
 612                 outptr++;
 613                 outlen -= 1;
 614             }
 615         }
 616         inptr++;
 617     }
 618     if (outlen > 0)
 619         *outptr = '\0';
 620 }
 621 
 622 /*************************************************************************/
 623 /* FUNCTION  : cwd_beenhere                                              */
 624 /* PURPOSE   : Return 1 if the user has already visited this directory   */
 625 /*             via C_WD.                                                 */
 626 /* ARGUMENTS : a power-of-two directory function code (README, MESSAGE)  */
 627 /*************************************************************************/
 628 
 629 int cwd_beenhere(int dircode)
 630 {
 631     struct dirlist {
 632         struct dirlist *next;
 633         int dircode;
 634         char dirname[1];
 635     };
 636 
 637     static struct dirlist *head = NULL;
 638     struct dirlist *curptr;
 639     char cwd[MAXPATHLEN];
 640 
 641     (void) fb_realpath(".", cwd);
 642 
 643     for (curptr = head; curptr != NULL; curptr = curptr->next)
 644         if (strcmp(curptr->dirname, cwd) == 0) {
 645             if (!(curptr->dircode & dircode)) {
 646                 curptr->dircode |= dircode;
 647                 return (0);
 648             }
 649             return (1);
 650         }
 651     curptr = (struct dirlist *) malloc(strlen(cwd) + 1 + sizeof(struct dirlist));
 652 
 653     if (curptr != NULL) {
 654         curptr->next = head;
 655         head = curptr;
 656         curptr->dircode = dircode;
 657         strcpy(curptr->dirname, cwd);
 658     }
 659     return (0);
 660 }
 661 
 662 /*************************************************************************/
 663 /* FUNCTION  : show_banner                                               */
 664 /* PURPOSE   : Display a banner on the user's terminal before login      */
 665 /* ARGUMENTS : reply code to use                                         */
 666 /*************************************************************************/
 667 
 668 void show_banner(int msgcode)
 669 {
 670     char *crptr, linebuf[1024], outbuf[1024];
 671     struct aclmember *entry = NULL;
 672     FILE *infile;
 673 
 674 #ifdef VIRTUAL
 675     extern int virtual_mode;
 676     extern int virtual_ftpaccess;
 677     extern char virtual_banner[];
 678 
 679     if (virtual_mode && !virtual_ftpaccess) {
 680         infile = fopen(virtual_banner, "r");
 681         if (infile) {
 682             while (fgets(linebuf, sizeof(linebuf), infile) != NULL) {
 683                 if ((crptr = strchr(linebuf, '\n')) != NULL)
 684                     *crptr = '\0';
 685                 msg_massage(linebuf, outbuf, sizeof(outbuf));
 686                 lreply(msgcode, "%s", outbuf);
 687             }
 688             fclose(infile);
 689 #ifndef NO_SUCKING_NEWLINES
 690             lreply(msgcode, "");
 691 #endif
 692         }
 693     }
 694     else {
 695 #endif
 696         /* banner <path> */
 697         while (getaclentry("banner", &entry)) {
 698             if (!ARG0)
 699                 continue;
 700             infile = fopen(ARG0, "r");
 701             if (infile) {
 702                 while (fgets(linebuf, sizeof(linebuf), infile) != NULL) {
 703                     if ((crptr = strchr(linebuf, '\n')) != NULL)
 704                         *crptr = '\0';
 705                     msg_massage(linebuf, outbuf, sizeof(outbuf));
 706                     lreply(msgcode, "%s", outbuf);
 707                 }
 708                 fclose(infile);
 709 #ifndef NO_SUCKING_NEWLINES
 710                 lreply(msgcode, "");
 711 #endif
 712             }
 713         }
 714 #ifdef VIRTUAL
 715     }
 716 #endif
 717 }
 718 /*************************************************************************/
 719 /* FUNCTION  : show_message                                              */
 720 /* PURPOSE   : Display a message on the user's terminal if the current   */
 721 /*             conditions are right                                      */
 722 /* ARGUMENTS : reply code to use, LOG_IN|CMD                             */
 723 /*************************************************************************/
 724 
 725 void show_message(int msgcode, int mode)
 726 {
 727     char *crptr, linebuf[1024], outbuf[1024], class[MAXPATHLEN], cwd[MAXPATHLEN];
 728     int show, which;
 729     struct aclmember *entry = NULL;
 730     FILE *infile;
 731 
 732     if (mode == C_WD && cwd_beenhere(1) != 0)
 733         return;
 734 
 735 #ifdef HAVE_GETCWD
 736     (void) getcwd(cwd, MAXPATHLEN - 1);
 737 #else
 738     (void) getwd(cwd);
 739 #endif
 740     (void) acl_getclass(class);
 741 
 742     /* message <path> [<when> [<class>]] */
 743     while (getaclentry("message", &entry)) {
 744         if (!ARG0)
 745             continue;
 746         show = 0;
 747 
 748         if (mode == LOG_IN && (!ARG1 || !strcasecmp(ARG1, "login")))
 749             if (!ARG2)
 750                 show++;
 751             else {
 752                 for (which = 2; (which < MAXARGS) && ARG[which]; which++)
 753                     if (strcasecmp(class, ARG[which]) == 0)
 754                         show++;
 755             }
 756         if (mode == C_WD && ARG1 && !strncasecmp(ARG1, "cwd=", 4) &&
 757             (!strcmp((ARG1) + 4, cwd) || *(ARG1 + 4) == '*' ||
 758              !wu_fnmatch((ARG1) + 4, cwd, FNM_PATHNAME)))
 759             if (!ARG2)
 760                 show++;
 761             else {
 762                 for (which = 2; (which < MAXARGS) && ARG[which]; which++)
 763                     if (strcasecmp(class, ARG[which]) == 0)
 764                         show++;
 765             }
 766         if (show && (int) strlen(ARG0) > 0) {
 767             infile = fopen(ARG0, "r");
 768             if (infile) {
 769                 while (fgets(linebuf, sizeof(linebuf), infile) != NULL) {
 770                     if ((crptr = strchr(linebuf, '\n')) != NULL)
 771                         *crptr = '\0';
 772                     msg_massage(linebuf, outbuf, sizeof(outbuf));
 773                     lreply(msgcode, "%s", outbuf);
 774                 }
 775                 fclose(infile);
 776 #ifndef NO_SUCKING_NEWLINES
 777                 lreply(msgcode, "");
 778 #endif
 779             }
 780         }
 781     }
 782 }
 783 
 784 /*************************************************************************/
 785 /* FUNCTION  : show_readme                                               */
 786 /* PURPOSE   : Display a message about a README file to the user if the  */
 787 /*             current conditions are right                              */
 788 /* ARGUMENTS : pointer to ACL buffer, reply code, LOG_IN|C_WD            */
 789 /*************************************************************************/
 790 
 791 void show_readme(int code, int mode)
 792 {
 793     char **filelist, **sfilelist, class[MAXPATHLEN], cwd[MAXPATHLEN];
 794     int show, which, days;
 795     time_t clock;
 796 
 797     struct stat buf;
 798     struct tm *tp;
 799     struct aclmember *entry = NULL;
 800 
 801     if (cwd_beenhere(2) != 0)
 802         return;
 803 
 804 #ifdef HAVE_GETCWD
 805     (void) getcwd(cwd, MAXPATHLEN - 1);
 806 #else
 807     (void) getwd(cwd);
 808 #endif
 809     (void) acl_getclass(class);
 810 
 811     /* readme  <path> {<when>} */
 812     while (getaclentry("readme", &entry)) {
 813         if (!ARG0)
 814             continue;
 815         show = 0;
 816 
 817         if (mode == LOG_IN && (!ARG1 || !strcasecmp(ARG1, "login")))
 818             if (!ARG2)
 819                 show++;
 820             else {
 821                 for (which = 2; (which < MAXARGS) && ARG[which]; which++)
 822                     if (strcasecmp(class, ARG[which]) == 0)
 823                         show++;
 824             }
 825         if (mode == C_WD && ARG1 && !strncasecmp(ARG1, "cwd=", 4)
 826             && (!strcmp((ARG1) + 4, cwd) || *(ARG1 + 4) == '*' ||
 827                 !wu_fnmatch((ARG1) + 4, cwd, FNM_PATHNAME)))
 828             if (!ARG2)
 829                 show++;
 830             else {
 831                 for (which = 2; (which < MAXARGS) && ARG[which]; which++)
 832                     if (strcasecmp(class, ARG[which]) == 0)
 833                         show++;
 834             }
 835         if (show) {
 836             globerr = NULL;
 837             filelist = ftpglob(ARG0, B_TRUE);
 838             sfilelist = filelist;       /* save to free later */
 839             if (!globerr) {
 840                 while (filelist && *filelist) {
 841                     errno = 0;
 842                     if (!stat(*filelist, &buf) &&
 843                         (buf.st_mode & S_IFMT) == S_IFREG) {
 844                         lreply(code, "Please read the file %s", *filelist);
 845                         (void) time(&clock);
 846                         tp = localtime(&clock);
 847                         days = 365 * tp->tm_year + tp->tm_yday;
 848                         tp = localtime((time_t *) & buf.st_mtime);
 849                         days -= 365 * tp->tm_year + tp->tm_yday;
 850 /*
 851    if (days == 0) {
 852    lreply(code, "  it was last modified on %.24s - Today",
 853    ctime((time_t *)&buf.st_mtime));
 854    } else {
 855  */
 856                         lreply(code,
 857                            "  it was last modified on %.24s - %d day%s ago",
 858                                ctime((time_t *) & buf.st_mtime), days, days == 1 ? "" : "s");
 859 /*
 860    }
 861  */
 862                     }
 863                     filelist++;
 864                 }
 865             }
 866             if (sfilelist) {
 867                 blkfree(sfilelist);
 868                 free((char *) sfilelist);
 869             }
 870         }
 871     }
 872 }
 873 
 874 /*************************************************************************/
 875 /* FUNCTION  : deny_badxfertype                                          */
 876 /* PURPOSE   : If user is in ASCII transfer mode and tries to retrieve a */
 877 /*             binary file, abort transfer and display appropriate error */
 878 /* ARGUMENTS : message code to use for denial, path of file to check for */
 879 /*             binary contents or NULL to assume binary file             */
 880 /*************************************************************************/
 881 
 882 int deny_badasciixfer(int msgcode, char *filepath)
 883 {
 884 
 885     if (type == TYPE_A && !*filepath) {
 886         reply(msgcode, "This is a BINARY file, using ASCII mode to transfer will corrupt it.");
 887         return (1);
 888     }
 889     /* The hooks are here to prevent transfers of actual binary files, not
 890      * just TAR or COMPRESS mode files... */
 891     return (0);
 892 }
 893 
 894 /*************************************************************************/
 895 /* FUNCTION  : is_shutdown                                               */
 896 /* PURPOSE   : Check to see if the server is shutting down, if it is     */
 897 /*             arrange for the shutdown message to be sent in the next   */
 898 /*             reply to the user                                         */
 899 /* ARGUMENTS : whether to arrange for a shutdown message to be sent, new */
 900 /*             or existing connection                                    */
 901 /* RETURNS   : 1 if shutting down, 0 if not                              */
 902 /*************************************************************************/
 903 
 904 int is_shutdown(int quiet, int new)
 905 {
 906     static struct tm tmbuf;
 907     static struct stat s_last;
 908     static time_t last = 0, shut, deny, disc;
 909     static int valid;
 910     static char text[2048];
 911     struct stat s_cur;
 912 
 913     extern char *autospout, Shutdown[];
 914 
 915     FILE *fp;
 916 
 917     int deny_off, disc_off;
 918 
 919     time_t curtime = time(NULL);
 920 
 921     char buf[1024], linebuf[1024];
 922 
 923     if (Shutdown[0] == '\0' || stat(Shutdown, &s_cur))
 924         return (0);
 925 
 926     if (s_last.st_mtime != s_cur.st_mtime) {
 927         valid = 0;
 928 
 929         fp = fopen(Shutdown, "r");
 930         if (fp == NULL)
 931             return (0);
 932         s_last = s_cur;
 933         fgets(buf, sizeof(buf), fp);
 934         if (sscanf(buf, "%d %d %d %d %d %ld %ld", &tmbuf.tm_year, &tmbuf.tm_mon,
 935         &tmbuf.tm_mday, &tmbuf.tm_hour, &tmbuf.tm_min, &deny, &disc) != 7) {
 936             (void) fclose(fp);
 937             return (0);
 938         }
 939         valid = 1;
 940         deny_off = 3600 * (deny / 100) + 60 * (deny % 100);
 941         disc_off = 3600 * (disc / 100) + 60 * (disc % 100);
 942 
 943         tmbuf.tm_year -= 1900;
 944         tmbuf.tm_isdst = -1;
 945         shut = mktime(&tmbuf);
 946         strcpy(shuttime, ctime(&shut));
 947 
 948         disc = shut - disc_off;
 949         strcpy(disctime, ctime(&disc));
 950 
 951         deny = shut - deny_off;
 952         strcpy(denytime, ctime(&deny));
 953 
 954         text[0] = '\0';
 955 
 956         while (fgets(buf, sizeof(buf), fp) != NULL) {
 957             msg_massage(buf, linebuf, sizeof(linebuf));
 958             if ((strlen(text) + strlen(linebuf)) < sizeof(text))
 959                 strcat(text, linebuf);
 960         }
 961 
 962         (void) fclose(fp);
 963     }
 964     if (!valid)
 965         return (0);
 966 
 967     /* if last == 0, then is_shutdown() only called with quiet == 1 so far */
 968     if (last == 0 && !quiet) {
 969         autospout = text;       /* warn them for the first time */
 970         autospout_free = 0;
 971         last = curtime;
 972     }
 973     /* if a new connection and past deny time, tell caller to drop 'em */
 974     if (new && curtime > deny)
 975         return (1);
 976 
 977     /* if past disconnect time, tell caller to drop 'em */
 978     if (curtime > disc)
 979         return (1);
 980 
 981     /* if less than 60 seconds to disconnection, warn 'em continuously */
 982     if (curtime > (disc - 60) && !quiet) {
 983         autospout = text;
 984         autospout_free = 0;
 985         last = curtime;
 986     }
 987     /* if less than 15 minutes to disconnection, warn 'em every 5 mins */
 988     if (curtime > (disc - 60 * 15)) {
 989         if ((curtime - last) > (60 * 5) && !quiet) {
 990             autospout = text;
 991             autospout_free = 0;
 992             last = curtime;
 993         }
 994     }
 995     /* if less than 24 hours to disconnection, warn 'em every 30 mins */
 996     if (curtime < (disc - 24 * 60 * 60) && !quiet) {
 997         if ((curtime - last) > (60 * 30)) {
 998             autospout = text;
 999             autospout_free = 0;
1000             last = curtime;
1001         }
1002     }
1003     /* if more than 24 hours to disconnection, warn 'em every 60 mins */
1004     if (curtime > (disc - 24 * 60 * 60) && !quiet) {
1005         if ((curtime - last) >= (24 * 60 * 60)) {
1006             autospout = text;
1007             autospout_free = 0;
1008             last = curtime;
1009         }
1010     }
1011     return (0);
1012 }
1013 
1014 #ifdef SITE_NEWER
1015 void newer(char *date, char *path, int showlots)
1016 {
1017     struct tm tm;
1018 
1019     if (sscanf(date, "%04d%02d%02d%02d%02d%02d",
1020                &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
1021                &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
1022 
1023         tm.tm_year -= 1900;
1024         tm.tm_mon--;
1025         tm.tm_isdst = -1;
1026         newer_time = mktime(&tm);
1027         dout = dataconn("file list", (off_t) - 1, "w");
1028 
1029         if (dout != NULL) {
1030             /* As ftw allocates storage it needs a chance to cleanup, setting
1031              * ftwflag prevents myoob from calling longjmp, incrementing
1032              * ftwflag instead which causes check_newer to return non-zero
1033              * which makes ftw return. */
1034             ftwflag = 1;
1035             transflag++;
1036             show_fullinfo = showlots;
1037 #if defined(HAVE_FTW)
1038             ftw(path, check_newer, -1);
1039 #else
1040             treewalk(path, check_newer, -1, NULL);
1041 #endif
1042 
1043             /* don't send a reply if myoob has already replied */
1044             if (ftwflag == 1) {
1045                 if (ferror(dout) != 0)
1046                     perror_reply(550, "Data connection");
1047                 else
1048                     reply(226, "Transfer complete.");
1049             }
1050 
1051             (void) fclose(dout);
1052             data = -1;
1053             pdata = -1;
1054             transflag = 0;
1055             ftwflag = 0;
1056         }
1057     }
1058     else
1059         reply(501, "Bad DATE format");
1060 }
1061 #endif
1062 
1063 int type_match(char *typelist)
1064 {
1065     char *start, *p;
1066     int len;
1067 
1068     if (typelist == NULL)
1069         return (0);
1070 
1071     for (p = start = typelist; *start != '\0'; start = p) {
1072         while (*p != '\0' && *p != ',')
1073             p++;
1074         len = p - start;
1075         if (*p != '\0')
1076             p++;
1077         if (len == 9 && anonymous && strncasecmp(start, "anonymous", 9) == 0)
1078             return (1);
1079         if (len == 5 && guest && strncasecmp(start, "guest", 5) == 0)
1080             return (1);
1081         if (len == 4 && !guest && !anonymous &&
1082             strncasecmp(start, "real", 4) == 0)
1083             return (1);
1084 
1085         if (len > 6 && strncasecmp(start, "class=", 6) == 0) {
1086             char class[1024];
1087 
1088             if ((acl_getclass(class) == 1) && (strlen(class) == len - 6) &&
1089                 (strncasecmp(start + 6, class, len - 6) == 0))
1090                 return (1);
1091         }
1092     }
1093     return (0);
1094 }
1095 
1096 int path_compare(char *p1, char *p2)
1097 {
1098     if ((strcmp(p1, "*") == 0) || (wu_fnmatch(p1, p2, FNM_PATHNAME) == 0))      /* 0 means they matched */
1099         return (strlen(p1));
1100     else
1101         return (-2);
1102 }
1103 
1104 void expand_id(void)
1105 {
1106     char class[1024];
1107     struct aclmember *entry = NULL;
1108     (void) acl_getclass(class);
1109     while (getaclentry("upload", &entry)) {
1110         char *q;
1111         int i = 0;
1112         int options = 1;
1113         int classfound = 0;
1114         int classmatched = 0;
1115         while (options
1116                && (i < MAXARGS)
1117                && ((q = entry->arg[i]) != (char *) NULL)
1118                && (q[0] != '\0')) {
1119             if (strcasecmp(q, "absolute") == 0)
1120                 i++;
1121             else if (strcasecmp(q, "relative") == 0)
1122                 i++;
1123             else if (strncasecmp(q, "class=", 6) == 0) {
1124                 i++;
1125                 classfound = 1;
1126                 if (strcasecmp(q + 6, class) == 0)
1127                     classmatched = 1;
1128             }
1129             else if (strcmp(q, "-") == 0) {
1130                 i++;
1131                 options = 0;
1132             }
1133             else
1134                 options = 0;
1135         }
1136         if (!classfound || classmatched) {
1137             char buf[BUFSIZ];
1138             /*
1139                *  UID
1140              */
1141             if (((i + 3) < MAXARGS)
1142                 && ((q = entry->arg[i + 3]) != (char *) NULL)
1143                 && (q[0] != '\0')
1144                 && (strcmp(q, "*") != 0)) {
1145                 if (q[0] == '%')
1146                     sprintf(buf, "%s", q + 1);
1147                 else {
1148                     struct passwd *pwent = getpwnam(q);
1149                     if (pwent)
1150                         sprintf(buf, "%" PW_UID_FORMAT, pwent->pw_uid);
1151                     else
1152                         sprintf(buf, "%d", 0);
1153                 }
1154                 entry->arg[i + 3] = (char *) malloc(strlen(buf) + 1);
1155                 if (entry->arg[i + 3] == NULL) {
1156                     syslog(LOG_ERR, "calloc error in expand_id");
1157                     dologout(1);
1158                 }
1159                 strcpy(entry->arg[i + 3], buf);
1160             }
1161             /*
1162                *  GID
1163              */
1164             if (((i + 4) < MAXARGS)
1165                 && ((q = entry->arg[i + 4]) != (char *) NULL)
1166                 && (q[0] != '\0')
1167                 && (strcmp(q, "*") != 0)) {
1168                 if (q[0] == '%')
1169                     sprintf(buf, "%s", q + 1);
1170                 else {
1171                     struct group *grent = getgrnam(q);
1172                     if (grent)
1173                         sprintf(buf, "%" GR_GID_FORMAT, grent->gr_gid);
1174                     else
1175                         sprintf(buf, "%d", 0);
1176                     endgrent();
1177                 }
1178                 entry->arg[i + 4] = (char *) malloc(strlen(buf) + 1);
1179                 if (entry->arg[i + 4] == NULL) {
1180                     syslog(LOG_ERR, "calloc error in expand_id");
1181                     dologout(1);
1182                 }
1183                 strcpy(entry->arg[i + 4], buf);
1184             }
1185         }
1186     }
1187 }
1188 
1189 int fn_check(char *name)
1190 {
1191     /* check to see if this is a valid file name... path-filter <type>
1192      * <message_file> <allowed_charset> <disallowed> */
1193 
1194     struct aclmember *entry = NULL;
1195     int j;
1196     char *path;
1197 #if ! defined(HAVE_REGEXEC)
1198     char *sp;
1199 #endif
1200 
1201 #ifdef M_UNIX
1202 #ifdef HAVE_REGEX
1203     char *regp;
1204 #endif
1205 #endif
1206 
1207 #ifdef HAVE_REGEXEC
1208     regex_t regexbuf;
1209     regmatch_t regmatchbuf;
1210     int rval;
1211 #endif
1212 
1213 #ifdef LINUX
1214     re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1215 #endif
1216 
1217     while (getaclentry("path-filter", &entry) && ARG0 != NULL) {
1218         if (type_match(ARG0) && ARG1 && ARG2) {
1219 
1220             /*
1221              * check *only* the basename
1222              */
1223 
1224             if ((path = strrchr(name, '/')))
1225                 ++path;
1226             else
1227                 path = name;
1228 
1229             /* is it in the allowed character set? */
1230 #if defined(HAVE_REGEXEC)
1231             if (regcomp(&regexbuf, ARG2, REG_EXTENDED) != 0) {
1232                 reply(550, "HAVE_REGEX error");
1233 #elif defined(HAVE_REGEX)
1234                 if ((sp = regcmp(ARG2, (char *) 0)) == NULL) {
1235                     reply(550, "HAVE_REGEX error");
1236 #else
1237             if ((sp = re_comp(ARG2)) != 0) {
1238                 perror_reply(550, sp);
1239 #endif
1240                 return (0);
1241             }
1242 #if defined(HAVE_REGEXEC)
1243             rval = regexec(&regexbuf, path, 1, &regmatchbuf, 0);
1244             regfree(&regexbuf);
1245             if (rval != 0) {
1246 #elif defined(HAVE_REGEX)
1247 #ifdef M_UNIX
1248                 regp = regex(sp, path);
1249                 free(sp);
1250                 if (regp == NULL) {
1251 #else
1252                 if ((regex(sp, path)) == NULL) {
1253 #endif
1254 #else
1255             if ((re_exec(path)) != 1) {
1256 #endif
1257                 pr_mesg(550, ARG1);
1258                 reply(550, "%s: Permission denied on server. (Filename (accept))", name);
1259                 return (0);
1260             }
1261             /* is it in any of the disallowed regexps */
1262 
1263             for (j = 3; j < MAXARGS; ++j) {
1264                 /* ARGj == entry->arg[j] */
1265                 if (entry->arg[j]) {
1266 #if defined(HAVE_REGEXEC)
1267                     if (regcomp(&regexbuf, entry->arg[j], REG_EXTENDED) != 0) {
1268                         reply(550, "HAVE_REGEX error");
1269 #elif defined(HAVE_REGEX)
1270                         if ((sp = regcmp(entry->arg[j], (char *) 0)) == NULL) {
1271                             reply(550, "HAVE_REGEX error");
1272 #else
1273                     if ((sp = re_comp(entry->arg[j])) != 0) {
1274                         perror_reply(550, sp);
1275 #endif
1276                         return (0);
1277                     }
1278 #if defined(HAVE_REGEXEC)
1279                     rval = regexec(&regexbuf, path, 1, &regmatchbuf, 0);
1280                     regfree(&regexbuf);
1281                     if (rval == 0) {
1282 #elif defined(HAVE_REGEX)
1283 #ifdef M_UNIX
1284                         regp = regex(sp, path);
1285                         free(sp);
1286                         if (regp != NULL) {
1287 #else
1288                         if ((regex(sp, path)) != NULL) {
1289 #endif
1290 #else
1291                     if ((re_exec(path)) == 1) {
1292 #endif
1293                         pr_mesg(550, ARG1);
1294                         reply(550, "%s: Permission denied on server. (Filename (deny))", name);
1295                         return (0);
1296                     }
1297                 }
1298             }
1299         }
1300     }
1301     return (1);
1302 }
1303 
1304 int dir_check(char *name, uid_t * uid, gid_t * gid, int *d_mode, int *valid)
1305 {
1306     struct aclmember *entry = NULL;
1307     int match_value = -1;
1308     char *ap2 = NULL;
1309     char *ap3 = NULL;
1310     char *ap4 = NULL;
1311     char *ap5 = NULL;
1312     char *ap6 = NULL;
1313     char *ap7 = NULL;
1314     char cwdir[MAXPATHLEN];
1315     char *pwdir;
1316     char abspwdir[MAXPATHLEN];
1317     char relpwdir[MAXPATHLEN];
1318     char path[MAXPATHLEN];
1319     char *sp;
1320     struct stat stbuf;
1321     int stat_result = -1;
1322     char class[1024];
1323     extern char *home;
1324 
1325     (void) acl_getclass(class);
1326 
1327     *valid = 0;
1328     /* what's our current directory? */
1329 
1330     /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
1331        out with an error. The rest of wu is so crufy that a long path might
1332        just blow up later */
1333 
1334     if ((strlen(name) + 1) > sizeof(path)) {
1335         perror_reply(550, "Path too long");
1336         return (-1);
1337     }
1338 
1339     strcpy(path, name);
1340     sp = strrchr(path, '/');
1341     if (sp)
1342         *sp = '\0';
1343     else
1344         strcpy(path, ".");
1345 
1346     if ((fb_realpath(path, cwdir)) == NULL) {
1347         perror_reply(550, "Could not determine cwdir");
1348         return (-1);
1349     }
1350 
1351     if ((fb_realpath(home, relpwdir)) == NULL) {
1352         perror_reply(550, "Could not determine pwdir");
1353         return (-1);
1354     }
1355 
1356     if ((wu_realpath(home, abspwdir, chroot_path)) == NULL) {
1357         perror_reply(550, "Could not determine pwdir");
1358         return (-1);
1359     }
1360 
1361     while (getaclentry("upload", &entry)) {
1362         char *q;
1363         int i = 0;
1364         int options = 1;
1365         int classfound = 0;
1366         int classmatched = 0;
1367         pwdir = abspwdir;
1368         while (options
1369                && (i < MAXARGS)
1370                && ((q = entry->arg[i]) != (char *) NULL)
1371                && (q[0] != '\0')) {
1372             if (strcasecmp(q, "absolute") == 0) {
1373                 i++;
1374                 pwdir = abspwdir;
1375             }
1376             else if (strcasecmp(q, "relative") == 0) {
1377                 i++;
1378                 pwdir = relpwdir;
1379             }
1380             else if (strncasecmp(q, "class=", 6) == 0) {
1381                 i++;
1382                 classfound = 1;
1383                 if (strcasecmp(q + 6, class) == 0)
1384                     classmatched = 1;
1385             }
1386             else if (strcmp(q, "-") == 0) {
1387                 i++;
1388                 options = 0;
1389             }
1390             else
1391                 options = 0;
1392         }
1393         if (!classfound || classmatched) {
1394             int j;
1395             if (((i + 1) < MAXARGS)
1396                 && ((q = entry->arg[i]) != (char *) NULL)
1397                 && (q[0] != '\0')
1398                 && (0 < path_compare(q, pwdir))
1399                 && ((j = path_compare(entry->arg[i + 1], cwdir)) >= match_value)) {
1400                 match_value = j;
1401 
1402                 ap2 = NULL;
1403                 if (((i + 2) < MAXARGS)
1404                     && ((q = entry->arg[i + 2]) != (char *) NULL)
1405                     && (q[0] != '\0'))
1406                     ap2 = q;
1407 
1408                 ap3 = NULL;
1409                 if (((i + 3) < MAXARGS)
1410                     && ((q = entry->arg[i + 3]) != (char *) NULL)
1411                     && (q[0] != '\0'))
1412                     ap3 = q;
1413 
1414                 ap4 = NULL;
1415                 if (((i + 4) < MAXARGS)
1416                     && ((q = entry->arg[i + 4]) != (char *) NULL)
1417                     && (q[0] != '\0'))
1418                     ap4 = q;
1419 
1420                 ap5 = NULL;
1421                 if (((i + 5) < MAXARGS)
1422                     && ((q = entry->arg[i + 5]) != (char *) NULL)
1423                     && (q[0] != '\0'))
1424                     ap5 = q;
1425 
1426                 ap6 = NULL;
1427                 if (((i + 6) < MAXARGS)
1428                     && ((q = entry->arg[i + 6]) != (char *) NULL)
1429                     && (q[0] != '\0'))
1430                     ap6 = q;
1431 
1432                 ap7 = NULL;
1433                 if (((i + 7) < MAXARGS)
1434                     && ((q = entry->arg[i + 7]) != (char *) NULL)
1435                     && (q[0] != '\0'))
1436                     ap7 = q;
1437             }
1438         }
1439     }
1440 
1441     if (anonymous && (match_value < 0)) {
1442         reply(550, "%s: Permission denied on server. (Upload dirs)", name);
1443         return (0);
1444     }
1445     if ((ap2 && !strcasecmp(ap2, "no"))
1446         || (ap3 && !strcasecmp(ap3, "nodirs"))
1447         || (ap6 && !strcasecmp(ap6, "nodirs"))) {
1448         reply(550, "%s: Permission denied on server. (Upload dirs)", name);
1449         return (0);
1450     }
1451     if ((ap3 && *ap3 == '*') || (ap4 && *ap4 == '*'))
1452         stat_result = stat(path, &stbuf);
1453     if (ap3) {
1454         if ((ap3[0] != '*') || (ap3[1] != '\0'))
1455             *uid = atoi(ap3);   /* the uid  */
1456         else if (stat_result == 0)
1457             *uid = stbuf.st_uid;
1458     }
1459     if (ap4) {
1460         if ((ap4[0] != '*') || (ap4[1] != '\0'))
1461             *gid = atoi(ap4);   /* the gid */
1462         else if (stat_result == 0)
1463             *gid = stbuf.st_gid;
1464     }
1465     if (ap7) {
1466         sscanf(ap7, "%o", d_mode);
1467         *valid = 1;
1468     }
1469     else if (ap5) {
1470         sscanf(ap5, "%o", d_mode);
1471         if (*d_mode & 0600)
1472             *d_mode |= 0100;
1473         if (*d_mode & 0060)
1474             *d_mode |= 0010;
1475         if (*d_mode & 0006)
1476             *d_mode |= 0001;
1477         *valid = 1;
1478     }
1479     return (1);
1480 }
1481 
1482 int upl_check(char *name, uid_t * uid, gid_t * gid, int *f_mode, int *valid)
1483 {
1484     int match_value = -1;
1485     char cwdir[MAXPATHLEN];
1486     char *pwdir;
1487     char abspwdir[MAXPATHLEN];
1488     char relpwdir[MAXPATHLEN];
1489     char path[MAXPATHLEN];
1490     char *sp;
1491     struct stat stbuf;
1492     int stat_result = -1;
1493     char *ap2 = NULL;
1494     char *ap3 = NULL;
1495     char *ap4 = NULL;
1496     char *ap5 = NULL;
1497     struct aclmember *entry = NULL;
1498     char class[1024];
1499     extern char *home;
1500 
1501     *valid = 0;
1502     (void) acl_getclass(class);
1503 
1504     /* what's our current directory? */
1505 
1506     /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
1507        out with an error. The rest of wu is so crufy that a long path might
1508        just blow up later */
1509 
1510     if ((strlen(name) + 1) > sizeof(path)) {
1511         perror_reply(553, "Path too long");
1512         return (-1);
1513     }
1514 
1515     strcpy(path, name);
1516     sp = strrchr(path, '/');
1517     if (sp)
1518         *sp = '\0';
1519     else
1520         strcpy(path, ".");
1521 
1522     if ((fb_realpath(path, cwdir)) == NULL) {
1523         perror_reply(553, "Could not determine cwdir");
1524         return (-1);
1525     }
1526 
1527     if ((wu_realpath(home, abspwdir, chroot_path)) == NULL) {
1528         perror_reply(553, "Could not determine pwdir");
1529         return (-1);
1530     }
1531 
1532     if ((fb_realpath(home, relpwdir)) == NULL) {
1533         perror_reply(553, "Could not determine pwdir");
1534         return (-1);
1535     }
1536 
1537     /*
1538        *  we are doing a "best match"... ..so we keep track of what "match
1539        *  value" we have received so far...
1540      */
1541     while (getaclentry("upload", &entry)) {
1542         char *q;
1543         int i = 0;
1544         int options = 1;
1545         int classfound = 0;
1546         int classmatched = 0;
1547         pwdir = abspwdir;
1548         while (options
1549                && (i < MAXARGS)
1550                && ((q = entry->arg[i]) != (char *) NULL)
1551                && (q[0] != '\0')) {
1552             if (strcasecmp(q, "absolute") == 0) {
1553                 i++;
1554                 pwdir = abspwdir;
1555             }
1556             else if (strcasecmp(q, "relative") == 0) {
1557                 i++;
1558                 pwdir = relpwdir;
1559             }
1560             else if (strncasecmp(q, "class=", 6) == 0) {
1561                 i++;
1562                 classfound = 1;
1563                 if (strcasecmp(q + 6, class) == 0)
1564                     classmatched = 1;
1565             }
1566             else if (strcmp(q, "-") == 0) {
1567                 i++;
1568                 options = 0;
1569             }
1570             else
1571                 options = 0;
1572         }
1573         if (!classfound || classmatched) {
1574             int j;
1575             if (((i + 1) < MAXARGS)
1576                 && ((q = entry->arg[i]) != (char *) NULL)
1577                 && (q[0] != '\0')
1578                 && (0 < path_compare(q, pwdir))
1579                 && ((j = path_compare(entry->arg[i + 1], cwdir)) >= match_value)) {
1580                 match_value = j;
1581 
1582                 ap2 = NULL;
1583                 if (((i + 2) < MAXARGS)
1584                     && ((q = entry->arg[i + 2]) != (char *) NULL)
1585                     && (q[0] != '\0'))
1586                     ap2 = q;
1587 
1588                 ap3 = NULL;
1589                 if (((i + 3) < MAXARGS)
1590                     && ((q = entry->arg[i + 3]) != (char *) NULL)
1591                     && (q[0] != '\0'))
1592                     ap3 = q;
1593 
1594                 ap4 = NULL;
1595                 if (((i + 4) < MAXARGS)
1596                     && ((q = entry->arg[i + 4]) != (char *) NULL)
1597                     && (q[0] != '\0'))
1598                     ap4 = q;
1599 
1600                 ap5 = NULL;
1601                 if (((i + 5) < MAXARGS)
1602                     && ((q = entry->arg[i + 5]) != (char *) NULL)
1603                     && (q[0] != '\0'))
1604                     ap5 = q;
1605             }
1606         }
1607     }
1608 
1609     if (ap3
1610         && ((!strcasecmp("dirs", ap3))
1611             || (!strcasecmp("nodirs", ap3))))
1612         ap3 = NULL;
1613 
1614     /*
1615        *  if we did get matches ... else don't do any of this stuff
1616      */
1617     if (match_value >= 0) {
1618         if (!strcasecmp(ap2, "yes")) {
1619             if ((ap3 && *ap3 == '*') || (ap4 && *ap4 == '*'))
1620                 stat_result = stat(path, &stbuf);
1621             if (ap3) {
1622                 if ((ap3[0] != '*') || (ap3[1] != '\0'))
1623                     *uid = atoi(ap3);   /* the uid  */
1624                 else if (stat_result == 0)
1625                     *uid = stbuf.st_uid;
1626             }
1627             if (ap4) {
1628                 if ((ap4[0] != '*') || (ap4[1] != '\0'))
1629                     *gid = atoi(ap4);   /* the gid  */
1630                 else if (stat_result == 0)
1631                     *gid = stbuf.st_gid;
1632                 *valid = 1;
1633             }
1634             if (ap5)
1635                 sscanf(ap5, "%o", f_mode);      /* the mode */
1636         }
1637         else {
1638             reply(553, "%s: Permission denied on server. (Upload)", name);
1639             return (-1);
1640         }
1641     }
1642     else {
1643         /*
1644            *  upload defaults to "permitted"
1645          */
1646         /* Not if anonymous */
1647         if (anonymous) {
1648             reply(553, "%s: Permission denied on server. (Upload)", name);
1649             return (-1);
1650         }
1651         return (1);
1652     }
1653 
1654     return (match_value);
1655 }
1656 
1657 int del_check(char *name)
1658 {
1659     int pdelete = (anonymous ? 0 : 1);
1660     struct aclmember *entry = NULL;
1661 
1662     while (getaclentry("delete", &entry) && ARG0 && ARG1 != NULL) {
1663         if (type_match(ARG1))
1664             if (anonymous) {
1665                 if (*ARG0 == 'y')
1666                     pdelete = 1;
1667             }
1668             else if (*ARG0 == 'n')
1669                 pdelete = 0;
1670     }
1671 
1672 /* H* fix: no deletion, period. You put a file here, I get to look at it. */
1673 #ifdef PARANOID
1674     pdelete = 0;
1675 #endif
1676 
1677     if (!pdelete) {
1678         reply(553, "%s: Permission denied on server. (Delete)", name);
1679         return (0);
1680     }
1681     else {
1682         return (1);
1683     }
1684 }
1685 
1686 /* The following is from the Debian add-ons. */
1687 
1688 #define lbasename(x) (strrchr(x,'/')?1+strrchr(x,'/'):x)
1689 
1690 int regexmatch(char *name, char *rgexp)
1691 {
1692 
1693 #ifdef M_UNIX
1694 #ifdef HAVE_REGEX
1695     char *regp;
1696 #endif
1697 #endif
1698 
1699 #ifdef HAVE_REGEXEC
1700     regex_t regexbuf;
1701     regmatch_t regmatchbuf;
1702     int rval;
1703 #else
1704     char *sp;
1705 #endif
1706 
1707 #if defined(HAVE_REGEXEC)
1708     if (regcomp(&regexbuf, rgexp, REG_EXTENDED) != 0) {
1709         reply(553, "HAVE_REGEX error");
1710 #elif defined(HAVE_REGEX)
1711         if ((sp = regcmp(rgexp, (char *) 0)) == NULL) {
1712             reply(553, "HAVE_REGEX error");
1713 #else
1714     if ((sp = re_comp(rgexp)) != 0) {
1715         perror_reply(553, sp);
1716 #endif
1717         return (0);
1718     }
1719 
1720 #if defined(HAVE_REGEXEC)
1721     rval = regexec(&regexbuf, name, 1, &regmatchbuf, 0);
1722     regfree(&regexbuf);
1723     if (rval != 0) {
1724 #elif defined(HAVE_REGEX)
1725 #ifdef M_UNIX
1726         regp = regex(sp, name);
1727         free(sp);
1728         if (regp == NULL) {
1729 #else
1730         if ((regex(sp, name)) == NULL) {
1731 #endif
1732 #else
1733     if ((re_exec(name)) != 1) {
1734 #endif
1735         return (0);
1736     }
1737     return (1);
1738 }
1739 
1740 static int allow_retrieve(char *name)
1741 {
1742     char realname[MAXPATHLEN + 1];
1743     char localname[MAXPATHLEN + 1];
1744     char *whichname;
1745     int i;
1746     struct aclmember *entry = NULL;
1747     char *p, *q;
1748     int options;
1749     int classfound;
1750     int classmatched;
1751     char class[1024];
1752 
1753     (void) acl_getclass(class);
1754     if ((name == (char *) NULL)
1755         || (*name == '\0'))
1756         return 0;
1757     fb_realpath(name, localname);
1758     wu_realpath(name, realname, chroot_path);
1759     while (getaclentry("allow-retrieve", &entry)) {
1760         whichname = realname;
1761         i = 0;
1762         options = 1;
1763         classfound = 0;
1764         classmatched = 0;
1765         while (options
1766                && (i < MAXARGS)
1767                && ((q = entry->arg[i]) != (char *) NULL)
1768                && (q[0] != '\0')) {
1769             if (strcasecmp(q, "absolute") == 0) {
1770                 i++;
1771                 whichname = realname;
1772             }
1773             else if (strcasecmp(q, "relative") == 0) {
1774                 i++;
1775                 whichname = localname;
1776             }
1777             else if (strncasecmp(q, "class=", 6) == 0) {
1778                 i++;
1779                 classfound = 1;
1780                 if (strcasecmp(q + 6, class) == 0)
1781                     classmatched = 1;
1782             }
1783             else if (strcmp(q, "-") == 0) {
1784                 i++;
1785                 options = 0;
1786             }
1787             else
1788                 options = 0;
1789         }
1790         if (!classfound || classmatched) {
1791             for (; (i < MAXARGS) && ((q = entry->arg[i]) != (char *) NULL) && (q[0] != '\0'); i++) {
1792                 p = (q[0] == '/') ? whichname : lbasename(whichname);
1793                 if (!wu_fnmatch(q, p, FNM_PATHNAME | FNM_LEADING_DIR)) {
1794                     return 1;
1795                 }
1796             }
1797         }
1798     }
1799     return 0;
1800 }
1801 
1802 int checknoretrieve(char *name)
1803 {
1804     char realname[MAXPATHLEN + 1];
1805     char localname[MAXPATHLEN + 1];
1806     char *whichname;
1807     int i;
1808     struct aclmember *entry = NULL;
1809     char *p, *q;
1810     int options;
1811     int classfound;
1812     int classmatched;
1813     char class[1024];
1814 
1815     extern struct passwd *pw;
1816     extern char *remoteident;
1817 
1818     (void) acl_getclass(class);
1819     if ((name == (char *) NULL)
1820         || (*name == '\0'))
1821         return 0;
1822     fb_realpath(name, localname);
1823     wu_realpath(name, realname, chroot_path);
1824     while (getaclentry("noretrieve", &entry)) {
1825         whichname = realname;
1826         i = 0;
1827         options = 1;
1828         classfound = 0;
1829         classmatched = 0;
1830         while (options
1831                && (i < MAXARGS)
1832                && ((q = entry->arg[i]) != (char *) NULL)
1833                && (q[0] != '\0')) {
1834             if (strcasecmp(q, "absolute") == 0) {
1835                 i++;
1836                 whichname = realname;
1837             }
1838             else if (strcasecmp(q, "relative") == 0) {
1839                 i++;
1840                 whichname = localname;
1841             }
1842             else if (strncasecmp(q, "class=", 6) == 0) {
1843                 i++;
1844                 classfound = 1;
1845                 if (strcasecmp(q + 6, class) == 0)
1846                     classmatched = 1;
1847             }
1848             else if (strcmp(q, "-") == 0) {
1849                 i++;
1850                 options = 0;
1851             }
1852             else
1853                 options = 0;
1854         }
1855         if (!classfound || classmatched) {
1856             for (; (i < MAXARGS) && ((q = entry->arg[i]) != (char *) NULL) && (q[0] != '\0'); i++) {
1857                 p = (q[0] == '/') ? whichname : lbasename(whichname);
1858                 if (!wu_fnmatch(q, p, FNM_PATHNAME | FNM_LEADING_DIR)) {
1859                     if (!allow_retrieve(name)) {
1860                         reply(550, "%s is marked unretrievable", localname);
1861                         return 1;
1862                     }
1863                 }
1864             }
1865         }
1866     }
1867     return 0;
1868 }
1869 
1870 #ifdef QUOTA
1871 
1872 #ifndef MNTMAXSTR
1873 #define MNTMAXSTR 2048          /* And hope it's enough */
1874 #endif
1875 
1876 #ifdef QUOTA_DEVICE
1877 
1878 int path_to_device(char *pathname, char *result)
1879 {
1880     FILE *fp;
1881 #ifdef HAS_OLDSTYLE_GETMNTENT
1882     struct mnttab static_mp;
1883     struct mnttab *mp = &static_mp;
1884 #else
1885     struct mntent *mp;
1886 #endif
1887     struct mount_ent {
1888         char mnt_fsname[MNTMAXSTR], mnt_dir[MNTMAXSTR];
1889         struct mount_ent *next;
1890     } mountent;
1891     struct mount_ent *current, *start, *new;
1892     char path[1024], mnt_dir[1024], *pos;
1893     int flag = 1;
1894 
1895     start = current = NULL;
1896 #ifdef HAS_OLDSTYLE_GETMNTENT
1897     fp = fopen(MNTTAB, "r");
1898 #else
1899     fp = setmntent(MNTTAB, "r");
1900 #endif
1901     if (fp == NULL)
1902         return 0;
1903 #ifdef HAS_OLDSTYLE_GETMNTENT
1904     while (getmntent(fp, &static_mp) == 0)
1905 #else
1906     while (mp = getmntent(fp))
1907 #endif
1908     {
1909         if (!(new = (struct mount_ent *) malloc(sizeof(mountent)))) {
1910             perror("malloc");
1911             flag = 0;
1912             break;
1913         }
1914 
1915         if (!start)
1916             start = current = new;
1917         else
1918             current = current->next = new;
1919 
1920 #ifdef HAS_OLDSTYLE_GETMNTENT
1921         strncpy(current->mnt_fsname, mp->mnt_special, strlen(mp->mnt_special) + 1);
1922         strncpy(current->mnt_dir, mp->mnt_mountp, strlen(mp->mnt_mountp) + 1);
1923 #else
1924         strncpy(current->mnt_fsname, mp->mnt_fsname, strlen(mp->mnt_fsname) + 1);
1925         strncpy(current->mnt_dir, mp->mnt_dir, strlen(mp->mnt_dir) + 1);
1926 #endif
1927     }
1928 #ifdef HAS_OLDSTYLE_GETMNTENT
1929     fclose(fp);
1930 #else
1931     endmntent(fp);
1932 #endif
1933     current->next = NULL;
1934 
1935     wu_realpath(pathname, path, chroot_path);
1936 
1937     while (*path && flag) {
1938         current = start;
1939         while (current && flag) {
1940             if (strcmp(current->mnt_dir, "swap")) {
1941                 wu_realpath(current->mnt_dir, mnt_dir, chroot_path);
1942                 if (!strcmp(mnt_dir, path)) {
1943                     flag = 0;
1944                     /* no support for remote quota yet */
1945                     if (!strchr(current->mnt_fsname, ':'))
1946                         strcpy(result, current->mnt_fsname);
1947                 }
1948             }
1949             current = current->next;
1950         }
1951         if (!((pos = strrchr(path, '/')) - path) && strlen(path) > 1)
1952             strcpy(path, "/");
1953         else
1954             path[pos - path] = '\0';
1955     }
1956     while (current) {
1957         new = current->next;
1958         free(current);
1959         current = new;
1960     }
1961     return 1;
1962 }
1963 #endif
1964 
1965 void get_quota(char *fs, int uid)
1966 {
1967     char mnt_fsname[MNTMAXSTR];
1968 #ifdef HAS_NO_QUOTACTL
1969     int dirfd;
1970     struct quotctl qp;
1971 #endif
1972 
1973     /*
1974      * Getting file system quota information can take a noticeable amount
1975      * of time, so only get quota information for specified users.
1976      * quota-info <uid-range> [<uid-range> ...]
1977      */
1978     if (!uid_match("quota-info", uid))
1979         return;
1980 
1981 #ifdef HAS_NO_QUOTACTL
1982     if (path_to_device(fs, mnt_fsname)) {
1983         dirfd = open(fs, O_RDONLY);
1984         qp.op = Q_GETQUOTA;
1985         qp.uid = uid;
1986         qp.addr = (char *) &quota;
1987         ioctl(dirfd, Q_QUOTACTL, &qp);
1988         close(dirfd);
1989     }
1990 #else
1991 #ifdef QUOTA_DEVICE
1992 
1993     if (path_to_device(fs, mnt_fsname))
1994 #ifdef QCMD
1995         quotactl(QCMD(Q_GETQUOTA, USRQUOTA), mnt_fsname, uid, (char *) &quota);
1996 #else
1997         quotactl(Q_GETQUOTA, mnt_fsname, uid, (char *) &quota);
1998 #endif
1999 #else
2000     quotactl(fs, QCMD(Q_GETQUOTA, USRQUOTA), uid, (char *) &quota);
2001 #endif
2002 #endif /* HAS_NO_QUOTACTL */
2003 }
2004 
2005 char *time_quota(long curstate, long softlimit, long timelimit, char *timeleft)
2006 {
2007     struct timeval tv;
2008 
2009     gettimeofday(&tv, NULL);
2010     if (softlimit && curstate >= softlimit) {
2011         if (timelimit == 0) {
2012             strcpy(timeleft, "NOT STARTED");
2013         }
2014         else if (timelimit > tv.tv_sec) {
2015             fmttime(timeleft, timelimit - tv.tv_sec);
2016         }
2017         else {
2018             strcpy(timeleft, "EXPIRED");
2019         }
2020     }
2021     else {
2022         timeleft[0] = '\0';
2023     }
2024     return (timeleft);
2025 }
2026 
2027 void fmttime(char *buf, register long time)
2028 {
2029     int i;
2030     static struct {
2031         int c_secs;             /* conversion units in secs */
2032         char *c_str;            /* unit string */
2033     } cunits[] = {
2034         {
2035             60 *60 * 24 * 28, "months"
2036         } ,
2037         {
2038             60 *60 * 24 * 7, "weeks"
2039         } ,
2040         {
2041             60 *60 * 24, "days"
2042         } ,
2043         {
2044             60 *60, "hours"
2045         } ,
2046         {
2047             60, "mins"
2048         } ,
2049         {
2050             1, "secs"
2051         }
2052     };
2053 
2054     if (time <= 0) {
2055         strcpy(buf, "EXPIRED");
2056         return;
2057     }
2058     for (i = 0; i < sizeof(cunits) / sizeof(cunits[0]); i++) {
2059         if (time >= cunits[i].c_secs)
2060             break;
2061     }
2062     sprintf(buf, "%.1f %s", (double) time / cunits[i].c_secs, cunits[i].c_str);
2063 }
2064 
2065 #endif
2066 
2067 #ifdef THROUGHPUT
2068 
2069 int file_compare(char *patterns, char *file)
2070 {
2071     char buf[MAXPATHLEN+1];
2072     char *cp;
2073     char *cp2;
2074     int i;
2075     int matches = 0;
2076 
2077     strncpy(buf, patterns, sizeof(buf) - 1);
2078     buf[sizeof(buf) - 2] = '\0';
2079     i = strlen(buf);
2080     buf[i++] = ',';
2081     buf[i++] = '\0';
2082 
2083     cp = buf;
2084     while ((cp2 = strchr(cp, ',')) != NULL) {
2085         *cp2++ = '\0';
2086         if (wu_fnmatch(cp, file, FNM_PATHNAME) == 0) {
2087             matches = 1;
2088             break;
2089         }
2090         cp = cp2;
2091     }
2092     return matches;
2093 }
2094 
2095 int remote_compare(char *patterns)
2096 {
2097     char buf[MAXPATHLEN+1];
2098     char *cp;
2099     char *cp2;
2100     int i;
2101     int matches = 0;
2102 
2103     strncpy(buf, patterns, sizeof(buf) - 1);
2104     buf[sizeof(buf) - 2] = '\0';
2105     i = strlen(buf);
2106     buf[i++] = ',';
2107     buf[i++] = '\0';
2108 
2109     cp = buf;
2110     while ((cp2 = strchr(cp, ',')) != NULL) {
2111         *cp2++ = '\0';
2112         if (hostmatch(cp, remoteaddr, remotehost)) {
2113             matches = 1;
2114             break;
2115         }
2116         cp = cp2;
2117     }
2118     return matches;
2119 }
2120 
2121 void throughput_calc(char *name, int *bps, double *bpsmult)
2122 {
2123     int match_value = -1;
2124     char cwdir[MAXPATHLEN];
2125     char pwdir[MAXPATHLEN];
2126     char path[MAXPATHLEN];
2127     char file[MAXPATHLEN];
2128     char *ap3 = NULL, *ap4 = NULL;
2129     struct aclmember *entry = NULL;
2130     extern char *home;
2131     char *sp;
2132     int i;
2133 
2134     /* default is maximum throughput */
2135     *bps = -1;
2136     *bpsmult = 1.0;
2137 
2138     /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
2139        out with an error. The rest of wu is so crufy that a long path might
2140        just blow up later */
2141 
2142     if ((strlen(name) + 1) > sizeof(path)) {
2143         return;
2144     }
2145 
2146     /* what's our current directory? */
2147     strcpy(path, name);
2148     if ((sp = strrchr(path, '/')))
2149         *sp = '\0';
2150     else
2151         strcpy(path, ".");
2152     if ((sp = strrchr(name, '/')))
2153         strcpy(file, sp + 1);
2154     else
2155         strcpy(file, name);
2156     if ((fb_realpath(path, cwdir)) == NULL) {
2157         return;
2158     }
2159 
2160     wu_realpath(home, pwdir, chroot_path);
2161 
2162     /* find best matching entry */
2163     while (getaclentry("throughput", &entry) && ARG0 && ARG1 && ARG2 && ARG3 && ARG4 && ARG5 != NULL) {
2164         if ((0 < path_compare(ARG0, pwdir))
2165             && ((i = path_compare(ARG1, cwdir)) >= match_value)
2166             ) {
2167             if (file_compare(ARG2, file)) {
2168                 if (remote_compare(ARG5)) {
2169                     match_value = i;
2170                     ap3 = ARG3;
2171                     ap4 = ARG4;
2172                 }
2173             }
2174         }
2175     }
2176 
2177     /* if we did get matches */
2178     if (match_value >= 0) {
2179         if (strcasecmp(ap3, "oo") == 0)
2180             *bps = -1;
2181         else
2182             *bps = atoi(ap3);
2183         if (strcmp(ap4, "-") == 0)
2184             *bpsmult = 1.0;
2185         else
2186             *bpsmult = atof(ap4);
2187     }
2188     return;
2189 }
2190 
2191 void throughput_adjust(char *name)
2192 {
2193     int match_value = -1;
2194     char pwdir[MAXPATHLEN];
2195     char cwdir[MAXPATHLEN];
2196     char path[MAXPATHLEN];
2197     char file[MAXPATHLEN];
2198     char buf[MAXPATHLEN];
2199     char *ap3 = NULL, *ap4 = NULL;
2200     char **pap;
2201     struct aclmember *entry = NULL;
2202     extern char *home;
2203     char *sp;
2204     int i;
2205 
2206     /* XXX We could use dynamic RAM to store this path, but I'd rather just bail
2207        out with an error. The rest of wu is so crufy that a long path might
2208        just blow up later */
2209 
2210     if ((strlen(name) + 1) > sizeof(path)) {
2211         return;
2212     }
2213 
2214     /* what's our current directory? */
2215     strcpy(path, name);
2216     if ((sp = strrchr(path, '/')))
2217         *sp = '\0';
2218     else
2219         strcpy(path, ".");
2220     if ((sp = strrchr(name, '/')))
2221         strcpy(file, sp + 1);
2222     else
2223         strcpy(file, name);
2224     if ((fb_realpath(path, cwdir)) == NULL) {
2225         return;
2226     }
2227 
2228     wu_realpath(home, pwdir, chroot_path);
2229 
2230     /* find best matching entry */
2231     while (getaclentry("throughput", &entry) && ARG0 && ARG1 && ARG2 && ARG3 && ARG4 && ARG5 != NULL) {
2232         if ((0 < path_compare(ARG0, pwdir))
2233             && ((i = path_compare(ARG1, cwdir)) >= match_value)
2234             ) {
2235             if (file_compare(ARG2, file)) {
2236                 if (remote_compare(ARG5)) {
2237                     match_value = i;
2238                     ap3 = ARG3;
2239                     pap = ARG;
2240                     ap4 = ARG4;
2241                 }
2242             }
2243         }
2244     }
2245 
2246     /* if we did get matches */
2247     if (match_value >= 0) {
2248         if (strcasecmp(ap3, "oo") != 0) {
2249             if (strcmp(ap4, "-") != 0) {
2250                 sprintf(buf, "%.0f", atoi(ap3) * atof(ap4));
2251                 pap[3] = (char *) malloc(strlen(buf) + 1);
2252                 if (pap[3] == NULL) {
2253                     syslog(LOG_ERR, "malloc error in throughput_adjust");
2254                     dologout(1);
2255                 }
2256                 /* Use ARG6 to keep track of malloced memory */
2257                 if (pap[6])
2258                     free(pap[6]);
2259                 pap[6] = pap[3];
2260                 strcpy(pap[3], buf);
2261             }
2262         }
2263     }
2264     return;
2265 }
2266 
2267 #endif
2268 
2269 #ifdef SOLARIS_2
2270 static int CheckMethod = 1;
2271 #else
2272 static int CheckMethod = 0;
2273 #endif
2274 
2275 void SetCheckMethod(const char *method)
2276 {
2277     if ((strcasecmp(method, "md5") == 0)
2278         || (strcasecmp(method, "rfc1321") == 0))
2279         CheckMethod = 0;
2280     else if ((strcasecmp(method, "crc") == 0)
2281              || (strcasecmp(method, "posix") == 0))
2282         CheckMethod = 1;
2283     else {
2284         reply(500, "Unrecognized checksum method");
2285         return;
2286     }
2287     switch (CheckMethod) {
2288     default:
2289         reply(200, "Checksum method is now: MD5 (RFC1321)");
2290         break;
2291     case 1:
2292         reply(200, "Checksum method is now: CRC (POSIX)");
2293         break;
2294     }
2295 }
2296 
2297 void ShowCheckMethod(void)
2298 {
2299     switch (CheckMethod) {
2300     default:
2301         reply(200, "Current checksum method: MD5 (RFC1321)");
2302         break;
2303     case 1:
2304         reply(200, "Current checksum method: CRC (POSIX)");
2305         break;
2306     }
2307 }
2308 
2309 void CheckSum(char *pathname)
2310 {
2311     char *cmd;
2312     char buf[MAXPATHLEN];
2313     FILE *cmdf;
2314     struct stat st;
2315 
2316     if (stat(pathname, &st) == 0) {
2317         if ((st.st_mode & S_IFMT) != S_IFREG) {
2318             reply(500, "%s: not a plain file.", pathname);
2319             return;
2320         }
2321     }
2322     else {
2323         perror_reply(550, pathname);
2324         return;
2325     }
2326 
2327     switch (CheckMethod) {
2328     default:
2329         cmd = "/bin/md5sum";
2330         break;
2331     case 1:
2332         cmd = "/bin/cksum";
2333         break;
2334     }
2335 
2336     if (strlen(cmd) + 1 + strlen(pathname) + 1 > sizeof(buf)) {
2337         reply(500, "Pathname too long");
2338         return;
2339     }
2340     sprintf(buf, "%s %s", cmd, pathname);
2341 
2342     cmdf = ftpd_popen(buf, "r", 0);
2343     if (!cmdf) {
2344         perror_reply(550, cmd);
2345     }
2346     else {
2347         if (fgets(buf, sizeof buf, cmdf)) {
2348             char *crptr = strchr(buf, '\n');
2349             if (crptr != NULL)
2350                 *crptr = '\0';
2351             reply(200, "%s", buf);
2352         }
2353         ftpd_pclose(cmdf);
2354     }
2355 }
2356 
2357 void CheckSumLastFile(void)
2358 {
2359     extern char LastFileTransferred[];
2360 
2361     if (LastFileTransferred[0] == '\0')
2362         reply(500, "Nothing transferred yet");
2363     else
2364         CheckSum(LastFileTransferred);
2365 }