Coverage Report

Created: 2024-09-30 06:24

/src/proftpd/modules/mod_xfer.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 1997, 1998 Public Flood Software
4
 * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5
 * Copyright (c) 2001-2024 The ProFTPD Project team
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20
 *
21
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
22
 * and other respective copyright holders give permission to link this program
23
 * with OpenSSL, and distribute the resulting executable, without including
24
 * the source code for OpenSSL in the source distribution.
25
 */
26
27
/* Data transfer module for ProFTPD */
28
29
#include "conf.h"
30
#include "privs.h"
31
#include "error.h"
32
33
#ifdef HAVE_SYS_SENDFILE_H
34
# include <sys/sendfile.h>
35
#endif
36
37
/* Minimum priority a process can have. */
38
#ifndef PRIO_MIN
39
# define PRIO_MIN -20
40
#endif
41
42
/* Maximum priority a process can have.  */
43
#ifndef PRIO_MAX
44
# define PRIO_MAX 20
45
#endif
46
47
extern module auth_module;
48
extern pid_t mpid;
49
50
/* Variables for this module */
51
static pr_fh_t *retr_fh = NULL;
52
static pr_fh_t *stor_fh = NULL;
53
static pr_fh_t *displayfilexfer_fh = NULL;
54
55
static unsigned char have_rfc2228_data = FALSE;
56
static unsigned char have_type = FALSE;
57
static unsigned char have_zmode = FALSE;
58
static unsigned char use_sendfile = TRUE;
59
static off_t use_sendfile_len = 0;
60
static float use_sendfile_pct = -1.0;
61
62
static int xfer_check_limit(cmd_rec *);
63
64
/* TransferOptions */
65
0
#define PR_XFER_OPT_HANDLE_ALLO     0x0001
66
0
#define PR_XFER_OPT_IGNORE_ASCII    0x0002
67
0
#define PR_XFER_OPT_ALLOW_SYMLINK_UPLOAD  0x0004
68
static unsigned long xfer_opts = PR_XFER_OPT_HANDLE_ALLO;
69
70
static void xfer_exit_ev(const void *, void *);
71
static void xfer_sigusr2_ev(const void *, void *);
72
static void xfer_timeout_session_ev(const void *, void *);
73
static void xfer_timeout_stalled_ev(const void *, void *);
74
static int xfer_sess_init(void);
75
76
/* Used for MaxTransfersPerHost and TransferRate */
77
static int xfer_parse_cmdlist(const char *, config_rec *, char *);
78
79
module xfer_module;
80
81
static int xfer_logged_sendfile_decline_msg = FALSE;
82
83
static const char *trace_channel = "xfer";
84
85
0
static off_t find_max_nbytes(char *directive) {
86
0
  config_rec *c = NULL;
87
0
  unsigned int ctxt_precedence = 0;
88
0
  unsigned char have_user_limit, have_group_limit, have_class_limit,
89
0
    have_all_limit;
90
0
  off_t max_nbytes = 0UL;
91
92
0
  have_user_limit = have_group_limit = have_class_limit =
93
0
    have_all_limit = FALSE;
94
95
0
  c = find_config(CURRENT_CONF, CONF_PARAM, directive, FALSE);
96
0
  while (c != NULL) {
97
0
    pr_signals_handle();
98
99
    /* This check is for more than three arguments: one argument is the
100
     * classifier (i.e. "user", "group", or "class"), one argument is
101
     * the precedence, one is the number of bytes; the remaining arguments
102
     * are the individual items in the configured expression.
103
     */
104
105
0
    if (c->argc > 3) {
106
0
      if (strcasecmp(c->argv[2], "user") == 0) {
107
0
        if (pr_expr_eval_user_or((char **) &c->argv[3]) == TRUE) {
108
0
          if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
109
110
            /* Set the context precedence */
111
0
            ctxt_precedence = *((unsigned int *) c->argv[1]);
112
113
0
            max_nbytes = *((off_t *) c->argv[0]);
114
115
0
            have_group_limit = have_class_limit = have_all_limit = FALSE;
116
0
            have_user_limit = TRUE;
117
0
          }
118
0
        }
119
120
0
      } else if (strcasecmp(c->argv[2], "group") == 0) {
121
0
        if (pr_expr_eval_group_or((char **) &c->argv[3]) == TRUE) {
122
0
          if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
123
124
            /* Set the context precedence */
125
0
            ctxt_precedence = *((unsigned int *) c->argv[1]);
126
127
0
            max_nbytes = *((off_t *) c->argv[0]);
128
129
0
            have_user_limit = have_class_limit = have_all_limit = FALSE;
130
0
            have_group_limit = TRUE;
131
0
          }
132
0
        }
133
134
0
      } else if (strcasecmp(c->argv[2], "class") == 0) {
135
0
        if (pr_expr_eval_class_or((char **) &c->argv[3]) == TRUE) {
136
0
          if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
137
138
            /* Set the context precedence */
139
0
            ctxt_precedence = *((unsigned int *) c->argv[1]);
140
141
0
            max_nbytes = *((off_t *) c->argv[0]);
142
143
0
            have_user_limit = have_group_limit = have_all_limit = FALSE;
144
0
            have_class_limit = TRUE;
145
0
          }
146
0
        }
147
0
      }
148
149
0
    } else {
150
0
      if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
151
152
        /* Set the context precedence. */
153
0
        ctxt_precedence = *((unsigned int *) c->argv[1]);
154
155
0
        max_nbytes = *((off_t *) c->argv[0]);
156
157
0
        have_user_limit = have_group_limit = have_class_limit = FALSE;
158
0
        have_all_limit = TRUE;
159
0
      }
160
0
    }
161
162
0
    c = find_config_next(c, c->next, CONF_PARAM, directive, FALSE);
163
0
  }
164
165
  /* Print out some nice debugging information. */
166
0
  if (max_nbytes > 0 &&
167
0
      (have_user_limit ||
168
0
       have_group_limit ||
169
0
       have_class_limit ||
170
0
       have_all_limit)) {
171
0
    pr_log_debug(DEBUG5, "%s (%" PR_LU " bytes) in effect for %s",
172
0
      directive, (pr_off_t) max_nbytes,
173
0
      have_user_limit ? "user " : have_group_limit ? "group " :
174
0
      have_class_limit ? "class " : "all");
175
0
  }
176
177
0
  return max_nbytes;
178
0
}
179
180
0
static void _log_transfer(char direction, char abort_flag) {
181
0
  struct timeval end_time;
182
0
  char *fullpath = NULL;
183
184
0
  memset(&end_time, '\0', sizeof(end_time));
185
186
0
  if (session.xfer.start_time.tv_sec != 0) {
187
0
    gettimeofday(&end_time, NULL);
188
0
    end_time.tv_sec -= session.xfer.start_time.tv_sec;
189
190
0
    if (end_time.tv_usec >= session.xfer.start_time.tv_usec) {
191
0
      end_time.tv_usec -= session.xfer.start_time.tv_usec;
192
193
0
    } else {
194
0
      end_time.tv_usec = 1000000L - (session.xfer.start_time.tv_usec -
195
0
        end_time.tv_usec);
196
0
      end_time.tv_sec--;
197
0
    }
198
0
  }
199
200
0
  fullpath = dir_abs_path(session.xfer.p, session.xfer.path, TRUE);
201
202
0
  if ((session.sf_flags & SF_ANON) != 0) {
203
0
    xferlog_write(end_time.tv_sec, pr_netaddr_get_sess_remote_name(),
204
0
      session.xfer.total_bytes, fullpath,
205
0
      (session.sf_flags & SF_ASCII ? 'a' : 'b'), direction,
206
0
      'a', session.anon_user, abort_flag, "_");
207
208
0
  } else {
209
0
    xferlog_write(end_time.tv_sec, pr_netaddr_get_sess_remote_name(),
210
0
      session.xfer.total_bytes, fullpath,
211
0
      (session.sf_flags & SF_ASCII ? 'a' : 'b'), direction,
212
0
      'r', session.user, abort_flag, "_");
213
0
  }
214
215
0
  pr_log_debug(DEBUG1, "Transfer %s %" PR_LU " bytes in %ld.%02lu seconds",
216
0
    abort_flag == 'c' ? "completed:" : "aborted after",
217
0
    (pr_off_t) session.xfer.total_bytes, (long) end_time.tv_sec,
218
0
    (unsigned long)(end_time.tv_usec / 10000));
219
0
}
220
221
/* Code borrowed from src/dirtree.c's get_word() -- modified to separate
222
 * words on commas as well as spaces.
223
 */
224
0
static char *get_cmd_from_list(char **list) {
225
0
  char *res = NULL, *dst = NULL;
226
0
  unsigned char quote_mode = FALSE;
227
228
0
  while (**list && PR_ISSPACE(**list)) {
229
0
    (*list)++;
230
0
  }
231
232
0
  if (!**list) {
233
0
    return NULL;
234
0
  }
235
236
0
  res = dst = *list;
237
238
0
  if (**list == '\"') {
239
0
    quote_mode = TRUE;
240
0
    (*list)++;
241
0
  }
242
243
0
  while (**list && **list != ',' &&
244
0
      (quote_mode ? (**list != '\"') : (!PR_ISSPACE(**list)))) {
245
246
0
    if (**list == '\\' && quote_mode) {
247
248
      /* escaped char */
249
0
      if (*((*list) + 1)) {
250
0
        *dst = *(++(*list));
251
0
      }
252
0
    }
253
254
0
    *dst++ = **list;
255
0
    ++(*list);
256
0
  }
257
258
0
  if (**list) {
259
0
    (*list)++;
260
0
  }
261
262
0
  *dst = '\0';
263
0
  return res;
264
0
}
265
266
0
static int xfer_check_limit(cmd_rec *cmd) {
267
0
  config_rec *c = NULL;
268
0
  const char *client_addr = pr_netaddr_get_ipstr(session.c->remote_addr);
269
0
  char server_addr[128];
270
271
0
  memset(server_addr, '\0', sizeof(server_addr));
272
0
  pr_snprintf(server_addr, sizeof(server_addr)-1, "%s:%d",
273
0
    pr_netaddr_get_ipstr(main_server->addr), main_server->ServerPort);
274
0
  server_addr[sizeof(server_addr)-1] = '\0';
275
276
0
  c = find_config(CURRENT_CONF, CONF_PARAM, "MaxTransfersPerHost", FALSE);
277
0
  while (c != NULL) {
278
0
    char *xfer_cmd = NULL, **cmdlist = (char **) c->argv[0];
279
0
    unsigned char matched_cmd = FALSE;
280
0
    unsigned int curr = 0, max = 0;
281
0
    pr_scoreboard_entry_t *score = NULL;
282
283
0
    pr_signals_handle();
284
285
    /* Does this MaxTransfersPerHost apply to the current command?  Note: this
286
     * could be made more efficient by using bitmasks rather than string
287
     * comparisons.
288
     */
289
0
    for (xfer_cmd = *cmdlist; xfer_cmd; xfer_cmd = *(cmdlist++)) {
290
0
      if (strcasecmp(xfer_cmd, cmd->argv[0]) == 0) {
291
0
        matched_cmd = TRUE;
292
0
        break;
293
0
      }
294
0
    }
295
296
0
    if (matched_cmd == FALSE) {
297
0
      c = find_config_next(c, c->next, CONF_PARAM, "MaxTransfersPerHost",
298
0
        FALSE);
299
0
      continue;
300
0
    }
301
302
0
    max = *((unsigned int *) c->argv[1]);
303
304
    /* Count how many times the current IP address is logged in, AND how
305
     * many of those other logins are currently using this command.
306
     */
307
308
0
    (void) pr_rewind_scoreboard();
309
0
    while ((score = pr_scoreboard_entry_read()) != NULL) {
310
0
      pr_signals_handle();
311
312
      /* Scoreboard entry must match local server address and remote client
313
       * address to be counted.
314
       */
315
0
      if (strcmp(score->sce_server_addr, server_addr) != 0) {
316
0
        pr_trace_msg(trace_channel, 25,
317
0
          "MaxTransfersPerHost: server address '%s' does not match '%s', "
318
0
          "skipping", server_addr, score->sce_server_addr);
319
0
        continue;
320
0
      }
321
322
0
      if (strcmp(score->sce_client_addr, client_addr) != 0) {
323
0
        pr_trace_msg(trace_channel, 25,
324
0
          "MaxTransfersPerHost: client address '%s' does not match '%s', "
325
0
          "skipping", client_addr, score->sce_client_addr);
326
0
        continue;
327
0
      }
328
329
0
      if (strcmp(score->sce_cmd, xfer_cmd) != 0) {
330
0
        pr_trace_msg(trace_channel, 25,
331
0
          "MaxTransfersPerHost: current command '%s' does not match '%s', "
332
0
          "skipping", xfer_cmd, score->sce_cmd);
333
0
        continue;
334
0
      }
335
336
0
      curr++;
337
0
    }
338
339
0
    pr_restore_scoreboard();
340
341
0
    pr_trace_msg(trace_channel, 19,
342
0
      "MaxTransfersPerHost: %s (current = %u, max = %u) for client '%s'",
343
0
      xfer_cmd, curr, max, client_addr);
344
345
0
    if (curr >= max) {
346
0
      char maxn[20];
347
348
0
      char *maxstr = "Sorry, the maximum number of data transfers (%m) from "
349
0
        "your host are currently being used.";
350
351
0
      if (c->argv[2] != NULL) {
352
0
        maxstr = c->argv[2];
353
0
      }
354
355
0
      pr_event_generate("mod_xfer.max-transfers-per-host", session.c);
356
357
0
      memset(maxn, '\0', sizeof(maxn));
358
0
      pr_snprintf(maxn, sizeof(maxn)-1, "%u", max);
359
0
      pr_response_send(R_451, "%s", sreplace(cmd->tmp_pool, maxstr, "%m",
360
0
        maxn, NULL));
361
0
      pr_log_debug(DEBUG4, "MaxTransfersPerHost %u exceeded for %s for "
362
0
        "client '%s'", max, xfer_cmd, client_addr);
363
364
0
      return -1;
365
0
    }
366
367
0
    c = find_config_next(c, c->next, CONF_PARAM, "MaxTransfersPerHost", FALSE);
368
0
  }
369
370
0
  c = find_config(CURRENT_CONF, CONF_PARAM, "MaxTransfersPerUser", FALSE);
371
0
  while (c != NULL) {
372
0
    char *xfer_cmd = NULL, **cmdlist = (char **) c->argv[0];
373
0
    unsigned char matched_cmd = FALSE;
374
0
    unsigned int curr = 0, max = 0;
375
0
    pr_scoreboard_entry_t *score = NULL;
376
377
0
    pr_signals_handle();
378
379
    /* Does this MaxTransfersPerUser apply to the current command?  Note: this
380
     * could be made more efficient by using bitmasks rather than string
381
     * comparisons.
382
     */
383
0
    for (xfer_cmd = *cmdlist; xfer_cmd; xfer_cmd = *(cmdlist++)) {
384
0
      if (strcasecmp(xfer_cmd, cmd->argv[0]) == 0) {
385
0
        matched_cmd = TRUE;
386
0
        break;
387
0
      }
388
0
    }
389
390
0
    if (matched_cmd == FALSE) {
391
0
      c = find_config_next(c, c->next, CONF_PARAM, "MaxTransfersPerUser",
392
0
        FALSE);
393
0
      continue;
394
0
    }
395
396
0
    max = *((unsigned int *) c->argv[1]);
397
398
    /* Count how many times the current user is logged in, AND how many of
399
     * those other logins are currently using this command.
400
     */
401
402
0
    (void) pr_rewind_scoreboard();
403
0
    while ((score = pr_scoreboard_entry_read()) != NULL) {
404
0
      pr_signals_handle();
405
406
0
      if (strcmp(score->sce_server_addr, server_addr) != 0) {
407
0
        pr_trace_msg(trace_channel, 25,
408
0
          "MaxTransfersPerUser: server address '%s' does not match '%s', "
409
0
          "skipping", server_addr, score->sce_server_addr);
410
0
        continue;
411
0
      }
412
413
0
      if (strcmp(score->sce_user, session.user) != 0) {
414
0
        pr_trace_msg(trace_channel, 25,
415
0
          "MaxTransfersPerUser: user '%s' does not match '%s', skipping",
416
0
          session.user, score->sce_user);
417
0
        continue;
418
0
      }
419
420
0
      if (strcmp(score->sce_cmd, xfer_cmd) == 0) {
421
0
        pr_trace_msg(trace_channel, 25,
422
0
          "MaxTransfersPerUser: command '%s' does not match '%s', skipping",
423
0
          xfer_cmd, score->sce_cmd);
424
0
        continue;
425
0
      }
426
427
0
      curr++;
428
0
    }
429
430
0
    pr_restore_scoreboard();
431
432
0
    pr_trace_msg(trace_channel, 19,
433
0
      "MaxTransfersPerUser: %s (current = %u, max = %u) for user '%s'",
434
0
      xfer_cmd, curr, max, session.user);
435
436
0
    if (curr >= max) {
437
0
      char maxn[20];
438
439
0
      char *maxstr = "Sorry, the maximum number of data transfers (%m) from "
440
0
        "this user are currently being used.";
441
442
0
      if (c->argv[2] != NULL) {
443
0
        maxstr = c->argv[2];
444
0
      }
445
446
0
      pr_event_generate("mod_xfer.max-transfers-per-user", session.user);
447
448
0
      memset(maxn, '\0', sizeof(maxn));
449
0
      pr_snprintf(maxn, sizeof(maxn)-1, "%u", max);
450
0
      pr_response_send(R_451, "%s", sreplace(cmd->tmp_pool, maxstr, "%m",
451
0
        maxn, NULL));
452
0
      pr_log_debug(DEBUG4, "MaxTransfersPerUser %u exceeded for %s for "
453
0
        "user '%s'", max, xfer_cmd, session.user);
454
455
0
      return -1;
456
0
    }
457
458
0
    c = find_config_next(c, c->next, CONF_PARAM, "MaxTransfersPerUser", FALSE);
459
0
  }
460
461
0
  return 0;
462
0
}
463
464
0
static void xfer_displayfile(void) {
465
466
0
  if (displayfilexfer_fh != NULL) {
467
0
    if (pr_display_fh(displayfilexfer_fh, session.vwd, R_226, 0) < 0) {
468
0
      pr_log_debug(DEBUG6, "unable to display DisplayFileTransfer "
469
0
        "file '%s': %s", displayfilexfer_fh->fh_path, strerror(errno));
470
0
    }
471
472
    /* Rewind the filehandle, so that it can be used again. */
473
0
    if (pr_fsio_lseek(displayfilexfer_fh, 0, SEEK_SET) < 0) {
474
0
      pr_log_debug(DEBUG6, "error rewinding DisplayFileTransfer "
475
0
        "file '%s': %s", displayfilexfer_fh->fh_path, strerror(errno));
476
0
    }
477
478
0
  } else {
479
0
    char *displayfilexfer;
480
481
0
    displayfilexfer = get_param_ptr(main_server->conf, "DisplayFileTransfer",
482
0
      FALSE);
483
0
    if (displayfilexfer) {
484
0
      if (pr_display_file(displayfilexfer, session.vwd, R_226, 0) < 0) {
485
0
        pr_log_debug(DEBUG6, "unable to display DisplayFileTransfer "
486
0
          "file '%s': %s", displayfilexfer, strerror(errno));
487
0
      }
488
0
    }
489
0
  }
490
0
}
491
492
static int xfer_parse_cmdlist(const char *name, config_rec *c,
493
0
    char *cmdlist) {
494
0
  char *cmd = NULL;
495
0
  array_header *cmds = NULL;
496
497
  /* Allocate an array_header. */
498
0
  cmds = make_array(c->pool, 0, sizeof(char *));
499
500
  /* Add each command to the array, checking for invalid commands or
501
   * duplicates.
502
   */
503
0
  while ((cmd = get_cmd_from_list(&cmdlist)) != NULL) {
504
505
    /* Is the given command a valid one for this directive? */
506
0
    if (strcasecmp(cmd, C_APPE) != 0 &&
507
0
        strcasecmp(cmd, C_RETR) != 0 &&
508
0
        strcasecmp(cmd, C_STOR) != 0 &&
509
0
        strcasecmp(cmd, C_STOU) != 0) {
510
0
      pr_log_debug(DEBUG0, "invalid %s command: %s", name, cmd);
511
0
      errno = EINVAL;
512
0
      return -1;
513
0
    }
514
515
0
    *((char **) push_array(cmds)) = pstrdup(c->pool, cmd);
516
0
  }
517
518
  /* Terminate the array with a NULL. */
519
0
  *((char **) push_array(cmds)) = NULL;
520
521
  /* Store the array of commands in the config_rec. */
522
0
  c->argv[0] = (void *) cmds->elts;
523
524
0
  return 0;
525
0
}
526
527
0
static int transmit_normal(pool *p, char *buf, size_t bufsz) {
528
0
  int xerrno;
529
0
  long nread;
530
0
  size_t read_len;
531
0
  pr_error_t *err = NULL;
532
533
0
  read_len = bufsz;
534
0
  if (session.range_len > 0) {
535
0
    if (((off_t) read_len) > session.range_len) {
536
0
      read_len = session.range_len;
537
0
    }
538
0
  }
539
540
0
  nread = pr_fsio_read_with_error(p, retr_fh, buf, read_len, &err);
541
0
  xerrno = errno;
542
543
0
  while (nread < 0) {
544
0
    if (xerrno == EINTR) {
545
      /* Interrupted by signal; handle it, and try again. */
546
0
      errno = EINTR;
547
0
      pr_signals_handle();
548
549
0
      nread = pr_fsio_read_with_error(p, retr_fh, buf, read_len, &err);
550
0
      xerrno = errno;
551
0
      continue;
552
0
    }
553
554
0
    pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
555
0
    pr_error_set_why(err, pstrcat(p, "normal download of '", retr_fh->fh_path,
556
0
      "'", NULL));
557
558
0
    (void) pr_trace_msg("fileperms", 1, "RETR, user '%s' (UID %s, GID %s): "
559
0
      "error reading from '%s': %s", session.user,
560
0
      pr_uid2str(p, session.uid), pr_gid2str(p, session.gid),
561
0
      retr_fh->fh_path, strerror(xerrno));
562
563
0
    if (err != NULL) {
564
0
      pr_log_debug(DEBUG9, "%s", pr_error_strerror(err, 0));
565
0
      pr_error_destroy(err);
566
0
      err = NULL;
567
0
    }
568
569
0
    errno = xerrno;
570
0
    return -1;
571
0
  }
572
573
0
  if (nread == 0) {
574
0
    return 0;
575
0
  }
576
577
0
  return pr_data_xfer(buf, nread);
578
0
}
579
580
#ifdef HAVE_SENDFILE
581
static int transmit_sendfile(off_t data_len, off_t *data_offset,
582
0
    pr_sendfile_t *sent_len) {
583
0
  off_t send_len;
584
585
  /* We don't use sendfile() if:
586
   * - We're using bandwidth throttling.
587
   * - We're transmitting an ASCII file.
588
   * - We're using RFC2228 data channel protection
589
   * - We're using MODE Z compression
590
   * - There's no data left to transmit.
591
   * - UseSendfile is set to off.
592
   */
593
0
  if (pr_throttle_have_rate() ||
594
0
     !(session.xfer.file_size - data_len) ||
595
0
     (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) ||
596
0
     have_rfc2228_data || have_zmode ||
597
0
     !use_sendfile) {
598
599
0
    if (!xfer_logged_sendfile_decline_msg) {
600
0
      if (!use_sendfile) {
601
0
        pr_log_debug(DEBUG10, "declining use of sendfile due to UseSendfile "
602
0
          "configuration setting");
603
604
0
      } else if (pr_throttle_have_rate()) {
605
0
        pr_log_debug(DEBUG10, "declining use of sendfile due to TransferRate "
606
0
          "restrictions");
607
608
0
      } else if (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) {
609
0
        pr_log_debug(DEBUG10, "declining use of sendfile for ASCII data");
610
611
0
      } else if (have_rfc2228_data) {
612
0
        pr_log_debug(DEBUG10, "declining use of sendfile due to RFC2228 data "
613
0
          "channel protections");
614
615
0
      } else if (have_zmode) {
616
0
        pr_log_debug(DEBUG10, "declining use of sendfile due to MODE Z "
617
0
          "restrictions");
618
619
0
      } else {
620
0
        pr_log_debug(DEBUG10, "declining use of sendfile due to lack of data "
621
0
          "to transmit");
622
0
      }
623
624
0
      xfer_logged_sendfile_decline_msg = TRUE;
625
0
    }
626
627
0
    return 0;
628
0
  }
629
630
0
  pr_log_debug(DEBUG10, "using sendfile capability for transmitting data");
631
632
  /* Determine how many bytes to send using sendfile(2).  By default,
633
   * we want to send all of the remaining bytes.
634
   *
635
   * However, the admin may have configured either a length in bytes, or
636
   * a percentage, using the UseSendfile directive.  We will send the smaller
637
   * of the remaining size, or the length/percentage.
638
   */
639
640
0
  if (session.range_len > 0) {
641
0
    send_len = session.range_len;
642
643
0
  } else {
644
0
    send_len = session.xfer.file_size - data_len;
645
0
  }
646
647
0
  if (use_sendfile_len > 0 &&
648
0
      send_len > use_sendfile_len) {
649
0
    pr_log_debug(DEBUG10, "using sendfile with configured UseSendfile length "
650
0
      "(%" PR_LU " bytes)", (pr_off_t) use_sendfile_len);
651
0
    send_len = use_sendfile_len;
652
653
0
  } else if (use_sendfile_pct > 0.0) {
654
0
    off_t pct_len;
655
656
0
    pct_len = (off_t) (session.xfer.file_size * use_sendfile_pct);
657
0
    if (send_len > pct_len) {
658
0
      pr_log_debug(DEBUG10, "using sendfile with configured UseSendfile "
659
0
        "percentage %0.0f%% (%" PR_LU " bytes)", use_sendfile_pct * 100.0,
660
0
        (pr_off_t) pct_len);
661
0
      send_len = pct_len;
662
0
    }
663
0
  }
664
665
0
 retry:
666
0
  *sent_len = pr_data_sendfile(PR_FH_FD(retr_fh), data_offset, send_len);
667
668
0
  if (*sent_len == -1) {
669
0
    int xerrno = errno;
670
671
0
    switch (xerrno) {
672
0
      case EAGAIN:
673
0
      case EINTR:
674
0
        if (XFER_ABORTED) {
675
0
          pr_log_pri(PR_LOG_NOTICE, "sendfile transmission aborted: %s",
676
0
            strerror(xerrno));
677
0
          errno = xerrno;
678
0
          return -1;
679
0
        }
680
681
        /* Interrupted call, or the other side wasn't ready yet. */
682
0
        pr_signals_handle();
683
0
        goto retry;
684
685
0
      case EPIPE:
686
0
      case ECONNRESET:
687
0
      case ETIMEDOUT:
688
0
      case EHOSTUNREACH:
689
        /* Other side broke the connection. */
690
0
        break;
691
692
0
#ifdef ENOSYS
693
0
      case ENOSYS:
694
0
#endif /* ENOSYS */
695
696
0
#ifdef EOVERFLOW
697
0
      case EOVERFLOW:
698
0
#endif /* EOVERFLOW */
699
700
0
      case EINVAL:
701
        /* No sendfile support, apparently.  Try it the normal way. */
702
0
        return 0;
703
0
        break;
704
705
0
    default:
706
0
      pr_log_pri(PR_LOG_WARNING, "error using sendfile(): [%d] %s", xerrno,
707
0
        strerror(xerrno));
708
0
      errno = xerrno;
709
0
      return -1;
710
0
    }
711
0
  }
712
713
0
  return 1;
714
0
}
715
#endif /* HAVE_SENDFILE */
716
717
/* Note: the data_len and data_offset arguments are only for the benefit of
718
 * transmit_sendfile(), if sendfile support is enabled.  The transmit_normal()
719
 * function only needs/uses buf and bufsz.
720
 */
