Coverage Report

Created: 2026-05-14 06:38

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