721
static long transmit_data(pool *p, off_t data_len, off_t *data_offset,
722
0
    char *buf, size_t bufsz) {
723
0
  long res;
724
0
  int xerrno = 0;
725
726
0
#ifdef HAVE_SENDFILE
727
0
  pr_sendfile_t sent_len;
728
0
  int ret;
729
0
#endif /* HAVE_SENDFILE */
730
731
0
  if (pr_inet_set_proto_cork(PR_NETIO_FD(session.d->outstrm), 1) < 0) {
732
0
    pr_log_pri(PR_LOG_NOTICE, "error corking socket fd %d: %s",
733
0
      PR_NETIO_FD(session.d->outstrm), strerror(errno));
734
0
  }
735
736
0
#ifdef HAVE_SENDFILE
737
0
  ret = transmit_sendfile(data_len, data_offset, &sent_len);
738
0
  if (ret > 0) {
739
    /* sendfile() was used, so return the value of sent_len. */
740
0
    res = (long) sent_len;
741
742
0
  } else if (ret == 0) {
743
    /* sendfile() should not be used for some reason, fallback to using
744
     * normal data transmission methods.
745
     */
746
0
    res = transmit_normal(p, buf, bufsz);
747
0
    xerrno = errno;
748
749
0
  } else {
750
    /* There was an error with sendfile(); do NOT attempt to re-send the
751
     * data using normal data transmission methods, unless the cause
752
     * of the error is one of an accepted few cases.
753
     */
754
0
# ifdef EOVERFLOW
755
0
    pr_log_debug(DEBUG10, "use of sendfile(2) failed due to %s (%d), "
756
0
      "falling back to normal data transmission", strerror(errno),
757
0
      errno);
758
0
    res = transmit_normal(p, buf, bufsz);
759
0
    xerrno = errno;
760
761
# else
762
    if (session.d != NULL) {
763
      (void) pr_inet_set_proto_cork(PR_NETIO_FD(session.d->outstrm), 0);
764
    }
765
766
    errno = EIO;
767
    res = -1;
768
# endif
769
0
  }
770
771
#else
772
  res = transmit_normal(p, buf, bufsz);
773
  xerrno = errno;
774
#endif /* HAVE_SENDFILE */
775
776
0
  if (session.d != NULL) {
777
    /* The session.d struct can become null after transmit_normal() if the
778
     * client aborts the transfer, thus we need to check for this.
779
     */
780
0
    if (pr_inet_set_proto_cork(PR_NETIO_FD(session.d->outstrm), 0) < 0) {
781
0
      if (errno != EINVAL) {
782
0
        pr_log_pri(PR_LOG_NOTICE, "error uncorking socket fd %d: %s",
783
0
          PR_NETIO_FD(session.d->outstrm), strerror(errno));
784
0
      }
785
0
    }
786
0
  }
787
788
0
  errno = xerrno;
789
0
  return res;
790
0
}
791
792
0
static void stor_chown(pool *p) {
793
0
  struct stat st;
794
0
  const char *xfer_path = NULL;
795
796
0
  if (session.xfer.xfer_type == STOR_HIDDEN) {
797
0
    xfer_path = session.xfer.path_hidden;
798
799
0
  } else {
800
0
    xfer_path = session.xfer.path;
801
0
  }
802
803
  /* session.fsgid defaults to -1, so chown(2) won't chgrp unless specifically
804
   * requested via GroupOwner.
805
   */
806
0
  if (session.fsuid != (uid_t) -1 &&
807
0
      xfer_path != NULL) {
808
0
    int res, xerrno = 0;
809
0
    pr_error_t *err = NULL;
810
811
0
    PRIVS_ROOT
812
0
    res = pr_fsio_lchown_with_error(p, xfer_path, session.fsuid, session.fsgid,
813
0
      &err);
814
0
    xerrno = errno;
815
0
    PRIVS_RELINQUISH
816
817
0
    if (res < 0) {
818
0
      pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 6);
819
0
      pr_error_set_why(err, pstrcat(p, "set UserOwner of '", xfer_path,
820
0
        "'", NULL));
821
822
0
      if (err != NULL) {
823
0
        pr_log_pri(PR_LOG_WARNING, "%s", pr_error_strerror(err, 0));
824
0
        pr_error_destroy(err);
825
0
        err = NULL;
826
827
0
      } else {
828
0
        pr_log_pri(PR_LOG_WARNING, "lchown(%s) as root failed: %s", xfer_path,
829
0
          strerror(xerrno));
830
0
      }
831
832
0
    } else {
833
0
      if (session.fsgid != (gid_t) -1) {
834
0
        pr_log_debug(DEBUG2, "root lchown(%s) to UID %s, GID %s successful",
835
0
          xfer_path, pr_uid2str(p, session.fsuid),
836
0
          pr_gid2str(p, session.fsgid));
837
838
0
      } else {
839
0
        pr_log_debug(DEBUG2, "root lchown(%s) to UID %s successful", xfer_path,
840
0
          pr_uid2str(p, session.fsuid));
841
0
      }
842
843
0
      pr_fs_clear_cache2(xfer_path);
844
0
      if (pr_fsio_stat(xfer_path, &st) < 0) {
845
0
        pr_log_debug(DEBUG0,
846
0
          "'%s' stat(2) error during root chmod: %s", xfer_path,
847
0
          strerror(errno));
848
0
      }
849
850
      /* The chmod happens after the chown because chown will remove
851
       * the S{U,G}ID bits on some files (namely, directories); the subsequent
852
       * chmod is used to restore those dropped bits.  This makes it
853
       * necessary to use root privs when doing the chmod as well (at least
854
       * in the case of chown'ing the file via root privs) in order to ensure
855
       * that the mode can be set (a file might be being "given away", and if
856
       * root privs aren't used, the chmod() will fail because the old owner/
857
       * session user doesn't have the necessary privileges to do so).
858
       */
859
0
      xerrno = 0;
860
0
      PRIVS_ROOT
861
0
      res = pr_fsio_chmod_with_error(p, xfer_path, st.st_mode, &err);
862
0
      xerrno = errno;
863
0
      PRIVS_RELINQUISH
864
865
0
      if (res < 0) {
866
0
        pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 5);
867
0
        pr_error_set_why(err, pstrcat(p, "restore SUID/SGID on '", xfer_path,
868
0
          "'", NULL));
869
870
0
        if (err != NULL) {
871
0
          pr_log_debug(DEBUG0, "%s", pr_error_strerror(err, 0));
872
0
          pr_error_destroy(err);
873
0
          err = NULL;
874
875
0
        } else {
876
0
          pr_log_debug(DEBUG0, "root chmod(%s) to %04o failed: %s", xfer_path,
877
0
            (unsigned int) st.st_mode, strerror(xerrno));
878
0
        }
879
880
0
      } else {
881
0
        pr_log_debug(DEBUG2, "root chmod(%s) to %04o successful", xfer_path,
882
0
          (unsigned int) st.st_mode);
883
0
      }
884
0
    }
885
886
0
  } else if (session.fsgid != (gid_t) -1 &&
887
0
             xfer_path != NULL) {
888
0
    register unsigned int i;
889
0
    int res, use_root_privs = TRUE, xerrno = 0;
890
0
    pr_error_t *err = NULL;
891
892
    /* Check if session.fsgid is in session.gids.  If not, use root privs. */
893
0
    for (i = 0; i < session.gids->nelts; i++) {
894
0
      gid_t *group_ids = session.gids->elts;
895
896
0
      if (group_ids[i] == session.fsgid) {
897
0
        use_root_privs = FALSE;
898
0
        break;
899
0
      }
900
0
    }
901
902
0
    if (use_root_privs) {
903
0
      PRIVS_ROOT
904
0
    }
905
906
0
    res = pr_fsio_lchown_with_error(p, xfer_path, (uid_t) -1, session.fsgid,
907
0
      &err);
908
0
    xerrno = errno;
909
910
0
    if (use_root_privs) {
911
0
      PRIVS_RELINQUISH
912
0
    }
913
914
0
    if (res < 0) {
915
0
      pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 9);
916
0
      pr_error_set_why(err, pstrcat(p, "set GroupOwner of '", xfer_path, "'",
917
0
        NULL));
918
919
0
      if (err != NULL) {
920
0
        pr_log_pri(PR_LOG_WARNING, "%s", pr_error_strerror(err, 0));
921
0
        pr_error_destroy(err);
922
0
        err = NULL;
923
924
0
      } else {
925
0
        pr_log_pri(PR_LOG_WARNING, "%slchown(%s) failed: %s",
926
0
          use_root_privs ? "root " : "", xfer_path, strerror(xerrno));
927
0
      }
928
929
0
    } else {
930
0
      pr_log_debug(DEBUG2, "%slchown(%s) to GID %s successful",
931
0
        use_root_privs ? "root " : "", xfer_path,
932
0
        pr_gid2str(p, session.fsgid));
933
934
0
      pr_fs_clear_cache2(xfer_path);
935
0
      if (pr_fsio_stat(xfer_path, &st) < 0) {
936
0
        pr_log_debug(DEBUG0,
937
0
          "'%s' stat(2) error during %schmod: %s", xfer_path,
938
0
          use_root_privs ? "root " : "", strerror(errno));
939
0
      }
940
941
0
      if (use_root_privs) {
942
0
        PRIVS_ROOT
943
0
      }
944
945
0
      res = pr_fsio_chmod_with_error(p, xfer_path, st.st_mode, &err);
946
0
      xerrno = errno;
947
948
0
      if (use_root_privs) {
949
0
        PRIVS_RELINQUISH
950
0
      }
951
952
0
      if (res < 0) {
953
0
        pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 8);
954
0
        pr_error_set_why(err, pstrcat(p, "restore SUID/SGID of '", xfer_path,
955
0
          "'", NULL));
956
957
0
        if (err != NULL) {
958
0
          pr_log_debug(DEBUG0, "%s", pr_error_strerror(err, 0));
959
0
          pr_error_destroy(err);
960
0
          err = NULL;
961
962
0
        } else {
963
0
          pr_log_debug(DEBUG0, "%schmod(%s) to %04o failed: %s",
964
0
            use_root_privs ? "root " : "", xfer_path, (unsigned int) st.st_mode,
965
0
            strerror(xerrno));
966
0
        }
967
0
      }
968
0
    }
969
0
  }
970
0
}
971
972
0
static void retr_abort(pool *p) {
973
  /* Isn't necessary to send anything here, just cleanup */
974
975
0
  if (retr_fh) {
976
0
    pr_fsio_close(retr_fh);
977
0
    retr_fh = NULL;
978
0
  }
979
980
0
  _log_transfer('o', 'i');
981
0
}
982
983
0
static void retr_complete(pool *p) {
984
0
  pr_fsio_close(retr_fh);
985
0
  retr_fh = NULL;
986
0
}
987
988
0
static void stor_abort(pool *p) {
989
0
  int res, xerrno = 0;
990
0
  pool *tmp_pool;
991
0
  pr_error_t *err = NULL;
992
0
  unsigned char *delete_stores = NULL;
993
994
0
  tmp_pool = make_sub_pool(p);
995
996
0
  if (stor_fh != NULL) {
997
0
    const char *fh_path;
998
999
    /* Note that FSIO close() will destroy the fh pool, including the path.
1000
     * So make a copy, for logging.
1001
     */
1002
0
    fh_path = pstrdup(tmp_pool, stor_fh->fh_path);
1003
1004
0
    res = pr_fsio_close_with_error(tmp_pool, stor_fh, &err);
1005
0
    xerrno = errno;
1006
1007
0
    if (res < 0) {
1008
0
      pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
1009
0
      pr_error_set_why(err, pstrcat(tmp_pool, "close file '", fh_path, "'",
1010
0
        NULL));
1011
1012
0
      if (err != NULL) {
1013
0
        pr_log_pri(PR_LOG_NOTICE, "%s", pr_error_strerror(err, 0));
1014
0
        pr_error_destroy(err);
1015
0
        err = NULL;
1016
1017
0
      } else {
1018
0
        pr_log_pri(PR_LOG_NOTICE, "notice: error closing '%s': %s", fh_path,
1019
0
          strerror(xerrno));
1020
0
      }
1021
1022
0
      errno = xerrno;
1023
0
    }
1024
1025
0
    stor_fh = NULL;
1026
0
  }
1027
1028
0
  delete_stores = get_param_ptr(CURRENT_CONF, "DeleteAbortedStores", FALSE);
1029
1030
0
  if (session.xfer.xfer_type == STOR_HIDDEN) {
1031
0
    if (delete_stores == NULL ||
1032
0
        *delete_stores == TRUE) {
1033
      /* If a hidden store was aborted, remove only hidden file, not real
1034
       * one.
1035
       */
1036
0
      if (session.xfer.path_hidden) {
1037
0
        pr_log_debug(DEBUG5, "removing aborted HiddenStores file '%s'",
1038
0
          session.xfer.path_hidden);
1039
1040
0
        res = pr_fsio_unlink_with_error(tmp_pool, session.xfer.path_hidden,
1041
0
          &err);
1042
0
        xerrno = errno;
1043
1044
0
        if (res < 0) {
1045
0
          pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 5);
1046
0
          pr_error_set_why(err, pstrcat(tmp_pool, "delete HiddenStores file '",
1047
0
            session.xfer.path_hidden, "'", NULL));
1048
1049
0
          if (xerrno != ENOENT) {
1050
0
            if (err != NULL) {
1051
0
              pr_log_debug(DEBUG0, "%s", pr_error_strerror(err, 0));
1052
1053
0
            } else {
1054
0
              pr_log_debug(DEBUG0, "error deleting HiddenStores file '%s': %s",
1055
0
                session.xfer.path_hidden, strerror(xerrno));
1056
0
            }
1057
0
          }
1058
1059
0
          pr_error_destroy(err);
1060
0
          err = NULL;
1061
0
        }
1062
0
      }
1063
0
    }
1064
1065
0
  } else if (session.xfer.path != NULL) {
1066
0
    if (delete_stores != NULL &&
1067
0
        *delete_stores == TRUE) {
1068
0
      pr_log_debug(DEBUG5, "removing aborted file '%s'", session.xfer.path);
1069
1070
0
      res = pr_fsio_unlink_with_error(tmp_pool, session.xfer.path, &err);
1071
0
      xerrno = errno;
1072
1073
0
      if (res < 0) {
1074
0
        pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
1075
0
        pr_error_set_why(err, pstrcat(tmp_pool, "delete aborted file '",
1076
0
          session.xfer.path, "'", NULL));
1077
1078
0
        if (xerrno != ENOENT) {
1079
0
          if (err != NULL) {
1080
0
            pr_log_debug(DEBUG0, "%s", pr_error_strerror(err, 0));
1081
0
            pr_error_destroy(err);
1082
0
            err = NULL;
1083
1084
0
          } else {
1085
0
            pr_log_debug(DEBUG0, "error deleting aborted file '%s': %s",
1086
0
              session.xfer.path, strerror(xerrno));
1087
0
          }
1088
0
        }
1089
0
      }
1090
0
    }
1091
0
  }
1092
1093
0
  destroy_pool(tmp_pool);
1094
0
  _log_transfer('i', 'i');
1095
0
}
1096
1097
0
static int stor_complete(pool *p) {
1098
0
  int res, xerrno = 0;
1099
0
  pool *tmp_pool;
1100
0
  pr_error_t *err = NULL;
1101
0
  const char *fh_path;
1102
1103
0
  tmp_pool = make_sub_pool(p);
1104
1105
  /* Note that FSIO close() will destroy the fh pool, including the path.
1106
   * So make a copy, for logging.
1107
   */
1108
0
  fh_path = pstrdup(tmp_pool, stor_fh->fh_path);
1109
1110
0
  res = pr_fsio_close_with_error(tmp_pool, stor_fh, &err);
1111
0
  xerrno = errno;
1112
1113
0
  if (res < 0) {
1114
0
    pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
1115
0
    pr_error_set_why(err, pstrcat(tmp_pool, "close uploaded file '", fh_path,
1116
0
      "'", NULL));
1117
1118
0
    if (err != NULL) {
1119
0
      pr_log_pri(PR_LOG_NOTICE, "%s", pr_error_strerror(err, 0));
1120
0
      pr_error_destroy(err);
1121
0
      err = NULL;
1122
1123
0
    } else {
1124
0
      pr_log_pri(PR_LOG_NOTICE, "notice: error closing '%s': %s", fh_path,
1125
0
        strerror(xerrno));
1126
0
    }
1127
1128
    /* We will unlink failed writes, but only if it's a HiddenStores file.
1129
     * Other files will need to be explicitly deleted/removed by the client.
1130
     */
1131
0
    if (session.xfer.xfer_type == STOR_HIDDEN) {
1132
0
      if (session.xfer.path_hidden) {
1133
0
        pr_log_debug(DEBUG5, "failed to close HiddenStores file '%s', removing",
1134
0
          session.xfer.path_hidden);
1135
1136
0
        res = pr_fsio_unlink_with_error(tmp_pool, session.xfer.path_hidden,
1137
0
          &err);
1138
0
        xerrno = errno;
1139
1140
0
        if (res < 0) {
1141
0
          pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
1142
0
          pr_error_set_why(err, pstrcat(tmp_pool, "close HiddenStores file '",
1143
0
            session.xfer.path_hidden, "'", NULL));
1144
1145
0
          if (xerrno != ENOENT) {
1146
0
            if (err != NULL) {
1147
0
              pr_log_debug(DEBUG0, "%s", pr_error_strerror(err, 0));
1148
1149
0
            } else {
1150
0
              pr_log_debug(DEBUG0, "error deleting HiddenStores file '%s': %s",
1151
0
                session.xfer.path_hidden, strerror(xerrno));
1152
0
            }
1153
0
          }
1154
1155
0
          pr_error_destroy(err);
1156
0
          err = NULL;
1157
0
        }
1158
0
      }
1159
0
    }
1160
1161
0
    errno = xerrno;
1162
0
    res = -1;
1163
0
  }
1164
1165
0
  destroy_pool(tmp_pool);
1166
0
  stor_fh = NULL;
1167
0
  return res;
1168
0
}
1169
1170
static int get_hidden_store_path(cmd_rec *cmd, const char *path,
1171
0
    const char *prefix, const char *suffix) {
1172
0
  const char *c = NULL;
1173
0
  char *hidden_path, *parent_dir = NULL;
1174
0
  int dotcount = 0, found_slash = FALSE, basenamestart = 0, maxlen;
1175
1176
  /* We have to also figure out the temporary hidden file name for receiving
1177
   * this transfer.  Length is +(N+M) due to prepended prefix and suffix.
1178
   */
1179
1180
  /* Figure out where the basename starts */
1181
0
  for (c = path; *c; ++c) {
1182
1183
0
    if (*c == '/') {
1184
0
      found_slash = TRUE;
1185
0
      basenamestart = dotcount = 0;
1186
1187
0
    } else if (*c == '.') {
1188
0
      ++dotcount;
1189
1190
      /* Keep track of leading dots, ... is normal, . and .. are special.
1191
       * So if we exceed ".." it becomes a normal file. Retroactively consider
1192
       * this the possible start of the basename.
1193
       */
1194
0
      if ((dotcount > 2) &&
1195
0
          !basenamestart) {
1196
0
        basenamestart = ((unsigned long) c - (unsigned long) path) - dotcount;
1197
0
      }
1198
1199
0
    } else {
1200
1201
      /* We found a nonslash, nondot character; if this is the first time
1202
       * we found one since the last slash, remember this as the possible
1203
       * start of the basename.
1204
       */
1205
0
      if (!basenamestart) {
1206
0
        basenamestart = ((unsigned long) c - (unsigned long) path) - dotcount;
1207
0
      }
1208
0
    }
1209
0
  }
1210
1211
0
  if (!basenamestart) {
1212
0
    session.xfer.xfer_type = STOR_DEFAULT;
1213
1214
0
    pr_log_debug(DEBUG6, "could not determine HiddenStores path for '%s'",
1215
0
      path);
1216
1217
    /* This probably shouldn't happen */
1218
0
    pr_response_add_err(R_451, _("%s: Bad file name"), path);
1219
0
    errno = EINVAL;
1220
0
    return -1;
1221
0
  }
1222
1223
  /* Add N+M for the prefix and suffix characters, plus one for a terminating
1224
   * NUL.
1225
   */
1226
0
  maxlen = strlen(prefix) + strlen(path) + strlen(suffix) + 1;
1227
1228
0
  if (maxlen > PR_TUNABLE_PATH_MAX) {
1229
0
    session.xfer.xfer_type = STOR_DEFAULT;
1230
1231
0
    pr_log_pri(PR_LOG_NOTICE, "making path '%s' a hidden path exceeds max "
1232
0
      "path length (%u)", path, PR_TUNABLE_PATH_MAX);
1233
1234
    /* This probably shouldn't happen */
1235
0
    pr_response_add_err(R_451, _("%s: File name too long"), path);
1236
0
    errno = EPERM;
1237
0
    return -1;
1238
0
  }
1239
1240
0
  if (pr_table_add(cmd->notes, "mod_xfer.store-hidden-path", NULL, 0) < 0) {
1241
0
    if (errno != EEXIST) {
1242
0
      pr_log_pri(PR_LOG_NOTICE,
1243
0
        "notice: error adding 'mod_xfer.store-hidden-path': %s",
1244
0
        strerror(errno));
1245
0
    }
1246
0
  }
1247
1248
0
  if (found_slash == FALSE) {
1249
1250
    /* Simple local file name */
1251
0
    hidden_path = pstrcat(cmd->tmp_pool, prefix, path, suffix, NULL);
1252
1253
0
    pr_log_debug(DEBUG2, "HiddenStore: local path, will rename %s to %s",
1254
0
      hidden_path, path);
1255
1256
0
  } else {
1257
1258
    /* Complex relative path or absolute path */
1259
0
    hidden_path = pstrndup(cmd->pool, path, maxlen);
1260
0
    hidden_path[basenamestart] = '\0';
1261
1262
0
    hidden_path = pstrcat(cmd->pool, hidden_path, prefix,
1263
0
      path + basenamestart, suffix, NULL);
1264
1265
0
    pr_log_debug(DEBUG2, "HiddenStore: complex path, will rename %s to %s",
1266
0
      hidden_path, path);
1267
0
  }
1268
1269
0
  pr_fs_clear_cache2(hidden_path);
1270
0
  if (file_mode2(cmd->tmp_pool, hidden_path)) {
1271
0
    session.xfer.xfer_type = STOR_DEFAULT;
1272
1273
0
    pr_log_debug(DEBUG3, "HiddenStore path '%s' already exists",
1274
0
      hidden_path);
1275
1276
0
    pr_response_add_err(R_550, _("%s: Temporary hidden file %s already exists"),
1277
0
      cmd->arg, hidden_path);
1278
0
    errno = EEXIST;
1279
0
    return -1;
1280
0
  }
1281
1282
0
  if (pr_table_set(cmd->notes, "mod_xfer.store-hidden-path",
1283
0
      hidden_path, 0) < 0) {
1284
0
    pr_log_pri(PR_LOG_NOTICE,
1285
0
      "notice: error setting 'mod_xfer.store-hidden-path': %s",
1286
0
      strerror(errno));
1287
0
  }
1288
1289
  /* Only use the O_EXCL open(2) flag if the path is NOT on an NFS-mounted
1290
   * filesystem (see Bug#3874).
1291
   */
1292
0
  if (found_slash == FALSE) {
1293
0
    parent_dir = "./";
1294
1295
0
  } else {
1296
0
    parent_dir = pstrndup(cmd->tmp_pool, path, basenamestart);
1297
0
  }
1298
1299
0
  if (pr_fs_is_nfs(parent_dir) == TRUE) {
1300
0
    if (pr_table_add(cmd->notes, "mod_xfer.store-hidden-nfs",
1301
0
        pstrdup(cmd->pool, "1"), 0) < 0) {
1302
0
      pr_log_pri(PR_LOG_NOTICE,
1303
0
        "notice: error adding 'mod_xfer.store-hidden-nfs' note: %s",
1304
0
        strerror(errno));
1305
0
    }
1306
0
  }
1307
1308
0
  session.xfer.xfer_type = STOR_HIDDEN;
1309
0
  return 0;
1310
0
}
1311
1312
0
MODRET xfer_opts_rest(cmd_rec *cmd) {
1313
0
  register unsigned int i;
1314
0
  char *method, *xfer_cmd;
1315
0
  unsigned char *authenticated;
1316
1317
0
  authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
1318
0
  if (authenticated == NULL ||
1319
0
      *authenticated == FALSE) {
1320
0
    pr_response_add_err(R_501, _("Please login with USER and PASS"));
1321
1322
0
    pr_cmd_set_errno(cmd, EPERM);
1323
0
    errno = EPERM;
1324
0
    return PR_ERROR(cmd);
1325
0
  }
1326
1327
0
  method = pstrdup(cmd->tmp_pool, cmd->argv[0]);
1328
1329
  /* Convert underscores to spaces in the method name, for prettier logging. */
1330
0
  for (i = 0; method[i]; i++) {
1331
0
    if (method[i] == '_') {
1332
0
      method[i] = ' ';
1333
0
    }
1334
0
  }
1335
1336
0
  if (cmd->argc != 2) {
1337
0
    pr_response_add_err(R_501, _("'%s' not understood"), method);
1338
1339
0
    pr_cmd_set_errno(cmd, EINVAL);
1340
0
    errno = EINVAL;
1341
0
    return PR_ERROR(cmd);
1342
0
  }
1343
1344
0
  xfer_cmd = cmd->argv[1];
1345
0
  if (strcasecmp(xfer_cmd, C_RETR) == 0) {
1346
0
    unsigned char *allow_restart = NULL;
1347
1348
    /* Do we allow resumed downloads? */
1349
0
    allow_restart = get_param_ptr(main_server->conf, "AllowRetrieveRestart",
1350
0
      FALSE);
1351
0
    if (allow_restart == NULL ||
1352
0
        *allow_restart == TRUE) {
1353
0
      pr_response_add(R_200, "%s", _("REST RETR allowed"));
1354
0
      return PR_HANDLED(cmd);
1355
0
    }
1356
1357
0
    pr_response_add_err(R_451, "%s", _("REST RETR not allowed"));
1358
0
    pr_cmd_set_errno(cmd, EPERM);
1359
0
    errno = EPERM;
1360
0
    return PR_ERROR(cmd);
1361
0
  }
1362
1363
0
  if (strcasecmp(xfer_cmd, C_STOR) == 0) {
1364
0
    unsigned char *allow_restart = NULL;
1365
1366
    /* Do we allow resumed uploads? */
1367
0
    allow_restart = get_param_ptr(main_server->conf, "AllowStoreRestart",
1368
0
      FALSE);
1369
0
    if (allow_restart != NULL &&
1370
0
        *allow_restart == TRUE) {
1371
0
      pr_response_add(R_200, "%s", _("REST STOR allowed"));
1372
0
      return PR_HANDLED(cmd);
1373
0
    }
1374
1375
0
    pr_response_add_err(R_451, "%s", _("REST STOR not allowed"));
1376
0
    pr_cmd_set_errno(cmd, EPERM);
1377
0
    errno = EPERM;
1378
0
    return PR_ERROR(cmd);
1379
0
  }
1380
1381
  /* Otherwise, it's an OPTS REST query we do not support. */
1382
0
  pr_response_add_err(R_501, _("'%s' not understood"), method);
1383
1384
0
  pr_cmd_set_errno(cmd, EINVAL);
1385
0
  errno = EINVAL;
1386
0
  return PR_ERROR(cmd);
1387
0
}
1388
1389
0
MODRET xfer_post_prot(cmd_rec *cmd) {
1390
0
  CHECK_CMD_ARGS(cmd, 2);
1391
1392
0
  if (strcmp(cmd->argv[1], "C") != 0) {
1393
0
    have_rfc2228_data = TRUE;
1394
1395
0
  } else {
1396
0
    have_rfc2228_data = FALSE;
1397
0
  }
1398
1399
0
  return PR_DECLINED(cmd);
1400
0
}
1401
1402
0
MODRET xfer_post_mode(cmd_rec *cmd) {
1403
0
  CHECK_CMD_ARGS(cmd, 2);
1404
1405
0
  if (strcmp(cmd->argv[1], "Z") == 0) {
1406
0
    have_zmode = TRUE;
1407
1408
0
  } else {
1409
0
    have_zmode = FALSE;
1410
0
  }
1411
1412
0
  return PR_DECLINED(cmd);
1413
0
}
1414
1415
/* This is a PRE_CMD handler that checks security, etc, and places the full
1416
 * filename to receive in cmd->notes, under the key 'mod_xfer.store-path'.
1417
 * Note that we CANNOT use cmd->tmp_pool for this, as tmp_pool only lasts for
1418
 * the duration of this function.
1419
 */
1420
0
MODRET xfer_pre_stor(cmd_rec *cmd) {
1421
0
  char *decoded_path, *path;
1422
0
  mode_t fmode = (mode_t) 0;
1423
0
  unsigned char *allow_overwrite = NULL, *allow_restart = NULL;
1424
0
  config_rec *c;
1425
0
  int res, is_file = FALSE;
1426
1427
0
  if (cmd->argc < 2) {
1428
0
    pr_response_add_err(R_500, _("'%s' not understood"),
1429
0
      pr_cmd_get_displayable_str(cmd, NULL));
1430
1431
0
    pr_cmd_set_errno(cmd, EINVAL);
1432
0
    errno = EINVAL;
1433
0
    return PR_ERROR(cmd);
1434
0
  }
1435
1436
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
1437
0
    FSIO_DECODE_FL_TELL_ERRORS);
1438
0
  if (decoded_path == NULL) {
1439
0
    int xerrno = errno;
1440
1441
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
1442
0
      strerror(xerrno));
1443
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
1444
0
      cmd->arg);
1445
1446
0
    pr_cmd_set_errno(cmd, xerrno);
1447
0
    errno = xerrno;
1448
0
    return PR_ERROR(cmd);
1449
0
  }
1450
1451
0
  pr_fs_clear_cache2(decoded_path);
1452
0
  path = dir_best_path(cmd->tmp_pool, decoded_path);
1453
1454
0
  if (path == NULL ||
1455
0
      !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
1456
0
    int xerrno = errno;
1457
1458
0
    pr_log_debug(DEBUG8, "%s %s denied by <Limit> configuration",
1459
0
      (char *) cmd->argv[0], cmd->arg);
1460
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1461
1462
0
    pr_cmd_set_errno(cmd, xerrno);
1463
0
    errno = xerrno;
1464
0
    return PR_ERROR(cmd);
1465
0
  }
1466
1467
0
  res = pr_filter_allow_path(CURRENT_CONF, path);
1468
0
  switch (res) {
1469
0
    case 0:
1470
0
      break;
1471
1472
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
1473
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
1474
0
        (char *) cmd->argv[0], path);
1475
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
1476
1477
0
      pr_cmd_set_errno(cmd, EPERM);
1478
0
      errno = EPERM;
1479
0
      return PR_ERROR(cmd);
1480
1481
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
1482
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
1483
0
        (char *) cmd->argv[0], path);
1484
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
1485
1486
0
      pr_cmd_set_errno(cmd, EPERM);
1487
0
      errno = EPERM;
1488
0
      return PR_ERROR(cmd);
1489
0
  }
1490
1491
0
  if (xfer_check_limit(cmd) < 0) {
1492
0
    pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
1493
1494
0
    pr_cmd_set_errno(cmd, EPERM);
1495
0
    errno = EPERM;
1496
0
    return PR_ERROR(cmd);
1497
0
  }
1498
1499
0
  fmode = file_mode2(cmd->tmp_pool, path);
1500
1501
0
  allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE);
1502
1503
0
  if (fmode != 0) {
1504
0
    if (session.xfer.xfer_type != STOR_APPEND &&
1505
0
        (!allow_overwrite || *allow_overwrite == FALSE)) {
1506
0
      pr_log_debug(DEBUG6, "AllowOverwrite denied permission for %s", cmd->arg);
1507
0
      pr_response_add_err(R_550, _("%s: Overwrite permission denied"), cmd->arg);
1508
1509
0
      pr_cmd_set_errno(cmd, EACCES);
1510
0
      errno = EACCES;
1511
0
      return PR_ERROR(cmd);
1512
0
    }
1513
1514
0
    if (S_ISREG(fmode) ||
1515
0
        S_ISFIFO(fmode)) {
1516
0
      is_file = TRUE;
1517
1518
0
    } else {
1519
      /* Make an exception for the non-regular /dev/null file.  This will allow
1520
       * network link testing by uploading as much data as necessary directly
1521
       * to /dev/null.
1522
       *
1523
       * On Linux, allow another exception for /dev/full; this is useful for
1524
       * tests which want to simulate running out-of-space scenarios.
1525
       */
1526
0
      if (strcasecmp(path, "/dev/null") == 0
1527
0
#ifdef LINUX
1528
0
          || strcasecmp(path, "/dev/full") == 0
1529
0
#endif
1530
0
        ) {
1531
0
        is_file = TRUE;
1532
0
      }
1533
1534
0
      if (is_file == FALSE &&
1535
0
          S_ISLNK(fmode) &&
1536
0
          (xfer_opts & PR_XFER_OPT_ALLOW_SYMLINK_UPLOAD)) {
1537
        /* We are allowing uploading to symlinks. */
1538
0
        is_file = TRUE;
1539
0
      }
1540
0
    }
1541
1542
0
  } else {
1543
    /* If we cannot discover the mode, assume it is a file. */
1544
0
    is_file = TRUE;
1545
0
  }
1546
1547
0
  if (is_file == FALSE) {
1548
0
    pr_response_add_err(R_550, _("%s: Not a regular file"), cmd->arg);
1549
1550
    /* Deliberately use EISDIR for anything non-file (e.g. directories). */
1551
0
    pr_cmd_set_errno(cmd, EISDIR);
1552
0
    errno = EISDIR;
1553
0
    return PR_ERROR(cmd);
1554
0
  }
1555
1556
  /* If restarting, check permissions on this directory, if
1557
   * AllowStoreRestart is set, permit it
1558
   */
1559
0
  allow_restart = get_param_ptr(CURRENT_CONF, "AllowStoreRestart", FALSE);
1560
1561
0
  if (fmode &&
1562
0
     ((session.restart_pos > 0 || session.range_len > 0) ||
1563
0
      (session.xfer.xfer_type == STOR_APPEND)) &&
1564
0
     (!allow_restart || *allow_restart == FALSE)) {
1565
1566
0
    pr_log_debug(DEBUG6, "AllowStoreRestart denied permission for %s",
1567
0
      cmd->arg);
1568
0
    pr_response_add_err(R_451, _("%s: Append/Restart not permitted, try again"),
1569
0
      cmd->arg);
1570
0
    session.restart_pos = 0L;
1571
0
    session.xfer.xfer_type = STOR_DEFAULT;
1572
1573
0
    pr_cmd_set_errno(cmd, EPERM);
1574
0
    errno = EPERM;
1575
0
    return PR_ERROR(cmd);
1576
0
  }
1577
1578
  /* Reject APPE preceded by RANG. */
1579
0
  if (session.xfer.xfer_type == STOR_APPEND &&
1580
0
      session.range_len > 0) {
1581
0
    pr_response_add_err(R_550, _("APPE incompatible with RANG"));
1582
0
    pr_cmd_set_errno(cmd, EPERM);
1583
0
    errno = EPERM;
1584
0
    return PR_ERROR(cmd);
1585
0
  }
1586
1587
  /* If the file exists, add a note indicating that it is being modified. */
1588
0
  if (fmode) {
1589
    /* Clear any existing key in the notes. */
1590
0
    (void) pr_table_remove(cmd->notes, "mod_xfer.file-modified", NULL);
1591
1592
0
    if (pr_table_add(cmd->notes, "mod_xfer.file-modified",
1593
0
        pstrdup(cmd->pool, "true"), 0) < 0) {
1594
0
      if (errno != EEXIST) {
1595
0
        pr_log_pri(PR_LOG_NOTICE,
1596
0
          "notice: error adding 'mod_xfer.file-modified' note: %s",
1597
0
          strerror(errno));
1598
0
      }
1599
0
    }
1600
0
  }
1601
1602
  /* Otherwise everything is good */
1603
0
  if (pr_table_add(cmd->notes, "mod_xfer.store-path",
1604
0
      pstrdup(cmd->pool, path), 0) < 0) {
1605
0
    if (errno != EEXIST) {
1606
0
      pr_log_pri(PR_LOG_NOTICE,
1607
0
        "notice: error adding 'mod_xfer.store-path': %s", strerror(errno));
1608
0
    }
1609
0
  }
1610
1611
0
  c = find_config(CURRENT_CONF, CONF_PARAM, "HiddenStores", FALSE);
1612
0
  if (c != NULL &&
1613
0
      *((int *) c->argv[0]) == TRUE) {
1614
1615
    /* If we're using HiddenStores, then RANG/REST won't work. */
1616
0
    if (session.restart_pos > 0 ||
1617
0
        session.range_len > 0) {
1618
0
      int used_rest = TRUE;
1619
1620
0
      if (session.range_len > 0) {
1621
0
        used_rest = FALSE;
1622
0
      }
1623
1624
0
      pr_log_debug(DEBUG9, "HiddenStore in effect, refusing %s upload",
1625
0
        used_rest ? "restarted" : "range");
1626
0
      pr_response_add_err(R_501,
1627
0
        _("%s not compatible with server configuration"),
1628
0
        used_rest ? C_REST : C_RANG);
1629
1630
0
      pr_cmd_set_errno(cmd, EPERM);
1631
0
      errno = EPERM;
1632
0
      return PR_ERROR(cmd);
1633
0
    }
1634
1635
    /* For Bug#3598, we rejected any APPE command when HiddenStores are in
1636
     * effect (for good reasons).
1637
     *
1638
     * However, for Bug#4144, we're relaxing that policy.  Instead of rejecting
1639
     * the APPE command, we accept that command, but we disable the HiddenStores
1640
     * functionality.
1641
     */
1642
0
    if (session.xfer.xfer_type != STOR_APPEND) {
1643
0
      const char *prefix, *suffix;
1644
1645
0
      prefix = c->argv[1];
1646
0
      suffix = c->argv[2];
1647
1648
      /* Substitute the %P variable for the PID, if present. */
1649
0
      if (strstr(prefix, "%P") != NULL) {
1650
0
        char pid_buf[32];
1651
1652
0
        memset(pid_buf, '\0', sizeof(pid_buf));
1653
0
        pr_snprintf(pid_buf, sizeof(pid_buf)-1, "%lu",
1654
0
          (unsigned long) session.pid);
1655
0
        prefix = sreplace(cmd->pool, prefix, "%P", pid_buf, NULL);
1656
0
      }
1657
1658
0
      if (strstr(suffix, "%P") != NULL) {
1659
0
        char pid_buf[32];
1660
1661
0
        memset(pid_buf, '\0', sizeof(pid_buf));
1662
0
        pr_snprintf(pid_buf, sizeof(pid_buf)-1, "%lu",
1663
0
          (unsigned long) session.pid);
1664
0
        suffix = sreplace(cmd->pool, suffix, "%P", pid_buf, NULL);
1665
0
      }
1666
1667
0
      if (get_hidden_store_path(cmd, path, prefix, suffix) < 0) {
1668
0
        int xerrno = errno;
1669
1670
0
        pr_cmd_set_errno(cmd, xerrno);
1671
0
        errno = xerrno;
1672
0
        return PR_ERROR(cmd);
1673
0
      }
1674
1675
0
    } else {
1676
0
      pr_log_debug(DEBUG9,
1677
0
        "HiddenStores in effect for APPE, ignoring HiddenStores");
1678
0
    }
1679
0
  }
1680
1681
0
  return PR_HANDLED(cmd);
1682
0
}
1683
1684
/* xfer_pre_stou() is a PRE_CMD handler that changes the uploaded filename
1685
 * to a unique one, after making the requisite security and authorization
1686
 * checks.
1687
 */
1688
0
MODRET xfer_pre_stou(cmd_rec *cmd) {
1689
0
  config_rec *c = NULL;
1690
0
  char *prefix = "ftp", *filename = NULL;
1691
0
  int stou_fd;
1692
0
  mode_t mode;
1693
0
  unsigned char *allow_overwrite = NULL;
1694
1695
0
  session.xfer.xfer_type = STOR_DEFAULT;
1696
1697
  /* Some FTP clients are "broken" in that they will send a filename
1698
   * along with STOU.  Technically this violates RFC959, but for now, just
1699
   * ignore that filename.  Stupid client implementors.
1700
   */
1701
1702
0
  if (cmd->argc > 2) {
1703
0
    pr_response_add_err(R_500, _("'%s' not understood"),
1704
0
      pr_cmd_get_displayable_str(cmd, NULL));
1705
1706
0
    pr_cmd_set_errno(cmd, EINVAL);
1707
0
    errno = EINVAL;
1708
0
    return PR_ERROR(cmd);
1709
0
  }
1710
1711
0
  if (xfer_check_limit(cmd) < 0) {
1712
0
    pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
1713
1714
0
    pr_cmd_set_errno(cmd, EPERM);
1715
0
    errno = EPERM;
1716
0
    return PR_ERROR(cmd);
1717
0
  }
1718
1719
  /* Watch for STOU preceded by REST, which makes no sense.  Similarly
1720
   * for STOU preceded by RANG.
1721
   */
1722
0
  if (session.restart_pos > 0 ||
1723
0
      session.range_len > 0) {
1724
1725
0
    if (session.restart_pos > 0) {
1726
0
      pr_response_add_err(R_550, _("STOU incompatible with REST"));
1727
1728
0
    } else {
1729
0
      pr_response_add_err(R_550, _("STOU incompatible with RANG"));
1730
0
    }
1731
1732
0
    pr_cmd_set_errno(cmd, EPERM);
1733
0
    errno = EPERM;
1734
0
    return PR_ERROR(cmd);
1735
0
  }
1736
1737
  /* Generate the filename to be stored, depending on the configured
1738
   * unique filename prefix.
1739
   */
1740
0
  c = find_config(CURRENT_CONF, CONF_PARAM, "StoreUniquePrefix", FALSE);
1741
0
  if (c != NULL) {
1742
0
    prefix = c->argv[0];
1743
0
  }
1744
1745
  /* Now, construct the unique filename using the cmd_rec's pool, the
1746
   * prefix, and mkstemp().
1747
   */
1748
0
  filename = pstrcat(cmd->pool, prefix, "XXXXXX", NULL);
1749
1750
0
  stou_fd = mkstemp(filename);
1751
0
  if (stou_fd < 0) {
1752
0
    int xerrno = errno;
1753
1754
0
    pr_log_pri(PR_LOG_WARNING, "error: unable to use mkstemp(): %s",
1755
0
      strerror(xerrno));
1756
1757
    /* If we can't guarantee a unique filename, refuse the command. */
1758
0
    pr_response_add_err(R_450, _("%s: unable to generate unique filename"),
1759
0
      (char *) cmd->argv[0]);
1760
1761
0
    pr_cmd_set_errno(cmd, xerrno);
1762
0
    errno = xerrno;
1763
0
    return PR_ERROR(cmd);
1764
0
  }
1765
1766
0
  cmd->arg = filename;
1767
1768
  /* Close the unique file.  This introduces a small race condition
1769
   * between the time this function returns, and the STOU CMD handler
1770
   * opens the unique file, but this may have to do, as closing that
1771
   * race would involve some major restructuring.
1772
   */
1773
0
  (void) close(stou_fd);
1774
1775
0
  filename = dir_best_path(cmd->tmp_pool, cmd->arg);
1776
1777
0
  if (filename == NULL ||
1778
0
      !dir_check(cmd->tmp_pool, cmd, cmd->group, filename, NULL)) {
1779
0
    int xerrno = errno;
1780
1781
    /* Do not forget to delete the file created by mkstemp(3) if there is
1782
     * an error.
1783
     */
1784
0
    (void) pr_fsio_unlink(cmd->arg);
1785
0
    pr_log_debug(DEBUG8, "%s %s denied by <Limit> configuration",
1786
0
          (char *) cmd->argv[0], cmd->arg);
1787
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1788
1789
0
    pr_cmd_set_errno(cmd, xerrno);
1790
0
    errno = xerrno;
1791
0
    return PR_ERROR(cmd);
1792
0
  }
1793
1794
0
  mode = file_mode2(cmd->tmp_pool, filename);
1795
1796
  /* Note: this case should never happen: how one can be appending to
1797
   * a supposedly unique filename?  Should probably be removed...
1798
   */
1799
0
  allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE);
1800
1801
0
  if (mode && session.xfer.xfer_type != STOR_APPEND &&
1802
0
      (!allow_overwrite || *allow_overwrite == FALSE)) {
1803
0
    pr_log_debug(DEBUG6, "AllowOverwrite denied permission for %s", cmd->arg);
1804
0
    pr_response_add_err(R_550, _("%s: Overwrite permission denied"), cmd->arg);
1805
1806
0
    pr_cmd_set_errno(cmd, EACCES);
1807
0
    errno = EACCES;
1808
0
    return PR_ERROR(cmd);
1809
0
  }
1810
1811
  /* Not likely to _not_ be a regular file, but just to be certain... */
1812
0
  if (mode &&
1813
0
      !S_ISREG(mode)) {
1814
0
    (void) pr_fsio_unlink(cmd->arg);
1815
0
    pr_response_add_err(R_550, _("%s: Not a regular file"), cmd->arg);
1816
1817
    /* Deliberately use EISDIR for anything non-file (e.g. directories). */
1818
0
    pr_cmd_set_errno(cmd, EISDIR);
1819
0
    errno = EISDIR;
1820
0
    return PR_ERROR(cmd);
1821
0
  }
1822
1823
  /* Otherwise everything is good */
1824
0
  if (pr_table_add(cmd->notes, "mod_xfer.store-path",
1825
0
      pstrdup(cmd->pool, filename), 0) < 0) {
1826
0
    if (errno != EEXIST) {
1827
0
      pr_log_pri(PR_LOG_NOTICE,
1828
0
        "notice: error adding 'mod_xfer.store-path': %s", strerror(errno));
1829
0
    }
1830
0
  }
1831
1832
0
  session.xfer.xfer_type = STOR_UNIQUE;
1833
0
  return PR_HANDLED(cmd);
1834
0
}
1835
1836
0
MODRET xfer_post_stor(cmd_rec *cmd) {
1837
0
  const char *path;
1838
1839
0
  path = pr_table_get(cmd->notes, "mod_xfer.store-path", NULL);
1840
0
  if (path != NULL) {
1841
0
    struct stat st;
1842
1843
0
    if (pr_fsio_stat(path, &st) == 0) {
1844
0
      off_t *file_size;
1845
1846
0
      file_size = palloc(cmd->pool, sizeof(off_t));
1847
0
      *file_size = st.st_size;
1848
0
      (void) pr_table_add(cmd->notes, "mod_xfer.file-size", file_size,
1849
0
        sizeof(off_t));
1850
0
    }
1851
0
  }
1852
1853
0
  return PR_DECLINED(cmd);
1854
0
}
1855
1856
/* xfer_post_stou() is a POST_CMD handler that changes the mode of the
1857
 * STOU file from 0600, which is what mkstemp() makes it, to 0666 (modulo
1858
 * Umask), the default for files uploaded via STOR.  This is to prevent users
1859
 * from being surprised.
1860
 */
1861
0
MODRET xfer_post_stou(cmd_rec *cmd) {
1862
0
  mode_t mask, perms, *umask_setting;
1863
0
  struct stat st;
1864
1865
  /* mkstemp(3) creates a file with 0600 perms; we need to adjust this
1866
   * for the Umask (Bug#4223).
1867
   */
1868
0
  umask_setting = get_param_ptr(CURRENT_CONF, "Umask", FALSE);
1869
0
  if (umask_setting != NULL) {
1870
0
    mask = *umask_setting;
1871
1872
0
  } else {
1873
0
    mask = (mode_t) 0022;
1874
0
  }
1875
1876
0
  perms = (0666 & ~mask);
1877
1878
0
  if (pr_fsio_chmod(cmd->arg, perms) < 0) {
1879
    /* Not much to do but log the error. */
1880
0
    pr_log_pri(PR_LOG_NOTICE, "error: unable to chmod '%s' to %04o: %s",
1881
0
      cmd->arg, perms, strerror(errno));
1882
0
  }
1883
1884
0
  if (pr_fsio_stat(cmd->arg, &st) == 0) {
1885
0
    off_t *file_size;
1886
1887
0
    file_size = palloc(cmd->pool, sizeof(off_t));
1888
0
    *file_size = st.st_size;
1889
0
    (void) pr_table_add(cmd->notes, "mod_xfer.file-size", file_size,
1890
0
      sizeof(off_t));
1891
0
  }
1892
1893
0
  return PR_DECLINED(cmd);
1894
0
}
1895
1896
/* xfer_pre_appe() is the PRE_CMD handler for the APPE command, which
1897
 * simply sets xfer_type to STOR_APPEND and calls xfer_pre_stor().
1898
 */
1899
0
MODRET xfer_pre_appe(cmd_rec *cmd) {
1900
0
  session.xfer.xfer_type = STOR_DEFAULT;
1901
1902
0
  if (xfer_check_limit(cmd) < 0) {
1903
0
    pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
1904
1905
0
    pr_cmd_set_errno(cmd, EPERM);
1906
0
    errno = EPERM;
1907
0
    return PR_ERROR(cmd);
1908
0
  }
1909
1910
0
  session.xfer.xfer_type = STOR_APPEND;
1911
0
  return xfer_pre_stor(cmd);
1912
0
}
1913
1914
0
MODRET xfer_stor(cmd_rec *cmd) {
1915
0
  const char *path;
1916
0
  char *lbuf;
1917
0
  int bufsz, len, xerrno = 0;
1918
0
  off_t nbytes_stored, nbytes_max_store = 0;
1919
0
  unsigned char have_limit = FALSE;
1920
0
  struct stat st;
1921
0
  off_t start_offset = 0, upload_len = 0;
1922
0
  off_t curr_offset, curr_pos = 0;
1923
0
  pr_error_t *err = NULL;
1924
1925
0
  memset(&st, 0, sizeof(st));
1926
1927
  /* Prepare for any potential throttling. */
1928
0
  pr_throttle_init(cmd);
1929
1930
0
  session.xfer.path = pr_table_get(cmd->notes, "mod_xfer.store-path", NULL);
1931
0
  session.xfer.path_hidden = pr_table_get(cmd->notes,
1932
0
    "mod_xfer.store-hidden-path", NULL);
1933
1934
0
  path = session.xfer.path;
1935
1936
  /* Make sure the proper current working directory is set in the FSIO
1937
   * layer, so that the proper FS can be used for the open().
1938
   */
1939
0
  pr_fs_setcwd(pr_fs_getcwd());
1940
1941
0
  if (session.xfer.xfer_type == STOR_HIDDEN) {
1942
0
    const void *nfs;
1943
0
    int oflags;
1944
1945
0
    oflags = O_WRONLY;
1946
1947
0
    if (session.restart_pos == 0) {
1948
0
      oflags |= O_CREAT;
1949
0
    }
1950
1951
0
    nfs = pr_table_get(cmd->notes, "mod_xfer.store-hidden-nfs", NULL);
1952
0
    if (nfs == NULL) {
1953
0
      pr_trace_msg("fsio", 9,
1954
0
        "HiddenStores path '%s' is NOT on NFS, using O_EXCL open(2) flags",
1955
0
        session.xfer.path_hidden);
1956
0
      oflags |= O_EXCL;
1957
1958
0
    } else {
1959
0
      pr_trace_msg("fsio", 9,
1960
0
        "HiddenStores path '%s' is on NFS, NOT using O_EXCL open(2) flags",
1961
0
        session.xfer.path_hidden);
1962
0
    }
1963
1964
0
    stor_fh = pr_fsio_open_with_error(cmd->pool, session.xfer.path_hidden,
1965
0
      oflags, &err);
1966
0
    xerrno = errno;
1967
1968
0
    if (stor_fh == NULL) {
1969
0
      pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 5);
1970
0
      pr_error_set_why(err, pstrcat(cmd->pool, "open HiddenStores file '",
1971
0
        session.xfer.path_hidden, "'", NULL));
1972
1973
0
      (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
1974
0
        "error opening '%s': %s", (char *) cmd->argv[0], session.user,
1975
0
        pr_uid2str(cmd->tmp_pool, session.uid),
1976
0
        pr_gid2str(cmd->tmp_pool, session.gid), session.xfer.path_hidden,
1977
0
        strerror(xerrno));
1978
0
    }
1979
1980
0
  } else if (session.xfer.xfer_type == STOR_APPEND) {
1981
0
    stor_fh = pr_fsio_open_with_error(cmd->pool, session.xfer.path,
1982
0
      O_CREAT|O_WRONLY, &err);
1983
0
    xerrno = errno;
1984
1985
0
    if (stor_fh != NULL) {
1986
0
      if (pr_fsio_lseek(stor_fh, 0, SEEK_END) == (off_t) -1) {
1987
0
        pr_log_debug(DEBUG4, "unable to seek to end of '%s' for appending: %s",
1988
0
          cmd->arg, strerror(errno));
1989
0
        (void) pr_fsio_close(stor_fh);
1990
0
        stor_fh = NULL;
1991
0
      }
1992
1993
0
    } else {
1994
0
      xerrno = errno;
1995
1996
0
      pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 15);
1997
0
      pr_error_set_why(err, pstrcat(cmd->pool, "append to file '",
1998
0
        session.xfer.path, "'", NULL));
1999
2000
0
      (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
2001
0
        "error opening '%s': %s", (char *) cmd->argv[0], session.user,
2002
0
        pr_uid2str(cmd->tmp_pool, session.uid),
2003
0
        pr_gid2str(cmd->tmp_pool, session.gid), session.xfer.path,
2004
0
        strerror(xerrno));
2005
0
    }
2006
2007
0
  } else {
2008
0
    int open_flags = O_WRONLY|O_CREAT;
2009
2010
0
    if (session.range_len == 0 &&
2011
0
        session.restart_pos == 0) {
2012
      /* If we are not resuming an upload or handling a byte range transfer,
2013
       * then we should truncate the file to receive the new data.
2014
       */
2015
0
      open_flags |= O_TRUNC;
2016
0
    }
2017
2018
    /* Normal session */
2019
0
    stor_fh = pr_fsio_open_with_error(cmd->pool, path, open_flags, &err);
2020
0
    xerrno = errno;
2021
2022
0
    if (stor_fh == NULL) {
2023
0
      pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
2024
0
      pr_error_set_why(err, pstrcat(cmd->pool, "upload file '", path, "'",
2025
0
        NULL));
2026
2027
0
      (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
2028
0
        "error opening '%s': %s", (char *) cmd->argv[0], session.user,
2029
0
        pr_uid2str(cmd->tmp_pool, session.uid),
2030
0
        pr_gid2str(cmd->tmp_pool, session.gid), path, strerror(xerrno));
2031
0
    }
2032
0
  }
2033
2034
0
  if (session.restart_pos > 0) {
2035
0
    start_offset = session.restart_pos;
2036
2037
0
  } else if (session.range_start > 0) {
2038
0
    start_offset = session.range_start;
2039
0
  }
2040
2041
0
  if (stor_fh != NULL &&
2042
0
      start_offset > 0) {
2043
0
    xerrno = 0;
2044
2045
0
    pr_fs_clear_cache2(path);
2046
0
    if (pr_fsio_lseek(stor_fh, start_offset, SEEK_SET) == -1) {
2047
0
      pr_log_debug(DEBUG4, "unable to seek to position %" PR_LU " of '%s': %s",
2048
0
        (pr_off_t) start_offset, cmd->arg, strerror(errno));
2049
0
      xerrno = errno;
2050
2051
0
    } else if (pr_fsio_stat(path, &st) < 0) {
2052
0
      pr_log_debug(DEBUG4, "error checking '%s': %s", cmd->arg,
2053
0
        strerror(errno));
2054
0
      xerrno = errno;
2055
0
    }
2056
2057
0
    if (xerrno) {
2058
0
      (void) pr_fsio_close(stor_fh);
2059
0
      errno = xerrno;
2060
0
      stor_fh = NULL;
2061
0
    }
2062
2063
    /* Make sure that the requested offset is valid (within the size of the
2064
     * file being resumed).
2065
     */
2066
0
    if (stor_fh != NULL &&
2067
0
        start_offset > st.st_size) {
2068
0
      int used_rest = TRUE;
2069
2070
0
      if (session.range_start > 0) {
2071
0
        used_rest = FALSE;
2072
0
      }
2073
2074
0
      pr_response_add_err(R_554, _("%s: invalid %s argument"),
2075
0
        used_rest ? C_REST : C_RANG, cmd->arg);
2076
0
      (void) pr_fsio_close(stor_fh);
2077
0
      stor_fh = NULL;
2078
2079
0
      pr_cmd_set_errno(cmd, EINVAL);
2080
0
      errno = EINVAL;
2081
0
      return PR_ERROR(cmd);
2082
0
    }
2083
2084
0
    curr_pos = start_offset;
2085
2086
0
    if (session.restart_pos > 0) {
2087
0
      session.restart_pos = 0L;
2088
2089
0
    } else if (session.range_start > 0) {
2090
0
      session.range_start = 0;
2091
0
    }
2092
0
  }
2093
2094
0
  if (stor_fh == NULL) {
2095
0
    if (err != NULL) {
2096
0
      pr_log_debug(DEBUG4, "%s", pr_error_strerror(err, 0));
2097
0
      pr_error_destroy(err);
2098
0
      err = NULL;
2099
2100
0
    } else {
2101
0
      pr_log_debug(DEBUG4, "unable to open '%s' for writing: %s", cmd->arg,
2102
0
        strerror(xerrno));
2103
0
    }
2104
2105
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2106
2107
0
    pr_cmd_set_errno(cmd, xerrno);
2108
0
    errno = xerrno;
2109
0
    return PR_ERROR(cmd);
2110
0
  }
2111
2112
  /* Advise the platform that we will be only writing this file.  Note that a
2113
   * preceding REST command does not mean we need to use a different offset
2114
   * value here; we can/should still tell the platform that the entire file
2115
   * should be treated this way.
2116
   */
2117
0
  pr_fs_fadvise(PR_FH_FD(stor_fh), 0, 0, PR_FS_FADVISE_DONTNEED);
2118
2119
  /* Stash the offset at which we're writing to this file. */
2120
0
  curr_offset = pr_fsio_lseek(stor_fh, (off_t) 0, SEEK_CUR);
2121
0
  if (curr_offset != (off_t) -1) {
2122
0
    off_t *file_offset;
2123
2124
0
    file_offset = palloc(cmd->pool, sizeof(off_t));
2125
0
    *file_offset = (off_t) curr_offset;
2126
0
    (void) pr_table_add(cmd->notes, "mod_xfer.file-offset", file_offset,
2127
0
      sizeof(off_t));
2128
0
  }
2129
2130
  /* Get the latest stats on the file.  If the file already existed, we
2131
   * want to know its current size.
2132
   */
2133
0
  (void) pr_fsio_fstat(stor_fh, &st);
2134
2135
  /* Block any timers for this section, where we want to prepare the
2136
   * data connection, then need to reprovision the session.xfer struct,
2137
   * and do NOT want timers (which may want/need that session.xfer data)
2138
   * to fire until after the reprovisioning (Bug#4168).
2139
   */
2140
0
  pr_alarms_block();
2141
2142
  /* Perform the actual transfer now */
2143
0
  pr_data_init(cmd->arg, PR_NETIO_IO_RD);
2144
2145
  /* Note that we have to re-populate the session.xfer variables here,
2146
   * AFTER the pr_data_init() call.  pr_data_init() ensures that there is
2147
   * no leftover information in session.xfer, as from aborted tranfers.
2148
   */
2149
0
  session.xfer.path = pr_table_get(cmd->notes, "mod_xfer.store-path", NULL);
2150
0
  session.xfer.path_hidden = pr_table_get(cmd->notes,
2151
0
    "mod_xfer.store-hidden-path", NULL);
2152
0
  session.xfer.file_size = curr_pos;
2153
2154
0
  pr_alarms_unblock();
2155
2156
  /* First, make sure the uploaded file has the requested ownership. */
2157
0
  stor_chown(cmd->tmp_pool);
2158
2159
0
  if (session.range_len > 0) {
2160
0
    upload_len = session.range_len;
2161
0
  }
2162
2163
0
  if (pr_data_open(cmd->arg, NULL, PR_NETIO_IO_RD, upload_len) < 0) {
2164
0
    xerrno = errno;
2165
2166
0
    stor_abort(cmd->pool);
2167
0
    pr_data_abort(0, TRUE);
2168
2169
0
    pr_cmd_set_errno(cmd, xerrno);
2170
0
    errno = xerrno;
2171
0
    return PR_ERROR(cmd);
2172
0
  }
2173
2174
  /* Initialize the number of bytes stored */
2175
0
  nbytes_stored = 0;
2176
2177
  /* Retrieve the number of bytes to store, maximum, if present.
2178
   * This check is needed during the pr_data_xfer() loop, below, because
2179
   * the size of the file being uploaded isn't known in advance
2180
   */
2181
0
  nbytes_max_store = find_max_nbytes("MaxStoreFileSize");
2182
0
  if (nbytes_max_store == 0UL) {
2183
0
    have_limit = FALSE;
2184
2185
0
  } else {
2186
0
    have_limit = TRUE;
2187
0
  }
2188
2189
0
  bufsz = pr_config_get_server_xfer_bufsz(PR_NETIO_IO_RD);
2190
0
  lbuf = (char *) palloc(cmd->tmp_pool, bufsz);
2191
0
  pr_trace_msg("data", 8, "allocated upload buffer of %lu bytes",
2192
0
    (unsigned long) bufsz);
2193
2194
0
  while ((len = pr_data_xfer(lbuf, bufsz)) > 0) {
2195
0
    int res;
2196
2197
0
    pr_signals_handle();
2198
2199
0
    if (XFER_ABORTED) {
2200
0
      break;
2201
0
    }
2202
2203
0
    nbytes_stored += len;
2204
2205
    /* If MaxStoreFileSize is configured, double-check the number of bytes
2206
     * uploaded so far against the configured limit.  Also make sure that
2207
     * we take into account the size of the file, i.e. if it already existed.
2208
     */
2209
0
    if (have_limit &&
2210
0
        (nbytes_stored + st.st_size > nbytes_max_store)) {
2211
0
      pr_log_pri(PR_LOG_NOTICE, "MaxStoreFileSize (%" PR_LU " bytes) reached: "
2212
0
        "aborting transfer of '%s'", (pr_off_t) nbytes_max_store, path);
2213
2214
      /* Abort the transfer. */
2215
0
      stor_abort(cmd->pool);
2216
2217
      /* Set errno to EFBIG (or the most appropriate alternative). */
2218
0
#if defined(EFBIG)
2219
0
      xerrno = EFBIG;
2220
#elif defined(EDQUOT)
2221
      xerrno = EDQUOT;
2222
#else
2223
      xerrno = EPERM;
2224
#endif
2225
2226
0
      pr_data_abort(xerrno, FALSE);
2227
0
      pr_cmd_set_errno(cmd, xerrno);
2228
0
      errno = xerrno;
2229
0
      return PR_ERROR(cmd);
2230
0
    }
2231
2232
    /* XXX Need to handle short writes better here.  It is possible that
2233
     * the underlying filesystem (e.g. a network-mounted filesystem) could
2234
     * be doing short writes, and we ideally should be more resilient/graceful
2235
     * in the face of such things.
2236
     */
2237
0
    res = pr_fsio_write_with_error(cmd->pool, stor_fh, lbuf, len, &err);
2238
0
    xerrno = errno;
2239
2240
0
    while (res < 0 &&
2241
0
           xerrno == EINTR) {
2242
      /* Interrupted by signal; handle it, and try again. */
2243
0
      errno = EINTR;
2244
0
      pr_signals_handle();
2245
2246
0
      res = pr_fsio_write_with_error(cmd->pool, stor_fh, lbuf, len, &err);
2247
0
      xerrno = errno;
2248
0
    }
2249
2250
0
    if (res != len) {
2251
0
      xerrno = EIO;
2252
2253
0
      if (res < 0) {
2254
0
        xerrno = errno;
2255
2256
0
        pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 9);
2257
0
        pr_error_set_why(err, pstrcat(cmd->pool, "writing '", stor_fh->fh_path,
2258
0
          "'", NULL));
2259
0
      }
2260
2261
0
      (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
2262
0
        "error writing to '%s': %s", (char *) cmd->argv[0], session.user,
2263
0
        pr_uid2str(cmd->tmp_pool, session.uid),
2264
0
        pr_gid2str(cmd->tmp_pool, session.gid), stor_fh->fh_path,
2265
0
        strerror(xerrno));
2266
2267
0
      if (err != NULL) {
2268
0
        pr_log_debug(DEBUG9, "%s", pr_error_strerror(err, 0));
2269
0
        pr_error_destroy(err);
2270
0
        err = NULL;
2271
0
      }
2272
2273
0
      stor_abort(cmd->pool);
2274
0
      pr_data_abort(xerrno, FALSE);
2275
2276
0
      pr_cmd_set_errno(cmd, xerrno);
2277
0
      errno = xerrno;
2278
0
      return PR_ERROR(cmd);
2279
0
    }
2280
2281
    /* If no throttling is configured, this does nothing. */
2282
0
    pr_throttle_pause(nbytes_stored, FALSE, nbytes_stored);
2283
2284
0
    if (session.range_len > 0) {
2285
0
      if (nbytes_stored == upload_len) {
2286
0
        break;
2287
0
      }
2288
2289
0
      if (nbytes_stored > upload_len) {
2290
0
        xerrno = EPERM;
2291
2292
0
        pr_log_pri(PR_LOG_NOTICE, "Transfer range length (%" PR_LU
2293
0
          " %s) exceeded; aborting transfer of '%s'", (pr_off_t) upload_len,
2294
0
          upload_len != 1 ? "bytes" : "byte", path);
2295
2296
        /* Abort the transfer. */
2297
0
        stor_abort(cmd->pool);
2298
2299
0
        pr_data_abort(xerrno, FALSE);
2300
0
        pr_cmd_set_errno(cmd, xerrno);
2301
0
        errno = xerrno;
2302
0
        return PR_ERROR(cmd);
2303
0
      }
2304
0
    }
2305
0
  }
2306
2307
0
  if (XFER_ABORTED) {
2308
0
    stor_abort(cmd->pool);
2309
0
    pr_data_abort(0, FALSE);
2310
2311
0
    pr_cmd_set_errno(cmd, EIO);
2312
0
    errno = EIO;
2313
0
    return PR_ERROR(cmd);
2314
0
  }
2315
2316
0
  if (len < 0) {
2317
    /* Default abort errno, in case session.d et al has already gone away */
2318
0
    xerrno = ECONNABORTED;
2319
2320
0
    stor_abort(cmd->pool);
2321
2322
0
    if (session.d != NULL &&
2323
0
        session.d->instrm != NULL) {
2324
0
      xerrno = PR_NETIO_ERRNO(session.d->instrm);
2325
0
    }
2326
2327
0
    pr_data_abort(xerrno, FALSE);
2328
0
    pr_cmd_set_errno(cmd, xerrno);
2329
0
    errno = xerrno;
2330
0
    return PR_ERROR(cmd);
2331
0
  }
2332
2333
  /* Did we receive all of the expected bytes in a range? */
2334
0
  if (session.range_len > 0 &&
2335
0
      nbytes_stored < upload_len) {
2336
0
    xerrno = EPERM;
2337
2338
0
    pr_log_pri(PR_LOG_NOTICE, "Transfer range length (%" PR_LU
2339
0
      " %s) not provided; aborting transfer of '%s'", (pr_off_t) upload_len,
2340
0
      upload_len != 1 ? "bytes" : "byte", path);
2341
2342
    /* Abort the transfer. */
2343
0
    stor_abort(cmd->pool);
2344
2345
0
    pr_data_abort(xerrno, FALSE);
2346
0
    pr_cmd_set_errno(cmd, xerrno);
2347
0
    errno = xerrno;
2348
0
    return PR_ERROR(cmd);
2349
0
  }
2350
2351
  /* If no throttling is configured, this does nothing. */
2352
0
  pr_throttle_pause(nbytes_stored, TRUE, nbytes_stored);
2353
2354
0
  if (stor_complete(cmd->pool) < 0) {
2355
0
    xerrno = errno;
2356
2357
0
    _log_transfer('i', 'i');
2358
2359
    /* Check errno for EDQOUT (or the most appropriate alternative).
2360
     * (I hate the fact that FTP has a special response code just for
2361
     * this, and that clients actually expect it.  Special cases are
2362
     * stupid.)
2363
     */
2364
0
#if defined(EDQUOT)
2365
0
    if (xerrno == EDQUOT) {
2366
0
      pr_response_add_err(R_552, "%s: %s", cmd->arg, strerror(xerrno));
2367
2368
0
      pr_cmd_set_errno(cmd, xerrno);
2369
0
      errno = xerrno;
2370
0
      return PR_ERROR(cmd);
2371
0
    }
2372
#elif defined(EFBIG)
2373
    if (xerrno == EFBIG) {
2374
      pr_response_add_err(R_552, "%s: %s", cmd->arg, strerror(xerrno));
2375
2376
      pr_cmd_set_errno(cmd, xerrno);
2377
      errno = xerrno;
2378
      return PR_ERROR(cmd);
2379
    }
2380
#endif
2381
2382
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2383
2384
0
    pr_cmd_set_errno(cmd, xerrno);
2385
0
    errno = xerrno;
2386
0
    return PR_ERROR(cmd);
2387
0
  }
2388
2389
0
  if (session.xfer.path &&
2390
0
      session.xfer.path_hidden) {
2391
0
    if (pr_fsio_rename(session.xfer.path_hidden, session.xfer.path) < 0) {
2392
0
      xerrno = errno;
2393
2394
      /* This should only fail on a race condition with a chmod/chown or if
2395
       * STOR_APPEND is on and the permissions are squirrely.  The poor user
2396
       * will have to re-upload, but we've got more important problems to worry
2397
       * about and this failure should be fairly rare.
2398
       */
2399
0
      pr_log_pri(PR_LOG_WARNING, "Rename of %s to %s failed: %s.",
2400
0
        session.xfer.path_hidden, session.xfer.path, strerror(xerrno));
2401
2402
0
      pr_response_add_err(R_550, _("%s: Rename of hidden file %s failed: %s"),
2403
0
        session.xfer.path, session.xfer.path_hidden, strerror(xerrno));
2404
2405
0
      if (pr_fsio_unlink(session.xfer.path_hidden) < 0) {
2406
0
        if (errno != ENOENT) {
2407
0
          pr_log_debug(DEBUG0, "failed to delete HiddenStores file '%s': %s",
2408
0
            session.xfer.path_hidden, strerror(errno));
2409
0
        }
2410
0
      }
2411
2412
0
      pr_cmd_set_errno(cmd, xerrno);
2413
0
      errno = xerrno;
2414
0
      return PR_ERROR(cmd);
2415
0
    }
2416
2417
    /* One way or another, we've dealt with the HiddenStores file. */
2418
0
    session.xfer.path_hidden = NULL;
2419
0
  }
2420
2421
0
  xfer_displayfile();
2422
0
  pr_data_close2();
2423
0
  pr_response_add(R_226, _("Transfer complete"));
2424
2425
0
  return PR_HANDLED(cmd);
2426
0
}
2427
2428
/* Should this become part of the String API? */
2429
0
static int parse_offset(char *str, off_t *num) {
2430
0
  char *ptr, *tmp = NULL;
2431
2432
  /* Don't allow negative numbers.  strtoul()/strtoull() will silently
2433
   * handle them.
2434
   */
2435
0
  ptr = str;
2436
0
  if (*ptr == '-') {
2437
0
    errno = EINVAL;
2438
0
    return -1;
2439
0
  }
2440
2441
0
#ifdef HAVE_STRTOULL
2442
0
  *num = strtoull(ptr, &tmp, 10);
2443
#else
2444
  *num = strtoul(ptr, &tmp, 10);
2445
#endif /* HAVE_STRTOULL */
2446
2447
0
  if (tmp && *tmp) {
2448
0
    errno = EINVAL;
2449
0
    return -1;
2450
0
  }
2451
2452
0
  return 0;
2453
0
}
2454
2455
0
MODRET xfer_rest(cmd_rec *cmd) {
2456
0
  int res;
2457
0
  off_t pos = 0;
2458
2459
0
  if (cmd->argc != 2) {
2460
0
    pr_response_add_err(R_500, _("'%s' not understood"),
2461
0
      pr_cmd_get_displayable_str(cmd, NULL));
2462
2463
0
    pr_cmd_set_errno(cmd, EINVAL);
2464
0
    errno = EINVAL;
2465
0
    return PR_ERROR(cmd);
2466
0
  }
2467
2468
0
  res = parse_offset(cmd->argv[1], &pos);
2469
0
  if (res < 0) {
2470
0
    int xerrno = errno;
2471
2472
0
    pr_response_add_err(R_501,
2473
0
      _("REST requires a value greater than or equal to 0"));
2474
2475
0
    pr_cmd_set_errno(cmd, xerrno);
2476
0
    errno = xerrno;
2477
0
    return PR_ERROR(cmd);
2478
0
  }
2479
2480
  /* Refuse the command if we're in ASCII mode, and the restart position
2481
   * is anything other than zero.
2482
   *
2483
   * Ideally, we would refuse the REST command when in ASCII mode regardless
2484
   * of position.  However, some (IMHO, stupid) clients "test" the FTP
2485
   * server by sending "REST 0" to see if the server supports REST, without
2486
   * regard to the transfer type.  This, then, is a hack to handle such
2487
   * clients.
2488
   */
2489
0
  if ((session.sf_flags & SF_ASCII) &&
2490
0
      pos != 0 &&
2491
0
      !(xfer_opts & PR_XFER_OPT_IGNORE_ASCII)) {
2492
0
    pr_log_debug(DEBUG5, "%s not allowed in ASCII mode", (char *) cmd->argv[0]);
2493
0
    pr_response_add_err(R_501,
2494
0
      _("%s: Resuming transfers not allowed in ASCII mode"),
2495
0
      (char *) cmd->argv[0]);
2496
2497
0
    pr_cmd_set_errno(cmd, EPERM);
2498
0
    errno = EPERM;
2499
0
    return PR_ERROR(cmd);
2500
0
  }
2501
2502
0
  session.restart_pos = pos;
2503
2504
  /* We can honor REST, or RANG, but not both at the same time. */
2505
0
  session.range_start = session.range_len = 0;
2506
2507
0
  pr_response_add(R_350, _("Restarting at %" PR_LU
2508
0
    ". Send STORE or RETRIEVE to initiate transfer"), (pr_off_t) pos);
2509
0
  return PR_HANDLED(cmd);
2510
0
}
2511
2512
0
MODRET xfer_rang(cmd_rec *cmd) {
2513
0
  int res;
2514
0
  off_t range_start, range_end;
2515
2516
0
  if (cmd->argc != 3) {
2517
0
    pr_response_add_err(R_500, _("'%s' not understood"),
2518
0
      pr_cmd_get_displayable_str(cmd, NULL));
2519
2520
0
    pr_cmd_set_errno(cmd, EINVAL);
2521
0
    errno = EINVAL;
2522
0
    return PR_ERROR(cmd);
2523
0
  }
2524
2525
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.cwd, NULL)) {
2526
0
    int xerrno = EPERM;
2527
2528
0
    pr_log_debug(DEBUG8, "RANG denied by <Limit> configuration");
2529
0
    pr_response_add_err(R_552, "%s: %s", (char *) cmd->argv[0],
2530
0
      strerror(xerrno));
2531
2532
0
    pr_cmd_set_errno(cmd, xerrno);
2533
0
    errno = xerrno;
2534
0
    return PR_ERROR(cmd);
2535
0
  }
2536
2537
0
  res = parse_offset(cmd->argv[1], &range_start);
2538
0
  if (res < 0) {
2539
0
    int xerrno = errno;
2540
2541
0
    pr_response_add_err(R_501,
2542
0
      _("RANG requires a value greater than or equal to 0"));
2543
2544
0
    pr_cmd_set_errno(cmd, xerrno);
2545
0
    errno = xerrno;
2546
0
    return PR_ERROR(cmd);
2547
0
  }
2548
2549
0
  res = parse_offset(cmd->argv[2], &range_end);
2550
0
  if (res < 0) {
2551
0
    int xerrno = errno;
2552
2553
0
    pr_response_add_err(R_501,
2554
0
      _("RANG requires a value greater than or equal to 0"));
2555
2556
0
    pr_cmd_set_errno(cmd, xerrno);
2557
0
    errno = xerrno;
2558
0
    return PR_ERROR(cmd);
2559
0
  }
2560
2561
0
  if (range_start > range_end) {
2562
    /* Per Draft, such ranges will automatically reset the range. */
2563
0
    session.range_start = session.range_len = 0;
2564
2565
    /* Iff start = 1 AND end = 0, then this is the acceptable way to reset
2566
     * the range.  Otherwise, it is an error.
2567
     */
2568
0
    if (range_start == 1 &&
2569
0
        range_end == 0) {
2570
0
      pr_response_add(R_350, _("Reset byte transfer range"));
2571
0
      return PR_HANDLED(cmd);
2572
0
    }
2573
2574
0
    pr_log_debug(DEBUG9, "rejecting RANG: start %" PR_LU " > end %" PR_LU,
2575
0
      (pr_off_t) range_start, (pr_off_t) range_end);
2576
0
    pr_response_add_err(R_501, _("RANG start must be less than end"));
2577
2578
0
    pr_cmd_set_errno(cmd, EINVAL);
2579
0
    errno = EINVAL;
2580
0
    return PR_ERROR(cmd);
2581
0
  }
2582
2583
  /* Per Draft, refuse (with 551) if we are not in IMAGE type, STREAM mode. */
2584
0
  if (session.sf_flags & SF_ASCII) {
2585
0
    pr_log_debug(DEBUG5, "%s not allowed in ASCII mode", (char *) cmd->argv[0]);
2586
0
    pr_response_add_err(R_551,
2587
0
      _("%s: Transfer ranges not allowed in ASCII mode"),
2588
0
      (char *) cmd->argv[0]);
2589
2590
0
    pr_cmd_set_errno(cmd, EPERM);
2591
0
    errno = EPERM;
2592
0
    return PR_ERROR(cmd);
2593
0
  }
2594
2595
  /* Per Draft, the values given are positions, inclusive.  Thus
2596
   * "RANG 0 1" would be a transfer range of TWO bytes.
2597
   *
2598
   * For consistency, we store offset+len, rather than start/end positions.
2599
   */
2600
0
  session.range_start = range_start;
2601
0
  session.range_len = (range_end - range_start + 1);
2602
2603
  /* We can honor RANG, or REST, but not both at the same time. */
2604
0
  session.restart_pos = 0;
2605
2606
0
  pr_response_add(R_350, _("Transferring byte range of %" PR_LU
2607
0
    " %s starting from %" PR_LU), (pr_off_t) session.range_len,
2608
0
    session.range_len != 1 ? "bytes" : "byte", (pr_off_t) range_start);
2609
0
  return PR_HANDLED(cmd);
2610
0
}
2611
2612
/* This is a PRE_CMD handler that checks security, etc, and places the full
2613
 * filename to send in cmd->notes (note that we CANNOT use cmd->tmp_pool
2614
 * for this, as tmp_pool only lasts for the duration of this function).
2615
 */
2616
0
MODRET xfer_pre_retr(cmd_rec *cmd) {
2617
0
  char *decoded_path, *dir = NULL;
2618
0
  mode_t fmode;
2619
0
  unsigned char *allow_restart = NULL;
2620
0
  config_rec *c;
2621
2622
0
  xfer_logged_sendfile_decline_msg = FALSE;
2623
2624
0
  if (cmd->argc < 2) {
2625
0
    pr_response_add_err(R_500, _("'%s' not understood"),
2626
0
      pr_cmd_get_displayable_str(cmd, NULL));
2627
2628
0
    pr_cmd_set_errno(cmd, EINVAL);
2629
0
    errno = EINVAL;
2630
0
    return PR_ERROR(cmd);
2631
0
  }
2632
2633
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
2634
0
    FSIO_DECODE_FL_TELL_ERRORS);
2635
0
  if (decoded_path == NULL) {
2636
0
    int xerrno = errno;
2637
2638
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
2639
0
      strerror(xerrno));
2640
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
2641
0
      cmd->arg);
2642
2643
0
    pr_cmd_set_errno(cmd, xerrno);
2644
0
    errno = xerrno;
2645
0
    return PR_ERROR(cmd);
2646
0
  }
2647
2648
0
  pr_fs_clear_cache2(decoded_path);
2649
0
  dir = dir_realpath(cmd->tmp_pool, decoded_path);
2650
0
  if (dir == NULL) {
2651
    /* Try using dir_best_path(), as xfer_pre_stor() does.
2652
     *
2653
     * Without this fallback, certain use cases (such as SFTP downloads using
2654
     * mod_sftp + mod_vroot) fail unexpectedly, with misleading
2655
     * "denied by <Limit> configuration" errors.
2656
     */
2657
0
    dir = dir_best_path(cmd->tmp_pool, decoded_path);
2658
0
  }
2659
2660
0
  if (dir == NULL ||
2661
0
      !dir_check(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
2662
0
    int xerrno = errno;
2663
2664
0
    pr_log_debug(DEBUG8, "%s %s denied by <Limit> configuration",
2665
0
        (char *) cmd->argv[0], cmd->arg);
2666
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2667
2668
0
    pr_cmd_set_errno(cmd, xerrno);
2669
0
    errno = xerrno;
2670
0
    return PR_ERROR(cmd);
2671
0
  }
2672
2673
  /* Check for UseSendfile. */
2674
0
  use_sendfile = TRUE;
2675
0
  use_sendfile_len = 0;
2676
0
  use_sendfile_pct = -1.0;
2677
2678
0
  c = find_config(CURRENT_CONF, CONF_PARAM, "UseSendfile", FALSE);
2679
0
  if (c != NULL) {
2680
0
    use_sendfile = *((unsigned char *) c->argv[0]);
2681
0
    use_sendfile_len = *((off_t *) c->argv[1]);
2682
0
    use_sendfile_pct = *((float *) c->argv[2]);
2683
0
  }
2684
2685
0
  if (xfer_check_limit(cmd) < 0) {
2686
0
    pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
2687
2688
0
    pr_cmd_set_errno(cmd, EPERM);
2689
0
    errno = EPERM;
2690
0
    return PR_ERROR(cmd);
2691
0
  }
2692
2693
0
  fmode = file_mode2(cmd->tmp_pool, dir);
2694
0
  if (fmode == 0) {
2695
0
    int xerrno = errno;
2696
2697
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2698
2699
0
    pr_cmd_set_errno(cmd, xerrno);
2700
0
    errno = xerrno;
2701
0
    return PR_ERROR(cmd);
2702
0
  }
2703
2704
0
  if (!S_ISREG(fmode)
2705
0
#ifdef S_ISFIFO
2706
0
      && !S_ISFIFO(fmode)
2707
0
#endif
2708
0
     ) {
2709
0
    pr_response_add_err(R_550, _("%s: Not a regular file"), cmd->arg);
2710
2711
    /* Deliberately use EISDIR for anything non-file (e.g. directories). */
2712
0
    pr_cmd_set_errno(cmd, EISDIR);
2713
0
    errno = EISDIR;
2714
0
    return PR_ERROR(cmd);
2715
0
  }
2716
2717
  /* If restart is on, check to see if AllowRestartRetrieve is off, in
2718
   * which case we disallow the transfer and clear restart_pos.
2719
   */
2720
0
  allow_restart = get_param_ptr(CURRENT_CONF, "AllowRetrieveRestart", FALSE);
2721
2722
0
  if ((session.restart_pos > 0 || session.range_len > 0) &&
2723
0
      (allow_restart && *allow_restart == FALSE)) {
2724
0
    pr_response_add_err(R_451, _("%s: Restart not permitted, try again"),
2725
0
      cmd->arg);
2726
0
    session.restart_pos = 0L;
2727
2728
0
    pr_cmd_set_errno(cmd, EPERM);
2729
0
    errno = EPERM;
2730
0
    return PR_ERROR(cmd);
2731
0
  }
2732
2733
  /* Otherwise everything is good */
2734
0
  if (pr_table_add(cmd->notes, "mod_xfer.retr-path",
2735
0
      pstrdup(cmd->pool, dir), 0) < 0) {
2736
0
    if (errno != EEXIST) {
2737
0
      pr_log_pri(PR_LOG_NOTICE, "notice: error adding 'mod_xfer.retr-path': %s",
2738
0
        strerror(errno));
2739
0
    }
2740
0
  }
2741
2742
0
  return PR_HANDLED(cmd);
2743
0
}
2744
2745
0
MODRET xfer_post_retr(cmd_rec *cmd) {
2746
0
  const char *path;
2747
2748
0
  path = pr_table_get(cmd->notes, "mod_xfer.retr-path", NULL);
2749
0
  if (path != NULL) {
2750
0
    struct stat st;
2751
2752
0
    if (pr_fsio_stat(path, &st) == 0) {
2753
0
      off_t *file_size;
2754
2755
0
      file_size = palloc(cmd->pool, sizeof(off_t));
2756
0
      *file_size = st.st_size;
2757
0
      (void) pr_table_add(cmd->notes, "mod_xfer.file-size", file_size,
2758
0
        sizeof(off_t));
2759
0
    }
2760
0
  }
2761
2762
0
  return PR_DECLINED(cmd);
2763
0
}
2764
2765
0
MODRET xfer_retr(cmd_rec *cmd) {
2766
0
  int xerrno = 0;
2767
0
  const char *dir = NULL;
2768
0
  char *lbuf;
2769
0
  struct stat st;
2770
0
  off_t nbytes_max_retrieve = 0;
2771
0
  unsigned char have_limit = FALSE;
2772
0
  long bufsz, len = 0;
2773
0
  off_t start_offset = 0, download_len = 0;
2774
0
  off_t curr_offset, curr_pos = 0, nbytes_sent = 0, cnt_steps = 0, cnt_next = 0;
2775
0
  pr_error_t *err = NULL;
2776
2777
  /* Prepare for any potential throttling. */
2778
0
  pr_throttle_init(cmd);
2779
2780
0
  dir = pr_table_get(cmd->notes, "mod_xfer.retr-path", NULL);
2781
2782
0
  retr_fh = pr_fsio_open_with_error(cmd->pool, dir, O_RDONLY, &err);
2783
0
  xerrno = errno;
2784
2785
0
  if (retr_fh == NULL) {
2786
0
    pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
2787
0
    pr_error_set_why(err, pstrcat(cmd->pool, "download file '", dir, "'",
2788
0
      NULL));
2789
2790
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
2791
0
      "error opening '%s': %s", (char *) cmd->argv[0], session.user,
2792
0
      pr_uid2str(cmd->tmp_pool, session.uid),
2793
0
      pr_gid2str(cmd->tmp_pool, session.gid), dir, strerror(xerrno));
2794
2795
0
    if (err != NULL) {
2796
0
      pr_log_debug(DEBUG9, "%s", pr_error_strerror(err, 0));
2797
0
      pr_error_destroy(err);
2798
0
      err = NULL;
2799
0
    }
2800
2801
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2802
2803
0
    pr_cmd_set_errno(cmd, xerrno);
2804
0
    errno = xerrno;
2805
0
    return PR_ERROR(cmd);
2806
0
  }
2807
2808
0
  if (pr_fsio_fstat(retr_fh, &st) < 0) {
2809
    /* Error stat'ing the file. */
2810
0
    xerrno = errno;
2811
0
    pr_log_debug(DEBUG6, "error checking path '%s': %s",
2812
0
      dir, strerror(xerrno));
2813
2814
0
    pr_fsio_close(retr_fh);
2815
0
    retr_fh = NULL;
2816
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2817
2818
0
    pr_cmd_set_errno(cmd, xerrno);
2819
0
    errno = xerrno;
2820
0
    return PR_ERROR(cmd);
2821
0
  }
2822
2823
  /* Advise the platform that we will be only reading this file
2824
   * sequentially.  Note that a preceding REST command does not mean we
2825
   * need to use a different offset value here; we can/should still
2826
   * tell the platform that the entire file should be treated this way.
2827
   */
2828
0
  pr_fs_fadvise(PR_FH_FD(retr_fh), 0, 0, PR_FS_FADVISE_SEQUENTIAL);
2829
2830
0
  if (session.restart_pos > 0) {
2831
0
    start_offset = session.restart_pos;
2832
2833
0
  } else if (session.range_start > 0) {
2834
0
    start_offset = session.range_start;
2835
0
  }
2836
2837
0
  if (start_offset > 0) {
2838
0
    char *offset_cmd;
2839
2840
0
    offset_cmd = C_REST;
2841
0
    if (session.range_start > 0) {
2842
0
      offset_cmd = C_RANG;
2843
0
    }
2844
2845
    /* Make sure that the requested offset is valid (within the size of the
2846
     * file being resumed).
2847
     */
2848
2849
0
    if (start_offset > st.st_size) {
2850
0
      pr_trace_msg(trace_channel, 4,
2851
0
        "%s offset %" PR_LU " exceeds file size (%" PR_LU " bytes)",
2852
0
        offset_cmd, (pr_off_t) start_offset, (pr_off_t) st.st_size);
2853
0
      pr_response_add_err(R_554, _("%s: invalid %s argument"), offset_cmd,
2854
0
        cmd->arg);
2855
0
      pr_fsio_close(retr_fh);
2856
0
      retr_fh = NULL;
2857
2858
0
      pr_cmd_set_errno(cmd, EINVAL);
2859
0
      errno = EINVAL;
2860
0
      return PR_ERROR(cmd);
2861
0
    }
2862
2863
    /* The RANG Draft says, on this topic:
2864
     *
2865
     *   The server-PI SHOULD transfer 0 octets with RETR if the specified
2866
     *   start point or start point and end point are larger than the actual
2867
     *   file size.
2868
     *
2869
     * However, I vehemently disagree.  Sending zero bytes in such a case
2870
     * would be treated as a successful download by the client, not informing
2871
     * the user of the erroneous conditions.  Thus, IMHO, violating the
2872
     * principle of least surprise; the user might end up asking "Why did
2873
     * my download succeed, but I have zero bytes?".  That is a terrible user
2874
     * experience.  So instead, we return an error in this case.
2875
     */
2876
2877
0
    if ((start_offset + session.range_len) > st.st_size) {
2878
0
      pr_trace_msg(trace_channel, 4,
2879
0
        "%s offset %" PR_LU " exceeds file size (%" PR_LU " bytes)",
2880
0
        offset_cmd, (pr_off_t) (start_offset + session.range_len),
2881
0
        (pr_off_t) st.st_size);
2882
0
      pr_response_add_err(R_554, _("%s: invalid RANG argument"), cmd->arg);
2883
0
      pr_fsio_close(retr_fh);
2884
0
      retr_fh = NULL;
2885
2886
0
      pr_cmd_set_errno(cmd, EINVAL);
2887
0
      errno = EINVAL;
2888
0
      return PR_ERROR(cmd);
2889
0
    }
2890
2891
0
    if (pr_fsio_lseek(retr_fh, start_offset, SEEK_SET) == (off_t) -1) {
2892
0
      xerrno = errno;
2893
0
      pr_fsio_close(retr_fh);
2894
0
      errno = xerrno;
2895
0
      retr_fh = NULL;
2896
2897
0
      (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
2898
0
        "error seeking to byte %" PR_LU " of '%s': %s", (char *) cmd->argv[0],
2899
0
        session.user, pr_uid2str(cmd->tmp_pool, session.uid),
2900
0
        pr_gid2str(cmd->tmp_pool, session.gid), (pr_off_t) start_offset,
2901
0
        dir, strerror(xerrno));
2902
2903
0
      pr_log_debug(DEBUG0, "error seeking to offset %" PR_LU
2904
0
        " for file %s: %s", (pr_off_t) start_offset, dir, strerror(xerrno));
2905
0
      pr_response_add_err(R_554, _("%s: invalid %s argument"), offset_cmd,
2906
0
        cmd->arg);
2907
2908
0
      pr_cmd_set_errno(cmd, EINVAL);
2909
0
      errno = EINVAL;
2910
0
      return PR_ERROR(cmd);
2911
0
    }
2912
2913
0
    curr_pos = start_offset;
2914
2915
0
    if (session.restart_pos > 0) {
2916
0
      session.restart_pos = 0L;
2917
2918
0
    } else if (session.range_start > 0) {
2919
0
      session.range_start = 0;
2920
0
    }
2921
0
  }
2922
2923
  /* Stash the offset at which we're writing from this file. */
2924
0
  curr_offset = pr_fsio_lseek(retr_fh, (off_t) 0, SEEK_CUR);
2925
0
  if (curr_offset != (off_t) -1) {
2926
0
    off_t *file_offset;
2927
2928
0
    file_offset = palloc(cmd->pool, sizeof(off_t));
2929
0
    *file_offset = (off_t) curr_offset;
2930
0
    (void) pr_table_add(cmd->notes, "mod_xfer.file-offset", file_offset,
2931
0
      sizeof(off_t));
2932
0
  }
2933
2934
  /* Block any timers for this section, where we want to prepare the
2935
   * data connection, then need to reprovision the session.xfer struct,
2936
   * and do NOT want timers (which may want/need that session.xfer data)
2937
   * to fire until after the reprovisioning (Bug#4168).
2938
   */
2939
0
  pr_alarms_block();
2940
2941
  /* Send the data */
2942
0
  pr_data_init(cmd->arg, PR_NETIO_IO_WR);
2943
2944
0
  session.xfer.path = dir;
2945
0
  session.xfer.file_size = st.st_size;
2946
2947
0
  pr_alarms_unblock();
2948
2949
0
  cnt_steps = session.xfer.file_size / 100;
2950
0
  if (cnt_steps == 0) {
2951
0
    cnt_steps = 1;
2952
0
  }
2953
2954
0
  if (session.range_len > 0) {
2955
0
    if (curr_pos + session.range_len > st.st_size) {
2956
      /* If the RANG end point is past the end of our file, ignore it and
2957
       * treat this as the remainder of the file, from the starting offset.
2958
       */
2959
0
      download_len = st.st_size - curr_pos;
2960
2961
0
    } else {
2962
0
      download_len = session.range_len;
2963
0
    }
2964
2965
0
  } else {
2966
0
    download_len = st.st_size - curr_pos;
2967
0
  }
2968
2969
0
  if (pr_data_open(cmd->arg, NULL, PR_NETIO_IO_WR, download_len) < 0) {
2970
0
    xerrno = errno;
2971
2972
0
    retr_abort(cmd->pool);
2973
0
    pr_data_abort(0, TRUE);
2974
2975
0
    pr_cmd_set_errno(cmd, xerrno);
2976
0
    errno = xerrno;
2977
0
    return PR_ERROR(cmd);
2978
0
  }
2979
2980
  /* Retrieve the number of bytes to retrieve, maximum, if present */
2981
0
  nbytes_max_retrieve = find_max_nbytes("MaxRetrieveFileSize");
2982
0
  if (nbytes_max_retrieve == 0UL) {
2983
0
    have_limit = FALSE;
2984
2985
0
  } else {
2986
0
    have_limit = TRUE;
2987
0
  }
2988
2989
  /* Check the MaxRetrieveFileSize.  If it is zero, or if the size
2990
   * of the file being retrieved is greater than the MaxRetrieveFileSize,
2991
   * then signal an error and abort the transfer now.
2992
   */
2993
0
  if (have_limit &&
2994
0
      ((nbytes_max_retrieve == 0) || (st.st_size > nbytes_max_retrieve))) {
2995
2996
0
    pr_log_pri(PR_LOG_NOTICE, "MaxRetrieveFileSize (%" PR_LU " %s) reached: "
2997
0
      "aborting transfer of '%s'", (pr_off_t) nbytes_max_retrieve,
2998
0
      nbytes_max_retrieve != 1 ? "bytes" : "byte", dir);
2999
3000
    /* Abort the transfer. */
3001
0
    retr_abort(cmd->pool);
3002
3003
    /* Set errno to EPERM ("Operation not permitted") */
3004
0
    pr_data_abort(EPERM, FALSE);
3005
3006
0
    pr_cmd_set_errno(cmd, EPERM);
3007
0
    errno = EPERM;
3008
0
    return PR_ERROR(cmd);
3009
0
  }
3010
3011
0
  bufsz = pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR);
3012
0
  lbuf = (char *) palloc(cmd->tmp_pool, bufsz);
3013
0
  pr_trace_msg("data", 8, "allocated download buffer of %lu bytes",
3014
0
    (unsigned long) bufsz);
3015
3016
0
  pr_scoreboard_entry_update(session.pid,
3017
0
    PR_SCORE_XFER_SIZE, download_len,
3018
0
    PR_SCORE_XFER_DONE, (off_t) 0,
3019
0
    NULL);
3020
3021
0
  if (session.range_len > 0) {
3022
0
    if (bufsz > session.range_len) {
3023
0
      bufsz = session.range_len;
3024
0
    }
3025
0
  }
3026
3027
0
  while (nbytes_sent != download_len) {
3028
0
    int update_scoreboard = FALSE;
3029
3030
0
    pr_signals_handle();
3031
3032
0
    if (XFER_ABORTED) {
3033
0
      break;
3034
0
    }
3035
3036
0
    len = transmit_data(cmd->pool, curr_offset, &curr_pos, lbuf, bufsz);
3037
0
    if (len == 0) {
3038
0
      break;
3039
0
    }
3040
3041
0
    if (len < 0) {
3042
      /* Make sure that the errno value, needed for the pr_data_abort() call,
3043
       * is preserved; errno itself might be overwritten in retr_abort().
3044
       */
3045
0
      int already_aborted = FALSE;
3046
3047
0
      xerrno = errno;
3048
0
      retr_abort(cmd->pool);
3049
3050
      /* Do we need to abort the data transfer here?  It's possible that
3051
       * the transfer has already been aborted, e.g. via the TCP OOB marker
3052
       * and/or the ABOR command.  And if that is the case, then calling
3053
       * pr_data_abort() here will only lead to a spurious response code
3054
       * (see Bug#4252).
3055
       *
3056
       * However, there are OTHER error conditions which would lead to this
3057
       * code path.  So we need to resort to some heuristics to differentiate
3058
       * between these cases.  The errno value checks match those in the
3059
       * pr_data_xfer() function, after the control channel has been polled
3060
       * for commands such as ABOR.
3061
       */
3062
3063
0
      if (session.d == NULL &&
3064
0
#if defined(ECONNABORTED)
3065
0
          xerrno == ECONNABORTED &&
3066
#elif defined(ENOTCONN)
3067
          xerrno == ENOTCONN &&
3068
#else
3069
          xerrno == EIO &&
3070
#endif
3071
0
          session.xfer.xfer_type == STOR_DEFAULT) {
3072
3073
        /* If the ABOR command has been sent, then pr_data_reset() and
3074
         * pr_data_cleanup() will have been called; the latter resets the
3075
         * xfer_type value to DEFAULT.
3076
         */
3077
0
        already_aborted = TRUE;
3078
0
      }
3079
3080
0
      if (already_aborted == FALSE) {
3081
0
        pr_data_abort(xerrno, FALSE);
3082
0
      }
3083
3084
0
      pr_cmd_set_errno(cmd, xerrno);
3085
0
      errno = xerrno;
3086
0
      return PR_ERROR(cmd);
3087
0
    }
3088
3089
0
    nbytes_sent += len;
3090
0
    curr_offset += len;
3091
3092
0
    if ((nbytes_sent / cnt_steps) != cnt_next) {
3093
0
      cnt_next = nbytes_sent / cnt_steps;
3094
3095
0
      update_scoreboard = TRUE;
3096
0
    }
3097
3098
    /* If no throttling is configured, this simply updates the scoreboard.
3099
     * In this case, we want to use session.xfer.total_bytes, rather than
3100
     * nbytes_sent, as the latter incorporates a REST position and the
3101
     * former does not.  (When handling STOR, this is not an issue: different
3102
     * end-of-loop conditions).
3103
     */
3104
0
    pr_throttle_pause(session.xfer.total_bytes, update_scoreboard, nbytes_sent);
3105
0
  }
3106
3107
0
  if (XFER_ABORTED) {
3108
0
    retr_abort(cmd->pool);
3109
0
    pr_data_abort(0, FALSE);
3110
3111
0
    pr_cmd_set_errno(cmd, EIO);
3112
0
    errno = EIO;
3113
0
    return PR_ERROR(cmd);
3114
0
  }
3115
3116
  /* If no throttling is configured, this simply updates the scoreboard.
3117
   * In this case, we want to use session.xfer.total_bytes, rather than
3118
   * nbytes_sent, as the latter incorporates a REST position and the
3119
   * former does not.  (When handling STOR, this is not an issue: different
3120
   * end-of-loop conditions).
3121
   */
3122
0
  pr_throttle_pause(session.xfer.total_bytes, TRUE, nbytes_sent);
3123
3124
0
  retr_complete(cmd->pool);
3125
0
  xfer_displayfile();
3126
0
  pr_data_close2();
3127
0
  pr_response_add(R_226, _("Transfer complete"));
3128
3129
0
  return PR_HANDLED(cmd);
3130
0
}
3131
3132
0
MODRET xfer_abor(cmd_rec *cmd) {
3133
0
  if (cmd->argc != 1) {
3134
0
    pr_response_add_err(R_500, _("'%s' not understood"),
3135
0
      pr_cmd_get_displayable_str(cmd, NULL));
3136
3137
0
    pr_cmd_set_errno(cmd, EINVAL);
3138
0
    errno = EINVAL;
3139
0
    return PR_ERROR(cmd);
3140
0
  }
3141
3142
0
  if (session.xfer.direction == PR_NETIO_IO_RD) {
3143
0
    stor_abort(cmd->pool);
3144
3145
0
  } else if (session.xfer.direction == PR_NETIO_IO_WR) {
3146
0
    retr_abort(cmd->pool);
3147
0
  }
3148
3149
0
  pr_data_abort(0, FALSE);
3150
3151
0
  pr_response_add(R_226, _("Abort successful"));
3152
0
  return PR_HANDLED(cmd);
3153
0
}
3154
3155
0
MODRET xfer_log_abor(cmd_rec *cmd) {
3156
3157
  /* Clean up the data connection info in the session structure. */
3158
0
  pr_data_reset();
3159
0
  pr_data_cleanup();
3160
3161
0
  return PR_DECLINED(cmd);
3162
0
}
3163
3164
0
MODRET xfer_type(cmd_rec *cmd) {
3165
0
  char *type;
3166
3167
0
  if (cmd->argc < 2 ||
3168
0
      cmd->argc > 3) {
3169
0
    pr_response_add_err(R_500, _("'%s' not understood"),
3170
0
      pr_cmd_get_displayable_str(cmd, NULL));
3171
3172
0
    pr_cmd_set_errno(cmd, EINVAL);
3173
0
    errno = EINVAL;
3174
0
    return PR_ERROR(cmd);
3175
0
  }
3176
3177
0
  type = pstrdup(cmd->tmp_pool, cmd->argv[1]);
3178
0
  type[0] = toupper((int) type[0]);
3179
3180
0
  if (strcmp(type, "A") == 0 ||
3181
0
      (cmd->argc == 3 &&
3182
0
       strcmp(type, "L") == 0 &&
3183
0
       strcmp(cmd->argv[2], "7") == 0)) {
3184
3185
    /* TYPE A(SCII) or TYPE L 7. */
3186
0
    session.sf_flags |= SF_ASCII;
3187
3188
0
  } else if (strcmp(type, "I") == 0 ||
3189
0
      (cmd->argc == 3 &&
3190
0
       strcmp(type, "L") == 0 &&
3191
0
       strcmp(cmd->argv[2], "8") == 0)) {
3192
3193
    /* TYPE I(MAGE) or TYPE L 8. */
3194
0
    session.sf_flags &= (SF_ALL^(SF_ASCII|SF_ASCII_OVERRIDE));
3195
3196
0
  } else {
3197
0
    pr_response_add_err(R_504, _("%s not implemented for '%s' parameter"),
3198
0
      (char *) cmd->argv[0], (char *) cmd->argv[1]);
3199
3200
0
    pr_cmd_set_errno(cmd, ENOSYS);
3201
0
    errno = ENOSYS;
3202
0
    return PR_ERROR(cmd);
3203
0
  }
3204
3205
  /* Note that the client may NOT be authenticated at this point in time.
3206
   * If that is the case, set a flag so that the POST_CMD PASS handler does
3207
   * not overwrite the TYPE command's setting.
3208
   *
3209
   * Alternatively, we COULD bar/reject any TYPE commands before authentication.
3210
   * However, I think that doing so would interfere with many existing clients
3211
   * which assume that they can send TYPE before authenticating.
3212
   */
3213
0
  if (session.auth_mech == NULL) {
3214
0
    have_type = TRUE;
3215
0
  }
3216
3217
0
  pr_response_add(R_200, _("Type set to %s"), (char *) cmd->argv[1]);
3218
0
  return PR_HANDLED(cmd);
3219
0
}
3220
3221
0
MODRET xfer_stru(cmd_rec *cmd) {
3222
0
  char *stru;
3223
3224
0
  if (cmd->argc != 2) {
3225
0
    pr_response_add_err(R_501, _("'%s' not understood"),
3226
0
      pr_cmd_get_displayable_str(cmd, NULL));
3227
3228
0
    pr_cmd_set_errno(cmd, EINVAL);
3229
0
    errno = EINVAL;
3230
0
    return PR_ERROR(cmd);
3231
0
  }
3232
3233
0
  stru = cmd->argv[1];
3234
0
  stru[0] = toupper((int) stru[0]);
3235
3236
0
  switch ((int) stru[0]) {
3237
0
    case 'F':
3238
      /* Should 202 be returned instead??? */
3239
0
      pr_response_add(R_200, _("Structure set to F"));
3240
0
      return PR_HANDLED(cmd);
3241
0
      break;
3242
3243
0
    case 'R':
3244
      /* Accept R but with no operational difference from F???
3245
       * R is required in minimum implementations by RFC-959, 5.1.
3246
       * RFC-1123, 4.1.2.13, amends this to only apply to servers whose file
3247
       * systems support record structures, but also suggests that such a
3248
       * server "may still accept files with STRU R, recording the byte stream
3249
       * literally." Another configurable choice, perhaps?
3250
       *
3251
       * NOTE: wu-ftpd does not so accept STRU R.
3252
       */
3253
3254
       /* FALLTHROUGH */
3255
3256
0
    case 'P':
3257
      /* RFC-1123 recommends against implementing P. */
3258
0
      pr_response_add_err(R_504, _("'%s' unsupported structure type"),
3259
0
        pr_cmd_get_displayable_str(cmd, NULL));
3260
3261
0
      pr_cmd_set_errno(cmd, ENOSYS);
3262
0
      errno = ENOSYS;
3263
0
      return PR_ERROR(cmd);
3264
3265
0
    default:
3266
0
      pr_response_add_err(R_501, _("'%s' unrecognized structure type"),
3267
0
        pr_cmd_get_displayable_str(cmd, NULL));
3268
3269
0
      pr_cmd_set_errno(cmd, EINVAL);
3270
0
      errno = EINVAL;
3271
0
      return PR_ERROR(cmd);
3272
0
  }
3273
0
}
3274
3275
0
MODRET xfer_mode(cmd_rec *cmd) {
3276
0
  char *mode;
3277
3278
0
  if (cmd->argc != 2) {
3279
0
    pr_response_add_err(R_501, _("'%s' not understood"),
3280
0
      pr_cmd_get_displayable_str(cmd, NULL));
3281
3282
0
    pr_cmd_set_errno(cmd, EINVAL);
3283
0
    errno = EINVAL;
3284
0
    return PR_ERROR(cmd);
3285
0
  }
3286
3287
0
  mode = cmd->argv[1];
3288
0
  mode[0] = toupper((int) mode[0]);
3289
3290
0
  switch ((int) mode[0]) {
3291
0
    case 'S':
3292
      /* Should 202 be returned instead??? */
3293
0
      pr_response_add(R_200, _("Mode set to S"));
3294
0
      return PR_HANDLED(cmd);
3295
3296
0
    case 'B':
3297
      /* FALLTHROUGH */
3298
3299
0
    case 'C':
3300
0
      pr_response_add_err(R_504, _("'%s' unsupported transfer mode"),
3301
0
        pr_cmd_get_displayable_str(cmd, NULL));
3302
3303
0
      pr_cmd_set_errno(cmd, ENOSYS);
3304
0
      errno = ENOSYS;
3305
0
      return PR_ERROR(cmd);
3306
0
  }
3307
3308
0
  pr_response_add_err(R_501, _("'%s' unrecognized transfer mode"),
3309
0
    pr_cmd_get_displayable_str(cmd, NULL));
3310
3311
0
  pr_cmd_set_errno(cmd, EINVAL);
3312
0
  errno = EINVAL;
3313
0
  return PR_ERROR(cmd);
3314
0
}
3315
3316
0
MODRET xfer_allo(cmd_rec *cmd) {
3317
0
  off_t requested_sz;
3318
0
  char *tmp = NULL;
3319
3320
  /* Even though we only handle the "ALLO <size>" command, we should not
3321
   * barf on the unlikely (but RFC-compliant) "ALLO <size> R <size>" commands.
3322
   * See RFC 959, Section 4.1.3.
3323
   */
3324
0
  if (cmd->argc != 2 &&
3325
0
      cmd->argc != 4) {
3326
0
    pr_response_add_err(R_504, _("'%s' not understood"),
3327
0
      pr_cmd_get_displayable_str(cmd, NULL));
3328
3329
0
    pr_cmd_set_errno(cmd, EINVAL);
3330
0
    errno = EINVAL;
3331
0
    return PR_ERROR(cmd);
3332
0
  }
3333
3334
0
#ifdef HAVE_STRTOULL
3335
0
  requested_sz = strtoull(cmd->argv[1], &tmp, 10);
3336
#else
3337
  requested_sz = strtoul(cmd->argv[1], &tmp, 10);
3338
#endif /* !HAVE_STRTOULL */
3339
3340
0
  if (tmp && *tmp) {
3341
0
    pr_response_add_err(R_504, _("%s: Invalid ALLO argument"), cmd->arg);
3342
3343
0
    pr_cmd_set_errno(cmd, EINVAL);
3344
0
    errno = EINVAL;
3345
0
    return PR_ERROR(cmd);
3346
0
  }
3347
3348
0
  if (xfer_opts & PR_XFER_OPT_HANDLE_ALLO) {
3349
0
    const char *path;
3350
0
    off_t avail_kb;
3351
0
    int res;
3352
3353
0
    path = pr_fs_getcwd();
3354
3355
0
    res = pr_fs_getsize2((char *) path, &avail_kb);
3356
0
    if (res < 0) {
3357
      /* If we can't check the filesystem stats for any reason, let the request
3358
       * proceed anyway.
3359
       */
3360
0
      pr_log_debug(DEBUG7,
3361
0
        "error getting available size for filesystem containing '%s': %s",
3362
0
        path, strerror(errno));
3363
0
      pr_response_add(R_202, _("No storage allocation necessary"));
3364
3365
0
    } else {
3366
0
      off_t requested_kb;
3367
3368
      /* The requested size is in bytes; the size returned from
3369
       * pr_fs_getsize2() is in KB.
3370
       */
3371
0
      requested_kb = requested_sz / 1024;
3372
3373
0
      if (requested_kb > avail_kb) {
3374
0
        pr_log_debug(DEBUG5, "%s requested %" PR_LU " KB, only %" PR_LU
3375
0
          " KB available on '%s'", (char *) cmd->argv[0],
3376
0
          (pr_off_t) requested_kb, (pr_off_t) avail_kb, path);
3377
0
        pr_response_add_err(R_552, "%s: %s", cmd->arg, strerror(ENOSPC));
3378
3379
0
        pr_cmd_set_errno(cmd, ENOSPC);
3380
0
        errno = ENOSPC;
3381
0
        return PR_ERROR(cmd);
3382
0
      }
3383
3384
0
      pr_log_debug(DEBUG9, "%s requested %" PR_LU " KB, %" PR_LU
3385
0
        " KB available on '%s'", (char *) cmd->argv[0], (pr_off_t) requested_kb,
3386
0
        (pr_off_t) avail_kb, path);
3387
0
      pr_response_add(R_200, _("%s command successful"), (char *) cmd->argv[0]);
3388
0
    }
3389
3390
0
  } else {
3391
0
    pr_response_add(R_202, _("No storage allocation necessary"));
3392
0
  }
3393
3394
0
  return PR_HANDLED(cmd);
3395
0
}
3396
3397
0
MODRET xfer_smnt(cmd_rec *cmd) {
3398
0
  pr_response_add(R_502, _("SMNT command not implemented"));
3399
0
  return PR_HANDLED(cmd);
3400
0
}
3401
3402
0
MODRET xfer_err_cleanup(cmd_rec *cmd) {
3403
3404
  /* If a hidden store was aborted, remove it. */
3405
0
  if (session.xfer.xfer_type == STOR_HIDDEN) {
3406
0
    unsigned char *delete_stores = NULL;
3407
3408
0
    delete_stores = get_param_ptr(CURRENT_CONF, "DeleteAbortedStores", FALSE);
3409
0
    if (delete_stores == NULL ||
3410
0
        *delete_stores == TRUE) {
3411
0
      if (session.xfer.path_hidden) {
3412
0
        pr_log_debug(DEBUG5, "removing aborted HiddenStores file '%s'",
3413
0
          session.xfer.path_hidden);
3414
0
        if (pr_fsio_unlink(session.xfer.path_hidden) < 0) {
3415
0
          if (errno != ENOENT) {
3416
0
            pr_log_debug(DEBUG0, "error deleting HiddenStores file '%s': %s",
3417
0
              session.xfer.path_hidden, strerror(errno));
3418
0
          }
3419
0
        }
3420
0
      }
3421
0
    }
3422
0
  }
3423
3424
0
  pr_data_reset();
3425
0
  pr_data_cleanup();
3426
3427
  /* Don't forget to clear any possible RANG/REST parameters as well. */
3428
0
  session.range_start = session.range_len = 0;
3429
0
  session.restart_pos = 0;
3430
3431
0
  return PR_DECLINED(cmd);
3432
0
}
3433
3434
0
MODRET xfer_log_stor(cmd_rec *cmd) {
3435
0
  _log_transfer('i', 'c');
3436
3437
  /* Increment the file counters. */
3438
0
  session.total_files_in++;
3439
0
  session.total_files_xfer++;
3440
3441
0
  pr_data_cleanup();
3442
3443
  /* Don't forget to clear any possible RANG/REST parameters as well. */
3444
0
  session.range_start = session.range_len = 0;
3445
0
  session.restart_pos = 0;
3446
3447
0
  return PR_DECLINED(cmd);
3448
0
}
3449
3450
0
MODRET xfer_log_retr(cmd_rec *cmd) {
3451
0
  _log_transfer('o', 'c');
3452
3453
  /* Increment the file counters. */
3454
0
  session.total_files_out++;
3455
0
  session.total_files_xfer++;
3456
3457
0
  pr_data_cleanup();
3458
3459
  /* Don't forget to clear any possible RANG/REST parameters as well. */
3460
0
  session.range_start = session.range_len = 0;
3461
0
  session.restart_pos = 0;
3462
3463
0
  return PR_DECLINED(cmd);
3464
0
}
3465
3466
0
static int noxfer_timeout_cb(CALLBACK_FRAME) {
3467
0
  int timeout;
3468
0
  const char *proto;
3469
3470
0
  timeout = pr_data_get_timeout(PR_DATA_TIMEOUT_NO_TRANSFER);
3471
3472
0
  if (session.sf_flags & SF_XFER) {
3473
0
    pr_trace_msg("timer", 4,
3474
0
      "TimeoutNoTransfer (%d %s) reached, but data transfer in progress, "
3475
0
      "ignoring", timeout, timeout != 1 ? "seconds" : "second");
3476
3477
    /* Transfer in progress, ignore this timeout */
3478
0
    return 1;
3479
0
  }
3480
3481
0
  pr_event_generate("core.timeout-no-transfer", NULL);
3482
0
  pr_response_send_async(R_421,
3483
0
    _("No transfer timeout (%d seconds): closing control connection"), timeout);
3484
3485
0
  pr_timer_remove(PR_TIMER_IDLE, ANY_MODULE);
3486
0
  pr_timer_remove(PR_TIMER_LOGIN, ANY_MODULE);
3487
3488
  /* If this timeout is encountered and we are expecting a passive transfer,
3489
   * add some logging that suggests things to check and possibly fix
3490
   * (e.g. network/firewall rules).
3491
   */
3492
0
  if (session.sf_flags & SF_PASSIVE) {
3493
0
    pr_log_pri(PR_LOG_NOTICE,
3494
0
      "Passive data transfer failed, possibly due to network issues");
3495
0
    pr_log_pri(PR_LOG_NOTICE,
3496
0
      "Check your PassivePorts and MasqueradeAddress settings,");
3497
0
    pr_log_pri(PR_LOG_NOTICE,
3498
0
       "and any router, NAT, and firewall rules in the network path.");
3499
0
  }
3500
3501
0
  proto = pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT);
3502
3503
0
  pr_log_pri(PR_LOG_NOTICE, "%s no transfer timeout, disconnected", proto);
3504
0
  pr_session_disconnect(&xfer_module, PR_SESS_DISCONNECT_TIMEOUT,
3505
0
    "TimeoutNoTransfer");
3506
3507
0
  return 0;
3508
0
}
3509
3510
0
MODRET xfer_post_pass(cmd_rec *cmd) {
3511
0
  config_rec *c;
3512
3513
  /* Default transfer mode is ASCII, per RFC 959, Section 3.1.1.1.  Unless
3514
   * the client has already sent a TYPE command.
3515
   */
3516
0
  if (have_type == FALSE) {
3517
0
    session.sf_flags |= SF_ASCII;
3518
0
    c = find_config(main_server->conf, CONF_PARAM, "DefaultTransferMode",
3519
0
      FALSE);
3520
0
    if (c != NULL) {
3521
0
      char *default_transfer_mode;
3522
3523
0
      default_transfer_mode = c->argv[0];
3524
0
      if (strcasecmp(default_transfer_mode, "binary") == 0) {
3525
0
        session.sf_flags &= (SF_ALL^SF_ASCII);
3526
0
      }
3527
0
    }
3528
0
  }
3529
3530
0
  c = find_config(TOPLEVEL_CONF, CONF_PARAM, "TimeoutNoTransfer", FALSE);
3531
0
  if (c != NULL) {
3532
0
    int timeout = *((int *) c->argv[0]);
3533
0
    pr_data_set_timeout(PR_DATA_TIMEOUT_NO_TRANSFER, timeout);
3534
3535
    /* Setup timer */
3536
0
    if (timeout > 0) {
3537
0
      pr_timer_add(timeout, PR_TIMER_NOXFER, &xfer_module, noxfer_timeout_cb,
3538
0
        "TimeoutNoTransfer");
3539
0
    }
3540
0
  }
3541
3542
0
  c = find_config(TOPLEVEL_CONF, CONF_PARAM, "TimeoutStalled", FALSE);
3543
0
  if (c != NULL) {
3544
0
    int timeout = *((int *) c->argv[0]);
3545
0
    pr_data_set_timeout(PR_DATA_TIMEOUT_STALLED, timeout);
3546
3547
    /* Note: timers for handling TimeoutStalled timeouts are handled in the
3548
     * data transfer routines, not here.
3549
     */
3550
0
  }
3551
3552
0
  c = find_config(main_server->conf, CONF_PARAM, "TransferOptions", FALSE);
3553
0
  while (c != NULL) {
3554
0
    unsigned long opts = 0;
3555
3556
0
    pr_signals_handle();
3557
3558
0
    opts = *((unsigned long *) c->argv[0]);
3559
0
    xfer_opts |= opts;
3560
3561
0
    c = find_config_next(c, c->next, CONF_PARAM, "TransferOptions", FALSE);
3562
0
  }
3563
3564
0
  if (xfer_opts & PR_XFER_OPT_IGNORE_ASCII) {
3565
0
    pr_log_debug(DEBUG8, "Ignoring ASCII translation for this session");
3566
0
    pr_data_ignore_ascii(TRUE);
3567
0
  }
3568
3569
  /* If we are chrooted, then skip actually processing the ALLO command
3570
   * (Bug#3996).
3571
   */
3572
0
  if (session.chroot_path != NULL) {
3573
0
    xfer_opts &= ~PR_XFER_OPT_HANDLE_ALLO;
3574
0
  }
3575
3576
0
  return PR_DECLINED(cmd);
3577
0
}
3578
3579
/* Configuration handlers
3580
 */
3581
3582
0
MODRET set_allowoverwrite(cmd_rec *cmd) {
3583
0
  int bool = -1;
3584
0
  config_rec *c = NULL;
3585
3586
0
  CHECK_ARGS(cmd, 1);
3587
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3588
0
    CONF_DIR|CONF_DYNDIR);
3589
3590
0
  bool = get_boolean(cmd, 1);
3591
0
  if (bool == -1)
3592
0
    CONF_ERROR(cmd, "expected boolean parameter");
3593
3594
0
  c = add_config_param(cmd->argv[0], 1, NULL);
3595
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3596
0
  *((unsigned char *) c->argv[0]) = (unsigned char) bool;
3597
0
  c->flags |= CF_MERGEDOWN;
3598
3599
0
  return PR_HANDLED(cmd);
3600
0
}
3601
3602
0
MODRET set_allowrestart(cmd_rec *cmd) {
3603
0
  int bool = -1;
3604
0
  config_rec *c = NULL;
3605
3606
0
  CHECK_ARGS(cmd, 1);
3607
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3608
0
    CONF_DIR|CONF_DYNDIR);
3609
3610
0
  bool = get_boolean(cmd, 1);
3611
0
  if (bool == -1)
3612
0
    CONF_ERROR(cmd, "expected boolean parameter");
3613
3614
0
  c = add_config_param(cmd->argv[0], 1, NULL);
3615
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3616
0
  *((unsigned char *) c->argv[0]) = bool;
3617
0
  c->flags |= CF_MERGEDOWN;
3618
3619
0
  return PR_HANDLED(cmd);
3620
0
}
3621
3622
/* usage: DefaultTransferMode ascii|binary */
3623
0
MODRET set_defaulttransfermode(cmd_rec *cmd) {
3624
0
  char *default_mode;
3625
3626
0
  CHECK_ARGS(cmd, 1);
3627
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3628
3629
0
  default_mode = cmd->argv[1];
3630
0
  if (strcasecmp(default_mode, "ascii") != 0 &&
3631
0
      strcasecmp(default_mode, "binary") != 0) {
3632
0
    CONF_ERROR(cmd, "parameter must be 'ascii' or 'binary'");
3633
0
  }
3634
3635
0
  add_config_param_str(cmd->argv[0], 1, default_mode);
3636
0
  return PR_HANDLED(cmd);
3637
0
}
3638
3639
0
MODRET set_deleteabortedstores(cmd_rec *cmd) {
3640
0
  int bool = -1;
3641
0
  config_rec *c = NULL;
3642
3643
0
  CHECK_ARGS(cmd, 1);
3644
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3645
0
    CONF_DIR|CONF_DYNDIR);
3646
3647
0
  bool = get_boolean(cmd, 1);
3648
0
  if (bool == -1)
3649
0
    CONF_ERROR(cmd, "expected Boolean parameter");
3650
3651
0
  c = add_config_param(cmd->argv[0], 1, NULL);
3652
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3653
0
  *((unsigned char *) c->argv[0]) = bool;
3654
0
  c->flags |= CF_MERGEDOWN;
3655
3656
0
  return PR_HANDLED(cmd);
3657
0
}
3658
3659
/* usage: DisplayFileTransfer path */
3660
0
MODRET set_displayfiletransfer(cmd_rec *cmd) {
3661
0
  CHECK_ARGS(cmd, 1);
3662
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3663
3664
0
  (void) add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
3665
0
  return PR_HANDLED(cmd);
3666
0
}
3667
3668
0
MODRET set_hiddenstores(cmd_rec *cmd) {
3669
0
  int enabled = -1, add_periods = TRUE;
3670
0
  config_rec *c = NULL;
3671
0
  char *prefix = NULL;
3672
3673
0
  CHECK_ARGS(cmd, 1);
3674
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR);
3675
3676
0
  c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
3677
3678
  /* Handle the case where the admin may, for some reason, want a custom
3679
   * prefix which could also be construed to be a Boolean value by
3680
   * get_boolean(): if the value begins AND ends with a period, then treat
3681
   * it as a custom prefix.
3682
   */
3683
0
  prefix = cmd->argv[1];
3684
0
  if (prefix[0] == '.' &&
3685
0
      prefix[strlen(prefix)-1] == '.') {
3686
0
    add_periods = FALSE;
3687
0
    enabled = -1;
3688
3689
0
  } else {
3690
0
    enabled = get_boolean(cmd, 1);
3691
0
  }
3692
3693
  /* If a suffix has been configured as well, assume that we do NOT
3694
   * automatically want periods.
3695
   */
3696
0
  if (cmd->argc == 3) {
3697
0
    add_periods = FALSE;
3698
0
  }
3699
3700
0
  if (enabled == -1) {
3701
    /* If the parameter is not a Boolean parameter, assume that the
3702
     * admin is configuring a specific prefix to use instead of the
3703
     * default ".in.".
3704
     */
3705
3706
0
    c->argv[0] = pcalloc(c->pool, sizeof(int));
3707
0
    *((int *) c->argv[0]) = TRUE;
3708
3709
0
    if (add_periods) {
3710
      /* Automatically add the leading and trailing periods. */
3711
0
      c->argv[1] = pstrcat(c->pool, ".", cmd->argv[1], ".", NULL);
3712
3713
0
    } else {
3714
0
      c->argv[1] = pstrdup(c->pool, cmd->argv[1]);
3715
0
    }
3716
3717
0
    if (cmd->argc == 3) {
3718
0
      c->argv[2] = pstrdup(c->pool, cmd->argv[2]);
3719
3720
0
    } else {
3721
0
      c->argv[2] = pstrdup(c->pool, ".");
3722
0
    }
3723
3724
0
  } else {
3725
0
    c->argv[0] = pcalloc(c->pool, sizeof(int));
3726
0
    *((int *) c->argv[0]) = enabled;
3727
3728
0
    if (enabled) {
3729
      /* The default HiddenStore prefix */
3730
0
      c->argv[1] = pstrdup(c->pool, ".in.");
3731
3732
      /* The default HiddenStores suffix. */
3733
0
      c->argv[2] = pstrdup(c->pool, ".");
3734
0
    }
3735
0
  }
3736
3737
0
  c->flags |= CF_MERGEDOWN;
3738
0
  return PR_HANDLED(cmd);
3739
0
}
3740
3741
0
MODRET set_maxfilesize(cmd_rec *cmd) {
3742
0
  config_rec *c = NULL;
3743
0
  off_t nbytes;
3744
0
  unsigned int precedence = 0;
3745
3746
0
  int ctxt = (cmd->config && cmd->config->config_type != CONF_PARAM ?
3747
0
     cmd->config->config_type : cmd->server->config_type ?
3748
0
     cmd->server->config_type : CONF_ROOT);
3749
3750
0
  if (cmd->argc-1 == 1) {
3751
0
    if (strcmp(cmd->argv[1], "*") != 0) {
3752
0
      CONF_ERROR(cmd, "incorrect number of parameters");
3753
0
    }
3754
3755
0
  } else if (cmd->argc-1 != 2 &&
3756
0
             cmd->argc-1 != 4) {
3757
0
    CONF_ERROR(cmd, "incorrect number of parameters");
3758
0
  }
3759
3760
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_ANON|CONF_VIRTUAL|CONF_GLOBAL|CONF_DIR|
3761
0
    CONF_DYNDIR);
3762
3763
  /* Set the precedence for this config_rec based on its configuration
3764
   * context.
3765
   */
3766
0
  if (ctxt & CONF_GLOBAL) {
3767
0
    precedence = 1;
3768
3769
  /* These will never appear simultaneously */
3770
0
  } else if ((ctxt & CONF_ROOT) ||
3771
0
             (ctxt & CONF_VIRTUAL)) {
3772
0
    precedence = 2;
3773
3774
0
  } else if (ctxt & CONF_ANON) {
3775
0
    precedence = 3;
3776
3777
0
  } else if (ctxt & CONF_DIR) {
3778
0
    precedence = 4;
3779
3780
0
  } else if (ctxt & CONF_DYNDIR) {
3781
0
    precedence = 5;
3782
0
  }
3783
3784
  /* If the directive was used with four arguments, it means the optional
3785
   * classifiers and expression were used.  Make sure the classifier is a valid
3786
   * one.
3787
   */
3788
0
  if (cmd->argc-1 == 4) {
3789
0
    if (strcasecmp(cmd->argv[3], "user") != 0 &&
3790
0
        strcasecmp(cmd->argv[3], "group") != 0 &&
3791
0
        strcasecmp(cmd->argv[3], "class") != 0) {
3792
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown classifier used: '",
3793
0
        cmd->argv[3], "'", NULL));
3794
0
    }
3795
0
  }
3796
3797
0
  if (cmd->argc-1 == 1) {
3798
    /* Do nothing here -- the "*" (the only parameter allowed if there is
3799
     * only a single parameter given) signifies an unlimited size, which is
3800
     * what the server provides by default.
3801
     */
3802
0
    nbytes = 0UL;
3803
3804
0
  } else {
3805
    /* Pass the cmd_rec off to see what number of bytes was
3806
     * requested/configured.
3807
     */
3808
0
    if (pr_str_get_nbytes(cmd->argv[1], cmd->argv[2], &nbytes) < 0) {
3809
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to parse: ",
3810
0
        cmd->argv[1], " ", cmd->argv[2], ": ", strerror(errno), NULL));
3811
0
    }
3812
3813
0
    if (nbytes == 0) {
3814
0
      CONF_ERROR(cmd, "size must be greater than zero");
3815
0
    }
3816
0
  }
3817
3818
0
  if (cmd->argc-1 == 1 ||
3819
0
      cmd->argc-1 == 2) {
3820
0
    c = add_config_param(cmd->argv[0], 2, NULL, NULL);
3821
0
    c->argv[0] = pcalloc(c->pool, sizeof(off_t));
3822
0
    *((off_t *) c->argv[0]) = nbytes;
3823
0
    c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
3824
0
    *((unsigned int *) c->argv[1]) = precedence;
3825
3826
0
  } else {
3827
0
    array_header *acl = NULL;
3828
0
    unsigned int argc;
3829
0
    void **argv;
3830
3831
0
    argc = cmd->argc - 4;
3832
0
    argv = cmd->argv + 3;
3833
3834
0
    acl = pr_expr_create(cmd->tmp_pool, &argc, (char **) argv);
3835
3836
0
    c = add_config_param(cmd->argv[0], 0);
3837
0
    c->argc = argc + 3;
3838
0
    c->argv = pcalloc(c->pool, ((argc + 4) * sizeof(void *)));
3839
3840
0
    argv = c->argv;
3841
3842
    /* Copy in the configured bytes */
3843
0
    *argv = pcalloc(c->pool, sizeof(unsigned long));
3844
0
    *((unsigned long *) *argv++) = nbytes;
3845
3846
    /* Copy in the precedence */
3847
0
    *argv = pcalloc(c->pool, sizeof(unsigned int));
3848
0
    *((unsigned int *) *argv++) = precedence;
3849
3850
    /* Copy in the classifier. */
3851
0
    *argv++ = pstrdup(c->pool, cmd->argv[3]);
3852
3853
0
    if (argc && acl) {
3854
0
      while (argc--) {
3855
0
        *argv++ = pstrdup(c->pool, *((char **) acl->elts));
3856
0
        acl->elts = ((char **) acl->elts) + 1;
3857
0
      }
3858
0
    }
3859
3860
    /* Don't forget the terminating NULL */
3861
0
    *argv = NULL;
3862
0
  }
3863
3864
0
  c->flags |= CF_MERGEDOWN_MULTI;
3865
3866
0
  return PR_HANDLED(cmd);
3867
0
}
3868
3869
/* usage: MaxTransfersPerHost cmdlist count [msg] */
3870
0
MODRET set_maxtransfersperhost(cmd_rec *cmd) {
3871
0
  config_rec *c = NULL;
3872
0
  int count = 0;
3873
3874
0
  if (cmd->argc-1 < 2 ||
3875
0
      cmd->argc-1 > 3) {
3876
0
    CONF_ERROR(cmd, "bad number of parameters");
3877
0
  }
3878
3879
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3880
0
    CONF_DIR|CONF_DYNDIR);
3881
3882
0
  count = atoi(cmd->argv[2]);
3883
0
  if (count < 1)
3884
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "count must be greater than zero: '",
3885
0
      cmd->argv[2], "'", NULL));
3886
3887
0
  c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
3888
3889
  /* Parse the command list. */
3890
0
  if (xfer_parse_cmdlist(cmd->argv[0], c, cmd->argv[1]) < 0) {
3891
0
    CONF_ERROR(cmd, "error with command list");
3892
0
  }
3893
3894
0
  c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
3895
0
  *((unsigned int *) c->argv[1]) = count;
3896
3897
0
  if (cmd->argc-1 == 3) {
3898
0
    c->argv[2] = pstrdup(c->pool, cmd->argv[3]);
3899
0
  }
3900
3901
0
  c->flags |= CF_MERGEDOWN_MULTI;
3902
3903
0
  return PR_HANDLED(cmd);
3904
0
}
3905
3906
/* usage: MaxTransfersPerUser cmdlist count [msg] */
3907
0
MODRET set_maxtransfersperuser(cmd_rec *cmd) {
3908
0
  config_rec *c = NULL;
3909
0
  int count = 0;
3910
3911
0
  if (cmd->argc-1 < 2 ||
3912
0
      cmd->argc-1 > 3) {
3913
0
    CONF_ERROR(cmd, "bad number of parameters");
3914
0
  }
3915
3916
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3917
0
    CONF_DIR|CONF_DYNDIR);
3918
3919
0
  count = atoi(cmd->argv[2]);
3920
0
  if (count < 1) {
3921
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "count must be greater than zero: '",
3922
0
      cmd->argv[2], "'", NULL));
3923
0
  }
3924
3925
0
  c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
3926
3927
  /* Parse the command list. */
3928
0
  if (xfer_parse_cmdlist(cmd->argv[0], c, cmd->argv[1]) < 0) {
3929
0
    CONF_ERROR(cmd, "error with command list");
3930
0
  }
3931
3932
0
  c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
3933
0
  *((unsigned int *) c->argv[1]) = count;
3934
3935
0
  if (cmd->argc-1 == 3) {
3936
0
    c->argv[2] = pstrdup(c->pool, cmd->argv[3]);
3937
0
  }
3938
3939
0
  c->flags |= CF_MERGEDOWN_MULTI;
3940
3941
0
  return PR_HANDLED(cmd);
3942
0
}
3943
3944
0
MODRET set_storeuniqueprefix(cmd_rec *cmd) {
3945
0
  config_rec *c = NULL;
3946
3947
0
  CHECK_ARGS(cmd, 1);
3948
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3949
0
    CONF_DIR|CONF_DYNDIR);
3950
3951
  /* make sure there are no slashes in the prefix */
3952
0
  if (strchr(cmd->argv[1], '/') != NULL) {
3953
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "no slashes allowed in prefix: '",
3954
0
      cmd->argv[1], "'", NULL));
3955
0
  }
3956
3957
0
  c = add_config_param_str(cmd->argv[0], 1, (void *) cmd->argv[1]);
3958
0
  c->flags |= CF_MERGEDOWN;
3959
3960
0
  return PR_HANDLED(cmd);
3961
0
}
3962
3963
0
MODRET set_timeoutnoxfer(cmd_rec *cmd) {
3964
0
  int timeout = -1;
3965
0
  config_rec *c = NULL;
3966
3967
0
  CHECK_ARGS(cmd, 1);
3968
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3969
3970
0
  if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
3971
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
3972
0
      cmd->argv[1], "': ", strerror(errno), NULL));
3973
0
  }
3974
3975
0
  c = add_config_param(cmd->argv[0], 1, NULL);
3976
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
3977
0
  *((int *) c->argv[0]) = timeout;
3978
0
  c->flags |= CF_MERGEDOWN;
3979
3980
0
  return PR_HANDLED(cmd);
3981
0
}
3982
3983
0
MODRET set_timeoutstalled(cmd_rec *cmd) {
3984
0
  int timeout = -1;
3985
0
  config_rec *c = NULL;
3986
3987
0
  CHECK_ARGS(cmd, 1);
3988
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3989
3990
0
  if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
3991
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
3992
0
      cmd->argv[1], "': ", strerror(errno), NULL));
3993
0
  }
3994
3995
0
  c = add_config_param(cmd->argv[0], 1, NULL);
3996
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
3997
0
  *((int *) c->argv[0]) = timeout;
3998
0
  c->flags |= CF_MERGEDOWN;
3999
4000
0
  return PR_HANDLED(cmd);
4001
0
}
4002
4003
/* usage: TransferOptions opt1 opt2 ... */
4004
0
MODRET set_transferoptions(cmd_rec *cmd) {
4005
0
  config_rec *c = NULL;
4006
0
  register unsigned int i = 0;
4007
0
  unsigned long opts = 0UL;
4008
4009
0
  if (cmd->argc-1 == 0) {
4010
0
    CONF_ERROR(cmd, "wrong number of parameters");
4011
0
  }
4012
4013
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
4014
4015
0
  c = add_config_param(cmd->argv[0], 1, NULL);
4016
4017
0
  for (i = 1; i < cmd->argc; i++) {
4018
0
    if (strcasecmp(cmd->argv[i], "IgnoreASCII") == 0) {
4019
0
      opts |= PR_XFER_OPT_IGNORE_ASCII;
4020
4021
0
    } else if (strcasecmp(cmd->argv[i], "AllowSymlinkUpload") == 0 ||
4022
0
               strcasecmp(cmd->argv[i], "AllowSymlinkUploads") == 0) {
4023
0
      opts |= PR_XFER_OPT_ALLOW_SYMLINK_UPLOAD;
4024
4025
0
    } else {
4026
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown TransferOption '",
4027
0
        cmd->argv[i], "'", NULL));
4028
0
    }
4029
0
  }
4030
4031
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned long));
4032
0
  *((unsigned long *) c->argv[0]) = opts;
4033
4034
0
  return PR_HANDLED(cmd);
4035
0
}
4036
4037
/* usage: TransferRate cmds kbps[:free-bytes] ["user"|"group"|"class"
4038
 *          expression]
4039
 */
4040
0
MODRET set_transferrate(cmd_rec *cmd) {
4041
0
  config_rec *c = NULL;
4042
0
  char *tmp = NULL, *endp = NULL;
4043
0
  long double rate = 0.0;
4044
0
  off_t freebytes = 0;
4045
0
  unsigned int precedence = 0;
4046
4047
0
  int ctxt = (cmd->config && cmd->config->config_type != CONF_PARAM ?
4048
0
     cmd->config->config_type : cmd->server->config_type ?
4049
0
     cmd->server->config_type : CONF_ROOT);
4050
4051
  /* Must have two or four parameters */
4052
0
  if (cmd->argc-1 != 2 &&
4053
0
      cmd->argc-1 != 4) {
4054
0
    CONF_ERROR(cmd, "wrong number of parameters");
4055
0
  }
4056
4057
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
4058
0
    CONF_DIR|CONF_DYNDIR);
4059
4060
  /* Set the precedence for this config_rec based on its configuration
4061
   * context.
4062
   */
4063
0
  if (ctxt & CONF_GLOBAL) {
4064
0
    precedence = 1;
4065
4066
  /* These will never appear simultaneously */
4067
0
  } else if (ctxt & CONF_ROOT || ctxt & CONF_VIRTUAL) {
4068
0
    precedence = 2;
4069
4070
0
  } else if (ctxt & CONF_ANON) {
4071
0
    precedence = 3;
4072
4073
0
  } else if (ctxt & CONF_DIR) {
4074
0
    precedence = 4;
4075
4076
  /* Note: by tweaking this value to be lower than the precedence for
4077
   * <Directory> appearances of this directive, I can effectively cause
4078
   * any .ftpaccess appearances not to override...
4079
   */
4080
0
  } else if (ctxt & CONF_DYNDIR) {
4081
0
    precedence = 5;
4082
0
  }
4083
4084
  /* Check for a valid classifier. */
4085
0
  if (cmd->argc-1 > 2) {
4086
0
    if (strcasecmp(cmd->argv[3], "user") != 0 &&
4087
0
        strcasecmp(cmd->argv[3], "group") != 0 &&
4088
0
        strcasecmp(cmd->argv[3], "class") != 0) {
4089
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown classifier requested: '",
4090
0
        cmd->argv[3], "'", NULL));
4091
0
    }
4092
0
  }
4093
4094
0
  tmp = strchr(cmd->argv[2], ':');
4095
0
  if (tmp != NULL) {
4096
0
    *tmp = '\0';
4097
0
  }
4098
4099
  /* Parse the 'kbps' part.  Ideally, we'd be using strtold(3) rather than
4100
   * strtod(3) here, but FreeBSD doesn't have strtold(3).  Yay.  Portability.
4101
   */
4102
0
  rate = (long double) strtod(cmd->argv[2], &endp);
4103
4104
0
  if (rate < 0.0) {
4105
0
    CONF_ERROR(cmd, "rate must be greater than zero");
4106
0
  }
4107
4108
0
  if (endp && *endp) {
4109
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid number: '",
4110
0
      cmd->argv[2], "'", NULL));
4111
0
  }
4112
4113
  /* Parse any 'free-bytes' part */
4114
0
  if (tmp) {
4115
0
    cmd->argv[2] = ++tmp;
4116
4117
0
    freebytes = strtoul(cmd->argv[2], &endp, 10);
4118
0
    if (endp && *endp) {
4119
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid number: '",
4120
0
        cmd->argv[2], "'", NULL));
4121
0
    }
4122
0
  }
4123
4124
  /* Construct the config_rec */
4125
0
  if (cmd->argc-1 == 2) {
4126
0
    c = add_config_param(cmd->argv[0], 4, NULL, NULL, NULL, NULL);
4127
4128
    /* Parse the command list. */
4129
0
    if (xfer_parse_cmdlist(cmd->argv[0], c, cmd->argv[1]) < 0) {
4130
0
      CONF_ERROR(cmd, "error with command list");
4131
0
    }
4132
4133
0
    c->argv[1] = pcalloc(c->pool, sizeof(long double));
4134
0
    *((long double *) c->argv[1]) = rate;
4135
0
    c->argv[2] = pcalloc(c->pool, sizeof(off_t));
4136
0
    *((off_t *) c->argv[2]) = freebytes;
4137
0
    c->argv[3] = pcalloc(c->pool, sizeof(unsigned int));
4138
0
    *((unsigned int *) c->argv[3]) = precedence;
4139
4140
0
  } else {
4141
0
    array_header *acl = NULL;
4142
0
    unsigned int argc;
4143
0
    void **argv;
4144
4145
0
    argc = cmd->argc - 4;
4146
0
    argv = cmd->argv + 3;
4147
4148
0
    acl = pr_expr_create(cmd->tmp_pool, &argc, (char **) argv);
4149
0
    c = add_config_param(cmd->argv[0], 0);
4150
4151
    /* Parse the command list.
4152
     *
4153
     * The five additional slots are for: cmd-list, bps, free-bytes,
4154
     * precedence, user/group/class.
4155
     */
4156
0
    c->argc = argc + 5;
4157
4158
0
    c->argv = pcalloc(c->pool, ((c->argc + 1) * sizeof(void *)));
4159
0
    argv = c->argv;
4160
4161
0
    if (xfer_parse_cmdlist(cmd->argv[0], c, cmd->argv[1]) < 0) {
4162
0
      CONF_ERROR(cmd, "error with command list");
4163
0
    }
4164
4165
    /* Note: the command list is at index 0, hence this increment. */
4166
0
    argv++;
4167
4168
0
    *argv = pcalloc(c->pool, sizeof(long double));
4169
0
    *((long double *) *argv++) = rate;
4170
0
    *argv = pcalloc(c->pool, sizeof(off_t));
4171
0
    *((unsigned long *) *argv++) = freebytes;
4172
0
    *argv = pcalloc(c->pool, sizeof(unsigned int));
4173
0
    *((unsigned int *) *argv++) = precedence;
4174
4175
0
    *argv++ = pstrdup(c->pool, cmd->argv[3]);
4176
4177
0
    if (argc && acl) {
4178
0
      while (argc--) {
4179
0
        *argv++ = pstrdup(c->pool, *((char **) acl->elts));
4180
0
        acl->elts = ((char **) acl->elts) + 1;
4181
0
      }
4182
0
    }
4183
4184
    /* don't forget the terminating NULL */
4185
0
    *argv = NULL;
4186
0
  }
4187
4188
0
  c->flags |= CF_MERGEDOWN_MULTI;
4189
0
  return PR_HANDLED(cmd);
4190
0
}
4191
4192
/* usage: UseSendfile on|off|"len units"|percentage"%" */
4193
0
MODRET set_usesendfile(cmd_rec *cmd) {
4194
0
  int bool = -1;
4195
0
  off_t sendfile_len = 0;
4196
0
  float sendfile_pct = -1.0;
4197
0
  config_rec *c;
4198
4199
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR|CONF_DYNDIR);
4200
4201
0
  if (cmd->argc-1 == 1) {
4202
    /* Is the given parameter a boolean, or a percentage?  Try parsing it a
4203
     * boolean first.
4204
     */
4205
0
    bool = get_boolean(cmd, 1);
4206
0
    if (bool == -1) {
4207
0
      char *arg;
4208
0
      size_t arglen;
4209
4210
      /* See if the given parameter is a percentage. */
4211
0
      arg = cmd->argv[1];
4212
0
      arglen = strlen(arg);
4213
0
      if (arglen > 1 &&
4214
0
          arg[arglen-1] == '%') {
4215
0
        char *ptr = NULL;
4216
4217
0
        arg[arglen-1] = '\0';
4218
4219
0
#ifdef HAVE_STRTOF
4220
0
        sendfile_pct = strtof(arg, &ptr);
4221
#elif HAVE_STRTOD
4222
        sendfile_pct = strtod(arg, &ptr);
4223
#else
4224
        sendfile_pct = atof(arg);
4225
#endif /* !HAVE_STRTOF and !HAVE_STRTOD */
4226
4227
0
        if (ptr && *ptr) {
4228
0
          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad percentage value '",
4229
0
            arg, "%'", NULL));
4230
0
        }
4231
4232
0
        sendfile_pct /= 100.0;
4233
0
        bool = TRUE;
4234
4235
0
      } else {
4236
0
        CONF_ERROR(cmd, "expected Boolean parameter");
4237
0
      }
4238
0
    }
4239
4240
0
  } else if (cmd->argc-1 == 2) {
4241
0
    off_t nbytes;
4242
4243
0
    if (pr_str_get_nbytes(cmd->argv[1], cmd->argv[2], &nbytes) < 0) {
4244
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to parse: ",
4245
0
        cmd->argv[1], " ", cmd->argv[2], ": ", strerror(errno), NULL));
4246
0
    }
4247
4248
0
    sendfile_len = nbytes;
4249
0
    bool = TRUE;
4250
4251
0
  } else {
4252
0
    CONF_ERROR(cmd, "wrong number of parameters");
4253
0
  }
4254
4255
0
  c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
4256
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
4257
0
  *((unsigned char *) c->argv[0]) = bool;
4258
0
  c->argv[1] = pcalloc(c->pool, sizeof(off_t));
4259
0
  *((off_t *) c->argv[1]) = sendfile_len;
4260
0
  c->argv[2] = pcalloc(c->pool, sizeof(float));
4261
0
  *((float *) c->argv[2]) = sendfile_pct;
4262
4263
0
  c->flags |= CF_MERGEDOWN;
4264
0
  return PR_HANDLED(cmd);
4265
0
}
4266
4267
/* Event handlers
4268
 */
4269
4270
0
static void xfer_exit_ev(const void *event_data, void *user_data) {
4271
4272
0
  if (stor_fh != NULL) {
4273
     /* An upload is occurring... */
4274
0
    pr_trace_msg(trace_channel, 6, "session exiting, aborting upload");
4275
0
    stor_abort(session.pool);
4276
4277
0
  } else if (retr_fh != NULL) {
4278
    /* A download is occurring... */
4279
0
    pr_trace_msg(trace_channel, 6, "session exiting, aborting download");
4280
0
    retr_abort(session.pool);
4281
0
  }
4282
4283
0
  if (session.sf_flags & SF_XFER) {
4284
0
    cmd_rec *cmd = NULL;
4285
4286
0
    pr_data_abort(0, FALSE);
4287
4288
0
    cmd = session.curr_cmd_rec;
4289
0
    if (cmd == NULL) {
4290
0
      cmd = pr_cmd_alloc(session.pool, 2, session.curr_cmd, session.xfer.path);
4291
0
    }
4292
4293
0
    (void) pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
4294
0
    (void) pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
4295
0
  }
4296
0
}
4297
4298
0
static void xfer_sess_reinit_ev(const void *event_data, void *user_data) {
4299
0
  int res;
4300
4301
  /* A HOST command changed the main_server pointer, reinitialize ourselves. */
4302
4303
0
  pr_event_unregister(&xfer_module, "core.exit", xfer_exit_ev);
4304
0
  pr_event_unregister(&xfer_module, "core.session-reinit", xfer_sess_reinit_ev);
4305
0
  pr_event_unregister(&xfer_module, "core.signal.USR2", xfer_sigusr2_ev);
4306
0
  pr_event_unregister(&xfer_module, "core.timeout-stalled",
4307
0
    xfer_timeout_stalled_ev);
4308
4309
0
  if (displayfilexfer_fh != NULL) {
4310
0
    (void) pr_fsio_close(displayfilexfer_fh);
4311
0
    displayfilexfer_fh = NULL;
4312
0
  }
4313
4314
0
  res = xfer_sess_init();
4315
0
  if (res < 0) {
4316
0
    pr_session_disconnect(&xfer_module,
4317
0
      PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
4318
0
  }
4319
0
}
4320
4321
0
static void xfer_sigusr2_ev(const void *event_data, void *user_data) {
4322
4323
0
  if (pr_module_exists("mod_shaper.c")) {
4324
    /* Only do this if we're currently involved in a data transfer.
4325
     * This is a hack put in to support mod_shaper's antics.
4326
     */
4327
0
    if (session.curr_cmd_id == PR_CMD_APPE_ID ||
4328
0
        session.curr_cmd_id == PR_CMD_RETR_ID ||
4329
0
        session.curr_cmd_id == PR_CMD_STOR_ID ||
4330
0
        session.curr_cmd_id == PR_CMD_STOU_ID) {
4331
0
      pool *tmp_pool;
4332
0
      cmd_rec *cmd;
4333
4334
0
      tmp_pool = make_sub_pool(session.pool);
4335
0
      pr_pool_tag(tmp_pool, "Data Transfer SIGUSR2 pool");
4336
4337
0
      cmd = pr_cmd_alloc(tmp_pool, 1, session.curr_cmd);
4338
4339
      /* Rescan the config tree for TransferRates, picking up any possible
4340
       * changes.
4341
       */
4342
0
      pr_log_debug(DEBUG2, "rechecking TransferRates");
4343
0
      pr_throttle_init(cmd);
4344
4345
0
      destroy_pool(tmp_pool);
4346
0
    }
4347
0
  }
4348
0
}
4349
4350
0
static void xfer_timedout(const char *reason) {
4351
0
  if (stor_fh != NULL) {
4352
0
    pr_trace_msg(trace_channel, 6, "%s, aborting upload", reason);
4353
0
    stor_abort(session.pool);
4354
4355
0
  } else if (retr_fh != NULL) {
4356
0
    pr_trace_msg(trace_channel, 6, "%s, aborting download", reason);
4357
0
    retr_abort(session.pool);
4358
0
  }
4359
0
}
4360
4361
0
static void xfer_timeout_session_ev(const void *event_data, void *user_data) {
4362
0
  xfer_timedout("session timeout");
4363
0
}
4364
4365
0
static void xfer_timeout_stalled_ev(const void *event_data, void *user_data) {
4366
  /* In this event handler, the "else" case, for a stalled transfer, will
4367
   * be handled by the 'core.exit' event handler above.  For in that
4368
   * scenario, a data transfer WILL have actually been in progress,
4369
   * whereas in the !SF_XFER case, the client requested a transfer, but
4370
   * never actually opened the data connection.
4371
   */
4372
4373
0
  if (!(session.sf_flags & SF_XFER)) {
4374
0
    xfer_timedout("transfer stalled");
4375
0
  }
4376
0
}
4377
4378
/* Initialization routines
4379
 */
4380
4381
0
static int xfer_init(void) {
4382
4383
  /* Add the commands handled by this module to the HELP list. */
4384
0
  pr_help_add(C_TYPE, _("<sp> type-code (A, I, L 7, L 8)"), TRUE);
4385
0
  pr_help_add(C_STRU, _("is not implemented (always F)"), TRUE);
4386
0
  pr_help_add(C_MODE, _("is not implemented (always S)"), TRUE);
4387
0
  pr_help_add(C_RETR, _("<sp> pathname"), TRUE);
4388
0
  pr_help_add(C_STOR, _("<sp> pathname"), TRUE);
4389
0
  pr_help_add(C_STOU, _("(store unique filename)"), TRUE);
4390
0
  pr_help_add(C_APPE, _("<sp> pathname"), TRUE);
4391
0
  pr_help_add(C_REST, _("<sp> byte-count"), TRUE);
4392
0
  pr_help_add(C_ABOR, _("(abort current operation)"), TRUE);
4393
0
  pr_help_add(C_RANG, _("<sp> start-point <sp> end-point"), TRUE);
4394
4395
  /* Add the additional features implemented by this module into the
4396
   * list, to be displayed in response to a FEAT command.
4397
   */
4398
0
  pr_feat_add(C_RANG " STREAM");
4399
4400
0
  return 0;
4401
0
}
4402
4403
0
static int xfer_sess_init(void) {
4404
0
  char *displayfilexfer = NULL;
4405
4406
  /* Exit handlers for HiddenStores cleanup */
4407
0
  pr_event_register(&xfer_module, "core.exit", xfer_exit_ev, NULL);
4408
0
  pr_event_register(&xfer_module, "core.session-reinit", xfer_sess_reinit_ev,
4409
0
    NULL);
4410
0
  pr_event_register(&xfer_module, "core.signal.USR2", xfer_sigusr2_ev,
4411
0
    NULL);
4412
0
  pr_event_register(&xfer_module, "core.timeout-session",
4413
0
    xfer_timeout_session_ev, NULL);
4414
0
  pr_event_register(&xfer_module, "core.timeout-stalled",
4415
0
    xfer_timeout_stalled_ev, NULL);
4416
4417
0
  have_type = FALSE;
4418
4419
  /* Look for a DisplayFileTransfer file which has an absolute path.  If we
4420
   * find one, open a filehandle, such that that file can be displayed
4421
   * even if the session is chrooted.  DisplayFileTransfer files with
4422
   * relative paths will be handled after chroot, preserving the old
4423
   * behavior.
4424
   */
4425
0
  displayfilexfer = get_param_ptr(main_server->conf, "DisplayFileTransfer",
4426
0
    FALSE);
4427
0
  if (displayfilexfer &&
4428
0
      *displayfilexfer == '/') {
4429
0
    struct stat st;
4430
4431
0
    displayfilexfer_fh = pr_fsio_open(displayfilexfer, O_RDONLY);
4432
0
    if (displayfilexfer_fh == NULL) {
4433
0
      pr_log_debug(DEBUG6, "unable to open DisplayFileTransfer file '%s': %s",
4434
0
        displayfilexfer, strerror(errno));
4435
4436
0
    } else {
4437
0
      if (pr_fsio_fstat(displayfilexfer_fh, &st) < 0) {
4438
0
        pr_log_debug(DEBUG6, "error checking DisplayFileTransfer file '%s': %s",
4439
0
          displayfilexfer, strerror(errno));
4440
0
        pr_fsio_close(displayfilexfer_fh);
4441
0
        displayfilexfer_fh = NULL;
4442
4443
0
      } else {
4444
0
        if (S_ISDIR(st.st_mode)) {
4445
0
          errno = EISDIR;
4446
4447
0
          pr_log_debug(DEBUG6,
4448
0
            "unable to use DisplayFileTransfer file '%s': %s",
4449
0
            displayfilexfer, strerror(errno));
4450
0
          pr_fsio_close(displayfilexfer_fh);
4451
0
          displayfilexfer_fh = NULL;
4452
0
        }
4453
0
      }
4454
0
    }
4455
0
  }
4456
4457
  /* IF the RFC2228 mechanism is "TLS" at this point in time, then set the flag
4458
   * to disable use of sendfile; the client is probably an FTPS client using
4459
   * implicit SSL (Bug#4073).
4460
   */
4461
0
  if (session.rfc2228_mech != NULL &&
4462
0
      strcmp(session.rfc2228_mech, "TLS") == 0) {
4463
0
    have_rfc2228_data = TRUE;
4464
0
  }
4465
4466
0
  return 0;
4467
0
}
4468
4469
/* Module API tables
4470
 */
4471
4472
static conftable xfer_conftab[] = {
4473
  { "AllowOverwrite",   set_allowoverwrite,   NULL },
4474
  { "AllowRetrieveRestart", set_allowrestart,   NULL },
4475
  { "AllowStoreRestart",  set_allowrestart,   NULL },
4476
  { "DefaultTransferMode",  set_defaulttransfermode,  NULL },
4477
  { "DeleteAbortedStores",  set_deleteabortedstores,  NULL },
4478
  { "DisplayFileTransfer",  set_displayfiletransfer,  NULL },
4479
  { "HiddenStores",   set_hiddenstores,   NULL },
4480
  { "MaxRetrieveFileSize",  set_maxfilesize,    NULL },
4481
  { "MaxStoreFileSize",   set_maxfilesize,    NULL },
4482
  { "MaxTransfersPerHost",  set_maxtransfersperhost,  NULL },
4483
  { "MaxTransfersPerUser",  set_maxtransfersperuser,  NULL },
4484
  { "StoreUniquePrefix",  set_storeuniqueprefix,    NULL },
4485
  { "TimeoutNoTransfer",  set_timeoutnoxfer,    NULL },
4486
  { "TimeoutStalled",   set_timeoutstalled,   NULL },
4487
  { "TransferOptions",    set_transferoptions,    NULL },
4488
  { "TransferRate",   set_transferrate,   NULL },
4489
  { "UseSendfile",    set_usesendfile,    NULL },
4490
4491
  { NULL }
4492
};
4493
4494
static cmdtable xfer_cmdtab[] = {
4495
  { CMD,     C_TYPE,  G_NONE,  xfer_type, FALSE,  FALSE, CL_MISC },
4496
  { CMD,     C_STRU,  G_NONE,  xfer_stru, TRUE, FALSE, CL_MISC },
4497
  { CMD,     C_MODE,  G_NONE,  xfer_mode, TRUE, FALSE, CL_MISC },
4498
  { POST_CMD,C_MODE,  G_NONE,  xfer_post_mode,FALSE,  FALSE },
4499
  { CMD,     C_ALLO,  G_NONE,  xfer_allo, TRUE, FALSE, CL_MISC },
4500
  { CMD,     C_SMNT,  G_NONE,  xfer_smnt, TRUE, FALSE, CL_MISC },
4501
  { PRE_CMD, C_RETR,  G_READ,  xfer_pre_retr, TRUE, FALSE },
4502
  { CMD,     C_RETR,  G_READ,  xfer_retr, TRUE, FALSE, CL_READ },
4503
  { POST_CMD,C_RETR,  G_NONE,  xfer_post_retr,FALSE,  FALSE },
4504
  { LOG_CMD, C_RETR,  G_NONE,  xfer_log_retr, FALSE,  FALSE },
4505
  { LOG_CMD_ERR, C_RETR,G_NONE,  xfer_err_cleanup,  FALSE,  FALSE },
4506
  { PRE_CMD, C_STOR,  G_WRITE, xfer_pre_stor, TRUE, FALSE },
4507
  { CMD,     C_STOR,  G_WRITE, xfer_stor, TRUE, FALSE, CL_WRITE },
4508
  { POST_CMD,C_STOR,  G_NONE,  xfer_post_stor,FALSE,  FALSE },
4509
  { LOG_CMD, C_STOR,    G_NONE,  xfer_log_stor, FALSE,  FALSE },
4510
  { LOG_CMD_ERR, C_STOR,G_NONE,  xfer_err_cleanup,  FALSE,  FALSE },
4511
  { PRE_CMD, C_STOU,  G_WRITE, xfer_pre_stou, TRUE, FALSE },
4512
  { CMD,     C_STOU,  G_WRITE, xfer_stor, TRUE, FALSE, CL_WRITE },
4513
  { POST_CMD,C_STOU,  G_WRITE, xfer_post_stou,FALSE,  FALSE },
4514
  { LOG_CMD, C_STOU,  G_NONE,  xfer_log_stor, FALSE,  FALSE },
4515
  { LOG_CMD_ERR, C_STOU,G_NONE,  xfer_err_cleanup,  FALSE,  FALSE },
4516
  { PRE_CMD, C_APPE,  G_WRITE, xfer_pre_appe, TRUE, FALSE },
4517
  { CMD,     C_APPE,  G_WRITE, xfer_stor, TRUE, FALSE, CL_WRITE },
4518
  { POST_CMD,C_APPE,  G_NONE,  xfer_post_stor,FALSE,  FALSE },
4519
  { LOG_CMD, C_APPE,  G_NONE,  xfer_log_stor, FALSE,  FALSE },
4520
  { LOG_CMD_ERR, C_APPE,G_NONE,  xfer_err_cleanup,  FALSE,  FALSE },
4521
  { CMD,     C_ABOR,  G_NONE,  xfer_abor, TRUE, TRUE,  CL_MISC  },
4522
  { LOG_CMD, C_ABOR,  G_NONE,  xfer_log_abor, TRUE, TRUE,  CL_MISC  },
4523
  { CMD,     C_OPTS "_REST", G_NONE, xfer_opts_rest, FALSE, FALSE },
4524
  { CMD,     C_REST,  G_NONE,  xfer_rest, TRUE, FALSE, CL_MISC  },
4525
  { CMD,     C_RANG,  G_NONE,  xfer_rang, TRUE, FALSE, CL_MISC  },
4526
  { POST_CMD,C_PROT,  G_NONE,  xfer_post_prot,  FALSE,  FALSE },
4527
  { POST_CMD,C_PASS,  G_NONE,  xfer_post_pass,  FALSE, FALSE },
4528
  { 0, NULL }
4529
};
4530
4531
module xfer_module = {
4532
  NULL, NULL,
4533
4534
  /* Module API version */
4535
  0x20,
4536
4537
  /* Module name */
4538
  "xfer",
4539
4540
  /* Module configuration directive table */
4541
  xfer_conftab,
4542
4543
  /* Module command handler table */
4544
  xfer_cmdtab,
4545
4546
  /* Module authentication handler table */
4547
  NULL,
4548
4549
  /* Module initialization function */
4550
  xfer_init,
4551
4552
  /* Session initialization function */
4553
  xfer_sess_init
4554
};