1 /*
2 * ProFTPD: mod_site_misc -- a module implementing miscellaneous SITE commands
3 * Copyright (c) 2004-2020 The ProFTPD Project
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, The ProFTPD Project team and other respective
20 * copyright holders give permission to link this program with OpenSSL, and
21 * distribute the resulting executable, without including the source code for
22 * OpenSSL in the source distribution.
23 */
24
25 #include "conf.h"
26
27 #define MOD_SITE_MISC_VERSION "mod_site_misc/1.6"
28
29 extern pr_response_t *resp_list, *resp_err_list;
30
31 module site_misc_module;
32
33 static unsigned int site_misc_engine = TRUE;
34
35 /* Necessary prototypes */
36 static int site_misc_sess_init(void);
37
site_misc_check_filters(cmd_rec * cmd,const char * path)38 static int site_misc_check_filters(cmd_rec *cmd, const char *path) {
39 #ifdef PR_USE_REGEX
40 pr_regex_t *pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE);
41 if (pre != NULL &&
42 pr_regexp_exec(pre, path, 0, NULL, 0, 0, 0) != 0) {
43 pr_log_pri(PR_LOG_NOTICE, MOD_SITE_MISC_VERSION
44 ": 'SITE %s' denied by PathAllowFilter", cmd->arg);
45 return -1;
46 }
47
48 pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE);
49 if (pre != NULL &&
50 pr_regexp_exec(pre, path, 0, NULL, 0, 0, 0) == 0) {
51 pr_log_pri(PR_LOG_NOTICE, MOD_SITE_MISC_VERSION
52 ": 'SITE %s' denied by PathDenyFilter", cmd->arg);
53 return -1;
54 }
55 #endif
56
57 return 0;
58 }
59
site_misc_create_dir(const char * dir)60 static int site_misc_create_dir(const char *dir) {
61 struct stat st;
62 int res;
63
64 pr_fs_clear_cache2(dir);
65 res = pr_fsio_stat(dir, &st);
66 if (res < 0 &&
67 errno != ENOENT) {
68 int xerrno = errno;
69
70 pr_log_debug(DEBUG2, MOD_SITE_MISC_VERSION ": error checking '%s': %s",
71 dir, strerror(xerrno));
72
73 errno = xerrno;
74 return -1;
75 }
76
77 if (res == 0) {
78 /* Directory already exists */
79 return 1;
80 }
81
82 if (pr_fsio_mkdir(dir, 0777) < 0) {
83 int xerrno = errno;
84
85 pr_log_debug(DEBUG2, MOD_SITE_MISC_VERSION ": error creating '%s': %s",
86 dir, strerror(xerrno));
87
88 errno = xerrno;
89 return -1;
90 }
91
92 return 0;
93 }
94
site_misc_create_path(pool * p,const char * path)95 static int site_misc_create_path(pool *p, const char *path) {
96 struct stat st;
97 char *curr_path, *tmp_path;
98
99 pr_fs_clear_cache2(path);
100 if (pr_fsio_stat(path, &st) == 0) {
101 return 0;
102 }
103
104 /* The given path should already be canonicalized; we do not need to worry
105 * if it is relative to the current working directory or not.
106 */
107
108 tmp_path = pstrdup(p, path);
109
110 curr_path = "/";
111 while (tmp_path &&
112 *tmp_path) {
113 char *curr_dir;
114 int res;
115 cmd_rec *cmd;
116 pool *sub_pool;
117
118 pr_signals_handle();
119
120 curr_dir = strsep(&tmp_path, "/");
121 curr_path = pdircat(p, curr_path, curr_dir, NULL);
122
123 /* Dispatch the fake C_MKD command, e.g. for mod_quotatab. */
124 sub_pool = pr_pool_create_sz(p, 64);
125 cmd = pr_cmd_alloc(sub_pool, 2, pstrdup(sub_pool, C_MKD),
126 pstrdup(sub_pool, curr_path));
127 cmd->arg = pstrdup(cmd->pool, curr_path);
128 cmd->cmd_class = CL_DIRS|CL_WRITE;
129
130 res = pr_cmd_dispatch_phase(cmd, PRE_CMD, 0);
131 if (res < 0) {
132 int xerrno = errno;
133
134 pr_log_debug(DEBUG3, MOD_SITE_MISC_VERSION
135 ": creating directory '%s' blocked by MKD handler: %s", curr_path,
136 strerror(xerrno));
137
138 pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
139 pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
140 pr_response_clear(&resp_err_list);
141
142 destroy_pool(sub_pool);
143 sub_pool = NULL;
144 cmd = NULL;
145
146 errno = xerrno;
147 return -1;
148 }
149
150 res = site_misc_create_dir(curr_path);
151 if (res < 0) {
152 int xerrno = errno;
153
154 pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
155 pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
156 pr_response_clear(&resp_err_list);
157
158 destroy_pool(sub_pool);
159 sub_pool = NULL;
160 cmd = NULL;
161
162 errno = xerrno;
163 return -1;
164 }
165
166 pr_cmd_dispatch_phase(cmd, POST_CMD, 0);
167 pr_cmd_dispatch_phase(cmd, LOG_CMD, 0);
168 pr_response_clear(&resp_list);
169
170 destroy_pool(sub_pool);
171 sub_pool = NULL;
172 cmd = NULL;
173 }
174
175 return 0;
176 }
177
site_misc_delete_dir(pool * p,const char * dir)178 static int site_misc_delete_dir(pool *p, const char *dir) {
179 void *dirh;
180 struct dirent *dent;
181 int res;
182 cmd_rec *cmd;
183 pool *sub_pool;
184
185 dirh = pr_fsio_opendir(dir);
186 if (dirh == NULL)
187 return -1;
188
189 while ((dent = pr_fsio_readdir(dirh)) != NULL) {
190 struct stat st;
191 char *file;
192
193 pr_signals_handle();
194
195 if (strncmp(dent->d_name, ".", 2) == 0 ||
196 strncmp(dent->d_name, "..", 3) == 0)
197 continue;
198
199 file = pdircat(p, dir, dent->d_name, NULL);
200
201 if (pr_fsio_stat(file, &st) < 0)
202 continue;
203
204 if (S_ISDIR(st.st_mode)) {
205 res = site_misc_delete_dir(p, file);
206 if (res < 0) {
207 int xerrno = errno;
208
209 pr_fsio_closedir(dirh);
210
211 errno = xerrno;
212 return -1;
213 }
214
215 } else {
216
217 /* Dispatch fake C_DELE command, e.g. for mod_quotatab */
218 sub_pool = pr_pool_create_sz(p, 64);
219 cmd = pr_cmd_alloc(sub_pool, 2, pstrdup(sub_pool, C_DELE),
220 pstrdup(sub_pool, file));
221 cmd->arg = pstrdup(cmd->pool, file);
222 cmd->cmd_class = CL_WRITE;
223
224 pr_response_block(TRUE);
225 res = pr_cmd_dispatch_phase(cmd, PRE_CMD, 0);
226 if (res < 0) {
227 int xerrno = errno;
228
229 pr_log_debug(DEBUG3, MOD_SITE_MISC_VERSION
230 ": deleting file '%s' blocked by DELE handler: %s", file,
231 strerror(xerrno));
232
233 pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
234 pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
235 pr_response_clear(&resp_err_list);
236 pr_response_block(FALSE);
237
238 destroy_pool(sub_pool);
239 pr_fsio_closedir(dirh);
240
241 errno = xerrno;
242 return -1;
243 }
244
245 res = pr_fsio_unlink(file);
246 if (res < 0) {
247 int xerrno = errno;
248
249 pr_fsio_closedir(dirh);
250
251 pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
252 pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
253 pr_response_clear(&resp_err_list);
254 pr_response_block(FALSE);
255
256 destroy_pool(sub_pool);
257 pr_fsio_closedir(dirh);
258
259 errno = xerrno;
260 return -1;
261 }
262
263 pr_response_add(R_250, _("%s command successful"), (char *) cmd->argv[0]);
264 pr_cmd_dispatch_phase(cmd, POST_CMD, 0);
265 pr_cmd_dispatch_phase(cmd, LOG_CMD, 0);
266 pr_response_clear(&resp_list);
267 destroy_pool(sub_pool);
268 pr_response_block(FALSE);
269 }
270 }
271
272 pr_fsio_closedir(dirh);
273
274 /* Dispatch fake C_RMD command, e.g. for mod_quotatab */
275 sub_pool = pr_pool_create_sz(p, 64);
276 cmd = pr_cmd_alloc(sub_pool, 2, pstrdup(sub_pool, C_RMD),
277 pstrdup(sub_pool, dir));
278 cmd->arg = pstrdup(cmd->pool, dir);
279 cmd->cmd_class = CL_DIRS|CL_WRITE;
280
281 pr_response_block(TRUE);
282 res = pr_cmd_dispatch_phase(cmd, PRE_CMD, 0);
283 if (res < 0) {
284 int xerrno = errno;
285
286 pr_log_debug(DEBUG3, MOD_SITE_MISC_VERSION
287 ": removing directory '%s' blocked by RMD handler: %s", dir,
288 strerror(xerrno));
289
290 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
291 pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
292 pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
293 pr_response_clear(&resp_err_list);
294 pr_response_block(FALSE);
295
296 destroy_pool(sub_pool);
297
298 errno = xerrno;
299 return -1;
300 }
301
302 res = pr_fsio_rmdir(dir);
303 if (res < 0) {
304 int xerrno = errno;
305
306 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
307 pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
308 pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
309 pr_response_clear(&resp_err_list);
310 pr_response_block(FALSE);
311
312 destroy_pool(sub_pool);
313 errno = xerrno;
314 return -1;
315 }
316
317 pr_response_add(R_257, _("\"%s\" - Directory successfully created"),
318 quote_dir(cmd->tmp_pool, (char *) dir));
319 pr_cmd_dispatch_phase(cmd, POST_CMD, 0);
320 pr_cmd_dispatch_phase(cmd, LOG_CMD, 0);
321 pr_response_clear(&resp_list);
322 pr_response_block(FALSE);
323 destroy_pool(sub_pool);
324
325 return 0;
326 }
327
site_misc_delete_path(pool * p,const char * path)328 static int site_misc_delete_path(pool *p, const char *path) {
329 struct stat st;
330
331 pr_fs_clear_cache2(path);
332 if (pr_fsio_stat(path, &st) < 0) {
333 return -1;
334 }
335
336 if (!S_ISDIR(st.st_mode)) {
337 errno = EINVAL;
338 return -1;
339 }
340
341 return site_misc_delete_dir(p, path);
342 }
343
344 /* Parse a timestamp string of the form "YYYYMMDDhhmm[ss]" into its
345 * individual components.
346 *
347 * We assume that the caller has already ensured that the given timestamp
348 * string is long enough, i.e. 12 or 14 characters long.
349 */
site_misc_parsetime(char * timestamp,size_t timestamp_len,unsigned int * year,unsigned int * month,unsigned int * day,unsigned int * hour,unsigned int * min,unsigned int * sec)350 static int site_misc_parsetime(char *timestamp, size_t timestamp_len,
351 unsigned int *year, unsigned int *month, unsigned int *day,
352 unsigned int *hour, unsigned int *min, unsigned int *sec) {
353 register unsigned int i;
354 char c, *ptr;
355 int have_secs = FALSE, valid_timestamp = TRUE;
356
357 /* Make sure the timestamp is comprised of all digits. */
358 for (i = 0; i < timestamp_len; i++) {
359 if (PR_ISDIGIT((int) timestamp[i]) == 0) {
360 valid_timestamp = FALSE;
361 break;
362 }
363 }
364
365 if (!valid_timestamp) {
366 pr_log_debug(DEBUG7, MOD_SITE_MISC_VERSION
367 ": timestamp '%s' contains non-digits", timestamp);
368 errno = EINVAL;
369 return -1;
370 }
371
372 if (timestamp_len == 14) {
373 have_secs = TRUE;
374 }
375
376 ptr = timestamp;
377 c = timestamp[4];
378 timestamp[4] = '\0';
379 *year = atoi(ptr);
380 timestamp[4] = c;
381
382 ptr = &(timestamp[4]);
383 c = timestamp[6];
384 timestamp[6] = '\0';
385 *month = atoi(ptr);
386 timestamp[6] = c;
387
388 if (*month > 12) {
389 pr_log_debug(DEBUG7, MOD_SITE_MISC_VERSION
390 ": bad number of months in '%s' (%u)", timestamp, *month);
391 errno = EINVAL;
392 return -1;
393 }
394
395 ptr = &(timestamp[6]);
396 c = timestamp[8];
397 timestamp[8] = '\0';
398 *day = atoi(ptr);
399 timestamp[8] = c;
400
401 if (*day > 31) {
402 pr_log_debug(DEBUG7, MOD_SITE_MISC_VERSION
403 ": bad number of days in '%s' (%u)", timestamp, *day);
404 errno = EINVAL;
405 return -1;
406 }
407
408 ptr = &(timestamp[8]);
409 c = timestamp[10];
410 timestamp[10] = '\0';
411 *hour = atoi(ptr);
412 timestamp[10] = c;
413
414 if (*hour > 24) {
415 pr_log_debug(DEBUG7, MOD_SITE_MISC_VERSION
416 ": bad number of hours in '%s' (%u)", timestamp, *hour);
417 errno = EINVAL;
418 return -1;
419 }
420
421 ptr = &(timestamp[10]);
422
423 /* Handle optional seconds. */
424 if (have_secs) {
425 c = timestamp[12];
426 timestamp[12] = '\0';
427 }
428
429 *min = atoi(ptr);
430
431 if (have_secs) {
432 timestamp[12] = c;
433 }
434
435 if (*min > 60) {
436 pr_log_debug(DEBUG7, MOD_SITE_MISC_VERSION
437 ": bad number of minutes in '%s' (%u)", timestamp, *min);
438 errno = EINVAL;
439 return -1;
440 }
441
442 if (have_secs) {
443 ptr = &(timestamp[12]);
444 *sec = atoi(ptr);
445
446 if (*sec > 60) {
447 pr_log_debug(DEBUG7, MOD_SITE_MISC_VERSION
448 ": bad number of seconds in '%s' (%u)", timestamp, *sec);
449 errno = EINVAL;
450 return -1;
451 }
452 }
453
454 return 0;
455 }
456
site_misc_mktime(unsigned int year,unsigned int month,unsigned int mday,unsigned int hour,unsigned int min,unsigned int sec)457 static time_t site_misc_mktime(unsigned int year, unsigned int month,
458 unsigned int mday, unsigned int hour, unsigned int min, unsigned int sec) {
459 struct tm tm;
460 time_t res;
461 char *env;
462
463 #ifdef HAVE_TZNAME
464 char *tzname_dup[2];
465
466 /* The mktime(3) function has a nasty habit of changing the tzname global
467 * variable as a side-effect. This can cause problems, as when the process
468 * has become chrooted, and mktime(3) sets/changes tzname wrong. (For more
469 * information on the tzname global variable, see the tzset(3) man page.)
470 *
471 * The best way to deal with this issue (which is especially prominent
472 * on systems running glibc-2.3 or later, which is particularly ill-behaved
473 * in a chrooted environment, as it assumes the ability to find system
474 * timezone files at paths which are no longer valid within the chroot)
475 * is to set the TZ environment variable explicitly, before starting
476 * proftpd. You can also use the SetEnv configuration directive within
477 * the proftpd.conf to set the TZ environment variable, e.g.:
478 *
479 * SetEnv TZ PST
480 *
481 * To try to help sites which fail to do this, the tzname global variable
482 * will be copied prior to the mktime(3) call, and the copy restored after
483 * the call. (Note that calling the ctime(3) and localtime(3) functions also
484 * causes a similar overwriting/setting of the tzname environment variable.)
485 */
486 memcpy(&tzname_dup, tzname, sizeof(tzname_dup));
487 #endif /* HAVE_TZNAME */
488
489 env = pr_env_get(session.pool, "TZ");
490
491 /* Set the TZ environment to be GMT, so that mktime(3) treats the timestamp
492 * provided by the client as being in GMT/UTC.
493 */
494 if (pr_env_set(session.pool, "TZ", "GMT") < 0) {
495 pr_log_debug(DEBUG8, MOD_SITE_MISC_VERSION
496 ": error setting TZ environment variable to 'GMT': %s", strerror(errno));
497 }
498
499 tm.tm_sec = sec;
500 tm.tm_min = min;
501 tm.tm_hour = hour;
502 tm.tm_mday = mday;
503 tm.tm_mon = (month - 1);
504 tm.tm_year = (year - 1900);
505 tm.tm_wday = 0;
506 tm.tm_yday = 0;
507 tm.tm_isdst = -1;
508
509 res = mktime(&tm);
510
511 /* Restore the old TZ setting, if any. */
512 if (env) {
513 if (pr_env_set(session.pool, "TZ", env) < 0) {
514 pr_log_debug(DEBUG8, MOD_SITE_MISC_VERSION
515 ": error setting TZ environment variable to '%s': %s", env,
516 strerror(errno));
517 }
518 }
519
520 #ifdef HAVE_TZNAME
521 /* Restore the old tzname values prior to returning. */
522 memcpy(tzname, tzname_dup, sizeof(tzname_dup));
523 #endif /* HAVE_TZNAME */
524
525 return res;
526 }
527
528 /* Configuration handlers
529 */
530
531 /* usage: SiteMiscEngine on|off */
set_sitemiscengine(cmd_rec * cmd)532 MODRET set_sitemiscengine(cmd_rec *cmd) {
533 config_rec *c;
534 int bool;
535
536 CHECK_ARGS(cmd, 1);
537 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
538
539 bool = get_boolean(cmd, 1);
540 if (bool == -1)
541 CONF_ERROR(cmd, "expected Boolean parameter");
542
543 c = add_config_param(cmd->argv[0], 1, NULL);
544 c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
545 *((unsigned int *) c->argv[0]) = bool;
546
547 return PR_HANDLED(cmd);
548 }
549
550 /* Command handlers
551 */
552
site_misc_mkdir(cmd_rec * cmd)553 MODRET site_misc_mkdir(cmd_rec *cmd) {
554 if (!site_misc_engine) {
555 return PR_DECLINED(cmd);
556 }
557
558 if (cmd->argc < 2) {
559 pr_log_debug(DEBUG5, MOD_SITE_MISC_VERSION
560 "%s : wrong number of parameters (%d)", (char *) cmd->argv[0], cmd->argc);
561 return PR_DECLINED(cmd);
562 }
563
564 if (strncasecmp(cmd->argv[1], "MKDIR", 6) == 0) {
565 register unsigned int i;
566 char *cmd_name, *decoded_path, *path = "";
567 unsigned char *authenticated;
568
569 if (cmd->argc < 3) {
570 return PR_DECLINED(cmd);
571 }
572
573 authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
574 if (authenticated == NULL ||
575 *authenticated == FALSE) {
576 pr_response_add_err(R_530, _("Please login with USER and PASS"));
577
578 pr_cmd_set_errno(cmd, EPERM);
579 errno = EPERM;
580 return PR_ERROR(cmd);
581 }
582
583 for (i = 2; i < cmd->argc; i++) {
584 path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", cmd->argv[i], NULL);
585 }
586
587 decoded_path = pr_fs_decode_path2(cmd->tmp_pool, path,
588 FSIO_DECODE_FL_TELL_ERRORS);
589 if (decoded_path == NULL) {
590 int xerrno = errno;
591
592 pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", path,
593 strerror(xerrno));
594 pr_response_add_err(R_550,
595 _("%s: Illegal character sequence in filename"), path);
596
597 pr_cmd_set_errno(cmd, xerrno);
598 errno = xerrno;
599 return PR_ERROR(cmd);
600 }
601
602 path = decoded_path;
603
604 if (site_misc_check_filters(cmd, path) < 0) {
605 int xerrno = EPERM;
606
607 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
608
609 pr_cmd_set_errno(cmd, xerrno);
610 errno = xerrno;
611 return PR_ERROR(cmd);
612 }
613
614 path = dir_canonical_path(cmd->tmp_pool, path);
615 if (path == NULL) {
616 int xerrno = EINVAL;
617
618 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
619
620 pr_cmd_set_errno(cmd, xerrno);
621 errno = xerrno;
622 return PR_ERROR(cmd);
623 }
624
625 cmd_name = cmd->argv[0];
626 cmd->argv[0] = "SITE_MKDIR";
627 if (!dir_check_canon(cmd->tmp_pool, cmd, G_WRITE, path, NULL)) {
628 int xerrno = EPERM;
629
630 cmd->argv[0] = cmd_name;
631
632 pr_log_debug(DEBUG4, MOD_SITE_MISC_VERSION
633 ": %s command denied by <Limit>", (char *) cmd->argv[0]);
634 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
635
636 pr_cmd_set_errno(cmd, xerrno);
637 errno = xerrno;
638 return PR_ERROR(cmd);
639 }
640 cmd->argv[0] = cmd_name;
641
642 if (site_misc_create_path(cmd->tmp_pool, path) < 0) {
643 int xerrno = errno;
644
645 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
646
647 pr_cmd_set_errno(cmd, xerrno);
648 errno = xerrno;
649 return PR_ERROR(cmd);
650 }
651
652 pr_response_add(R_200, _("SITE %s command successful"),
653 (char *) cmd->argv[1]);
654 return PR_HANDLED(cmd);
655 }
656
657 if (strncasecmp(cmd->argv[1], "HELP", 5) == 0) {
658 pr_response_add(R_214, "MKDIR <sp> path");
659 }
660
661 return PR_DECLINED(cmd);
662 }
663
site_misc_rmdir(cmd_rec * cmd)664 MODRET site_misc_rmdir(cmd_rec *cmd) {
665 if (!site_misc_engine) {
666 return PR_DECLINED(cmd);
667 }
668
669 if (cmd->argc < 2) {
670 pr_log_debug(DEBUG5, MOD_SITE_MISC_VERSION
671 "%s : wrong number of parameters (%d)", (char *) cmd->argv[0], cmd->argc);
672 return PR_DECLINED(cmd);
673 }
674
675 if (strncasecmp(cmd->argv[1], "RMDIR", 6) == 0) {
676 register unsigned int i;
677 char *cmd_name, *decoded_path, *path = "";
678 unsigned char *authenticated;
679
680 if (cmd->argc < 3)
681 return PR_DECLINED(cmd);
682
683 authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
684
685 if (!authenticated ||
686 *authenticated == FALSE) {
687 pr_response_add_err(R_530, _("Please login with USER and PASS"));
688
689 pr_cmd_set_errno(cmd, EPERM);
690 errno = EPERM;
691 return PR_ERROR(cmd);
692 }
693
694 for (i = 2; i < cmd->argc; i++) {
695 path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", cmd->argv[i], NULL);
696 }
697
698 decoded_path = pr_fs_decode_path2(cmd->tmp_pool, path,
699 FSIO_DECODE_FL_TELL_ERRORS);
700 if (decoded_path == NULL) {
701 int xerrno = errno;
702
703 pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", path,
704 strerror(xerrno));
705 pr_response_add_err(R_550,
706 _("%s: Illegal character sequence in filename"), path);
707
708 pr_cmd_set_errno(cmd, xerrno);
709 errno = xerrno;
710 return PR_ERROR(cmd);
711 }
712
713 path = dir_canonical_path(cmd->tmp_pool, decoded_path);
714 if (path == NULL) {
715 int xerrno = EINVAL;
716
717 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
718
719 pr_cmd_set_errno(cmd, xerrno);
720 errno = xerrno;
721 return PR_ERROR(cmd);
722 }
723
724 cmd_name = cmd->argv[0];
725 cmd->argv[0] = "SITE_RMDIR";
726 if (!dir_check_canon(cmd->tmp_pool, cmd, G_WRITE, path, NULL)) {
727 int xerrno = EPERM;
728
729 cmd->argv[0] = cmd_name;
730
731 pr_log_debug(DEBUG4, MOD_SITE_MISC_VERSION
732 ": %s command denied by <Limit>", (char *) cmd->argv[0]);
733 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
734
735 pr_cmd_set_errno(cmd, xerrno);
736 errno = xerrno;
737 return PR_ERROR(cmd);
738 }
739 cmd->argv[0] = cmd_name;
740
741 if (site_misc_delete_path(cmd->tmp_pool, path) < 0) {
742 int xerrno = errno;
743
744 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
745
746 pr_cmd_set_errno(cmd, xerrno);
747 errno = xerrno;
748 return PR_ERROR(cmd);
749 }
750
751 pr_response_add(R_200, _("SITE %s command successful"),
752 (char *) cmd->argv[1]);
753 return PR_HANDLED(cmd);
754 }
755
756 if (strncasecmp(cmd->argv[1], "HELP", 5) == 0) {
757 pr_response_add(R_214, "RMDIR <sp> path");
758 }
759
760 return PR_DECLINED(cmd);
761 }
762
site_misc_symlink(cmd_rec * cmd)763 MODRET site_misc_symlink(cmd_rec *cmd) {
764 if (!site_misc_engine) {
765 return PR_DECLINED(cmd);
766 }
767
768 if (cmd->argc < 2) {
769 pr_log_debug(DEBUG5, MOD_SITE_MISC_VERSION
770 "%s : wrong number of parameters (%d)", (char *) cmd->argv[0], cmd->argc);
771 return PR_DECLINED(cmd);
772 }
773
774 if (strncasecmp(cmd->argv[1], "SYMLINK", 8) == 0) {
775 struct stat st;
776 int res;
777 char *cmd_name, *decoded_path, *src, *dst;
778 unsigned char *authenticated;
779
780 if (cmd->argc < 4)
781 return PR_DECLINED(cmd);
782
783 authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
784
785 if (!authenticated ||
786 *authenticated == FALSE) {
787 pr_response_add_err(R_530, _("Please login with USER and PASS"));
788
789 pr_cmd_set_errno(cmd, EPERM);
790 errno = EPERM;
791 return PR_ERROR(cmd);
792 }
793
794 decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[2],
795 FSIO_DECODE_FL_TELL_ERRORS);
796 if (decoded_path == NULL) {
797 int xerrno = errno;
798
799 pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s",
800 (char *) cmd->argv[2], strerror(xerrno));
801 pr_response_add_err(R_550,
802 _("%s: Illegal character sequence in filename"), (char *) cmd->argv[2]);
803
804 pr_cmd_set_errno(cmd, xerrno);
805 errno = xerrno;
806 return PR_ERROR(cmd);
807 }
808
809 src = dir_canonical_path(cmd->tmp_pool, decoded_path);
810 if (src == NULL) {
811 int xerrno = EINVAL;
812
813 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
814
815 errno = xerrno;
816 return PR_ERROR(cmd);
817 }
818
819 cmd_name = cmd->argv[0];
820 cmd->argv[0] = "SITE_SYMLINK";
821 if (!dir_check_canon(cmd->tmp_pool, cmd, G_READ, src, NULL)) {
822 int xerrno = EPERM;
823
824 cmd->argv[0] = cmd_name;
825
826 pr_log_debug(DEBUG4, MOD_SITE_MISC_VERSION
827 ": %s command denied by <Limit>", (char *) cmd->argv[0]);
828 pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[2],
829 strerror(xerrno));
830
831 errno = xerrno;
832 return PR_ERROR(cmd);
833 }
834
835 decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[3],
836 FSIO_DECODE_FL_TELL_ERRORS);
837 if (decoded_path == NULL) {
838 int xerrno = errno;
839
840 pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s",
841 (char *) cmd->argv[3], strerror(xerrno));
842 pr_response_add_err(R_550,
843 _("%s: Illegal character sequence in filename"), (char *) cmd->argv[3]);
844
845 pr_cmd_set_errno(cmd, xerrno);
846 errno = xerrno;
847 return PR_ERROR(cmd);
848 }
849
850 dst = dir_canonical_path(cmd->tmp_pool, decoded_path);
851 if (dst == NULL) {
852 int xerrno = EINVAL;
853
854 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
855
856 errno = xerrno;
857 return PR_ERROR(cmd);
858 }
859
860 if (!dir_check_canon(cmd->tmp_pool, cmd, G_WRITE, dst, NULL)) {
861 int xerrno = EPERM;
862
863 cmd->argv[0] = cmd_name;
864
865 pr_log_debug(DEBUG4, MOD_SITE_MISC_VERSION
866 ": %s command denied by <Limit>", (char *) cmd->argv[0]);
867 pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[3],
868 strerror(xerrno));
869
870 errno = xerrno;
871 return PR_ERROR(cmd);
872 }
873 cmd->argv[0] = cmd_name;
874
875 if (site_misc_check_filters(cmd, dst) < 0) {
876 int xerrno = EPERM;
877
878 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
879
880 errno = xerrno;
881 return PR_ERROR(cmd);
882 }
883
884 /* Make sure the source path exists. The symlink(2) man page suggests
885 * that the system call will do this, but experimentally (Mac OSX 10.4)
886 * I've seen symlink(2) happily link two names, neither of which exist
887 * in the filesystem.
888 */
889
890 pr_fs_clear_cache2(src);
891 res = pr_fsio_stat(src, &st);
892 if (res < 0) {
893 int xerrno = errno;
894
895 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
896
897 errno = xerrno;
898 return PR_ERROR(cmd);
899 }
900
901 if (pr_fsio_symlink(src, dst) < 0) {
902 int xerrno = errno;
903
904 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
905
906 errno = xerrno;
907 return PR_ERROR(cmd);
908 }
909
910 pr_response_add(R_200, _("SITE %s command successful"),
911 (char *) cmd->argv[1]);
912 return PR_HANDLED(cmd);
913 }
914
915 if (strncasecmp(cmd->argv[1], "HELP", 5) == 0) {
916 pr_response_add(R_214, "SYMLINK <sp> source <sp> destination");
917 }
918
919 return PR_DECLINED(cmd);
920 }
921
922 /* Handle: SITE UTIME mtime path-with-spaces */
site_misc_utime_mtime(cmd_rec * cmd)923 MODRET site_misc_utime_mtime(cmd_rec *cmd) {
924 register unsigned int i;
925 char *cmd_name, *decoded_path, *path = "";
926 size_t timestamp_len;
927 unsigned int year, month, day, hour, min, sec = 0;
928 struct timeval tvs[2];
929 struct stat st;
930
931 /* Accept both 'YYYYMMDDhhmm' and 'YYYYMMDDhhmmss' formats. */
932 timestamp_len = strlen(cmd->argv[2]);
933 if (timestamp_len != 12 &&
934 timestamp_len != 14) {
935 int xerrno = EINVAL;
936
937 pr_log_debug(DEBUG7, MOD_SITE_MISC_VERSION
938 ": wrong number of digits in timestamp argument '%s' (%lu)",
939 (char *) cmd->argv[2], (unsigned long) timestamp_len);
940 pr_response_add_err(R_500, "%s: %s", cmd->arg, strerror(xerrno));
941
942 errno = xerrno;
943 return PR_ERROR(cmd);
944 }
945
946 for (i = 3; i < cmd->argc; i++) {
947 path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", cmd->argv[i], NULL);
948 }
949
950 decoded_path = pr_fs_decode_path2(cmd->tmp_pool, path,
951 FSIO_DECODE_FL_TELL_ERRORS);
952 if (decoded_path == NULL) {
953 int xerrno = errno;
954
955 pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", path,
956 strerror(xerrno));
957 pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
958 path);
959
960 pr_cmd_set_errno(cmd, xerrno);
961 errno = xerrno;
962 return PR_ERROR(cmd);
963 }
964
965 if (pr_fsio_lstat(decoded_path, &st) == 0) {
966 if (S_ISLNK(st.st_mode)) {
967 char link_path[PR_TUNABLE_PATH_MAX];
968 int len;
969
970 memset(link_path, '\0', sizeof(link_path));
971 len = dir_readlink(cmd->tmp_pool, decoded_path, link_path,
972 sizeof(link_path)-1, PR_DIR_READLINK_FL_HANDLE_REL_PATH);
973 if (len > 0) {
974 link_path[len] = '\0';
975 decoded_path = pstrdup(cmd->tmp_pool, link_path);
976 }
977 }
978 }
979
980 path = dir_canonical_path(cmd->tmp_pool, decoded_path);
981 if (path == NULL) {
982 int xerrno = EINVAL;
983
984 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
985
986 errno = xerrno;
987 return PR_ERROR(cmd);
988 }
989
990 cmd_name = cmd->argv[0];
991 cmd->argv[0] = "SITE_UTIME";
992 if (!dir_check_canon(cmd->tmp_pool, cmd, G_WRITE, path, NULL)) {
993 int xerrno = EPERM;
994
995 cmd->argv[0] = cmd_name;
996
997 pr_log_debug(DEBUG4, MOD_SITE_MISC_VERSION
998 ": %s command denied by <Limit>", (char *) cmd->argv[0]);
999 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1000
1001 errno = xerrno;
1002 return PR_ERROR(cmd);
1003 }
1004 cmd->argv[0] = cmd_name;
1005
1006 if (site_misc_check_filters(cmd, path) < 0) {
1007 int xerrno = EPERM;
1008
1009 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1010
1011 errno = xerrno;
1012 return PR_ERROR(cmd);
1013 }
1014
1015 if (site_misc_parsetime(cmd->argv[2], timestamp_len, &year, &month, &day,
1016 &hour, &min, &sec) < 0) {
1017 int xerrno = errno;
1018
1019 pr_response_add_err(R_500, "%s: %s", cmd->arg, strerror(xerrno));
1020
1021 errno = xerrno;
1022 return PR_ERROR(cmd);
1023 }
1024
1025 tvs[0].tv_usec = tvs[1].tv_usec = 0;
1026 tvs[0].tv_sec = tvs[1].tv_sec = site_misc_mktime(year, month, day, hour,
1027 min, sec);
1028
1029 if (pr_fsio_utimes_with_root(path, tvs) < 0) {
1030 int xerrno = errno;
1031
1032 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1033
1034 errno = xerrno;
1035 return PR_ERROR(cmd);
1036 }
1037
1038 pr_response_add(R_200, _("SITE %s command successful"),
1039 (char *) cmd->argv[1]);
1040 return PR_HANDLED(cmd);
1041 }
1042
1043 /* Handle: SITE UTIME path-with-spaces atime mtime ctime UTC */
site_misc_utime_atime_mtime_ctime(cmd_rec * cmd)1044 MODRET site_misc_utime_atime_mtime_ctime(cmd_rec *cmd) {
1045 register unsigned int i;
1046 char *cmd_name, *decoded_path, *path = "", *timestamp;
1047 size_t timestamp_len;
1048 unsigned int year, month, day, hour, min, sec = 0;
1049 time_t parsed_atime, parsed_mtime, parsed_ctime;
1050 struct timeval tvs[2];
1051
1052 for (i = 2; i < cmd->argc-4; i++) {
1053 path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", cmd->argv[i], NULL);
1054 }
1055
1056 decoded_path = pr_fs_decode_path2(cmd->tmp_pool, path,
1057 FSIO_DECODE_FL_TELL_ERRORS);
1058 if (decoded_path == NULL) {
1059 int xerrno = errno;
1060
1061 pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", path,
1062 strerror(xerrno));
1063 pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
1064 path);
1065
1066 pr_cmd_set_errno(cmd, xerrno);
1067 errno = xerrno;
1068 return PR_ERROR(cmd);
1069 }
1070
1071 path = dir_canonical_path(cmd->tmp_pool, decoded_path);
1072 if (path == NULL) {
1073 int xerrno = EINVAL;
1074
1075 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1076
1077 errno = xerrno;
1078 return PR_ERROR(cmd);
1079 }
1080
1081 cmd_name = cmd->argv[0];
1082 cmd->argv[0] = "SITE_UTIME";
1083 if (!dir_check_canon(cmd->tmp_pool, cmd, G_WRITE, path, NULL)) {
1084 int xerrno = EPERM;
1085
1086 cmd->argv[0] = cmd_name;
1087
1088 pr_log_debug(DEBUG4, MOD_SITE_MISC_VERSION
1089 ": %s command denied by <Limit>", (char *) cmd->argv[0]);
1090 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1091
1092 errno = xerrno;
1093 return PR_ERROR(cmd);
1094 }
1095 cmd->argv[0] = cmd_name;
1096
1097 if (site_misc_check_filters(cmd, path) < 0) {
1098 int xerrno = EPERM;
1099
1100 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1101
1102 errno = xerrno;
1103 return PR_ERROR(cmd);
1104 }
1105
1106 /* Handle the atime. Accept both 'YYYYMMDDhhmm' and 'YYYYMMDDhhmmss'
1107 * formats.
1108 */
1109 timestamp = cmd->argv[cmd->argc-4];
1110 timestamp_len = strlen(timestamp);
1111 if (timestamp_len != 12 &&
1112 timestamp_len != 14) {
1113 int xerrno = EINVAL;
1114
1115 pr_log_debug(DEBUG7, MOD_SITE_MISC_VERSION
1116 ": wrong number of digits in timestamp argument '%s' (%lu)",
1117 timestamp, (unsigned long) timestamp_len);
1118 pr_response_add_err(R_500, "%s: %s", cmd->arg, strerror(xerrno));
1119
1120 errno = xerrno;
1121 return PR_ERROR(cmd);
1122 }
1123
1124 if (site_misc_parsetime(timestamp, timestamp_len, &year, &month, &day,
1125 &hour, &min, &sec) < 0) {
1126 int xerrno = errno;
1127
1128 pr_response_add_err(R_500, "%s: %s", cmd->arg, strerror(xerrno));
1129
1130 errno = xerrno;
1131 return PR_ERROR(cmd);
1132 }
1133
1134 parsed_atime = site_misc_mktime(year, month, day, hour, min, sec);
1135
1136 /* Handle the mtime. Accept both 'YYYYMMDDhhmm' and 'YYYYMMDDhhmmss'
1137 * formats.
1138 */
1139
1140 sec = 0;
1141 timestamp = cmd->argv[cmd->argc-3];
1142 timestamp_len = strlen(timestamp);
1143 if (timestamp_len != 12 &&
1144 timestamp_len != 14) {
1145 int xerrno = EINVAL;
1146
1147 pr_log_debug(DEBUG7, MOD_SITE_MISC_VERSION
1148 ": wrong number of digits in timestamp argument '%s' (%lu)",
1149 timestamp, (unsigned long) timestamp_len);
1150 pr_response_add_err(R_500, "%s: %s", cmd->arg, strerror(xerrno));
1151
1152 errno = xerrno;
1153 return PR_ERROR(cmd);
1154 }
1155
1156 if (site_misc_parsetime(timestamp, timestamp_len, &year, &month, &day,
1157 &hour, &min, &sec) < 0) {
1158 int xerrno = errno;
1159
1160 pr_response_add_err(R_500, "%s: %s", cmd->arg, strerror(xerrno));
1161
1162 errno = xerrno;
1163 return PR_ERROR(cmd);
1164 }
1165
1166 parsed_mtime = site_misc_mktime(year, month, day, hour, min, sec);
1167
1168 /* Handle the ctime. Accept both 'YYYYMMDDhhmm' and 'YYYYMMDDhhmmss'
1169 * formats.
1170 */
1171
1172 sec = 0;
1173 timestamp = cmd->argv[cmd->argc-2];
1174 timestamp_len = strlen(timestamp);
1175 if (timestamp_len != 12 &&
1176 timestamp_len != 14) {
1177 int xerrno = EINVAL;
1178
1179 pr_log_debug(DEBUG7, MOD_SITE_MISC_VERSION
1180 ": wrong number of digits in timestamp argument '%s' (%lu)",
1181 timestamp, (unsigned long) timestamp_len);
1182 pr_response_add_err(R_500, "%s: %s", cmd->arg, strerror(xerrno));
1183
1184 errno = xerrno;
1185 return PR_ERROR(cmd);
1186 }
1187
1188 if (site_misc_parsetime(timestamp, timestamp_len, &year, &month, &day,
1189 &hour, &min, &sec) < 0) {
1190 int xerrno = errno;
1191
1192 pr_response_add_err(R_500, "%s: %s", cmd->arg, strerror(xerrno));
1193
1194 errno = xerrno;
1195 return PR_ERROR(cmd);
1196 }
1197
1198 /* Unix filesystems typically do not allow changing/setting the creation
1199 * timestamp. Thus we parse the timestamp provided by the client, but
1200 * do nothing but log it.
1201 */
1202 parsed_ctime = site_misc_mktime(year, month, day, hour, min, sec);
1203 pr_trace_msg("command", 9,
1204 "SITE UTIME command sent ctime timestamp of %lu secs",
1205 (unsigned long) parsed_ctime);
1206
1207 tvs[0].tv_usec = tvs[1].tv_usec = 0;
1208 tvs[0].tv_sec = parsed_atime;
1209 tvs[1].tv_sec = parsed_mtime;
1210
1211 if (pr_fsio_utimes_with_root(path, tvs) < 0) {
1212 int xerrno = errno;
1213
1214 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1215
1216 errno = xerrno;
1217 return PR_ERROR(cmd);
1218 }
1219
1220 pr_response_add(R_200, _("SITE %s command successful"),
1221 (char *) cmd->argv[1]);
1222 return PR_HANDLED(cmd);
1223 }
1224
site_misc_utime(cmd_rec * cmd)1225 MODRET site_misc_utime(cmd_rec *cmd) {
1226 if (!site_misc_engine) {
1227 return PR_DECLINED(cmd);
1228 }
1229
1230 if (cmd->argc < 2) {
1231 pr_log_debug(DEBUG5, MOD_SITE_MISC_VERSION
1232 "%s : wrong number of parameters (%d)", (char *) cmd->argv[0], cmd->argc);
1233 return PR_DECLINED(cmd);
1234 }
1235
1236 if (strncasecmp(cmd->argv[1], "UTIME", 6) == 0) {
1237 unsigned char *authenticated;
1238
1239 authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
1240 if (authenticated == NULL ||
1241 *authenticated == FALSE) {
1242 pr_response_add_err(R_530, _("Please login with USER and PASS"));
1243 errno = EACCES;
1244 return PR_ERROR(cmd);
1245 }
1246
1247 /* Now try to determine whether we are dealing with the mtime-only
1248 * SITE UTIME variant (one timestamp only), or with the atime/mtime/ctime
1249 * variant (three timestamps). What makes this trickier is making sure
1250 * to handle filenames that contain spaces.
1251 */
1252
1253 if (cmd->argc < 4) {
1254 /* Not enough arguments for any SITE UTIME variant. */
1255 pr_log_debug(DEBUG9, MOD_SITE_MISC_VERSION
1256 ": SITE UTIME command has wrong number of parameters (%d), ignoring",
1257 cmd->argc);
1258 return PR_DECLINED(cmd);
1259 }
1260
1261 /* If we have at least 7 parameters, AND the last parameter is "UTC"
1262 * (case-insensitive), then it's a candidate for the atime/mtime/ctime
1263 * variant.
1264 */
1265 if (cmd->argc >= 7 &&
1266 strncasecmp(cmd->argv[cmd->argc-1], "UTC", 4) == 0) {
1267 return site_misc_utime_atime_mtime_ctime(cmd);
1268 }
1269
1270 return site_misc_utime_mtime(cmd);
1271 }
1272
1273 if (strncasecmp(cmd->argv[1], "HELP", 5) == 0) {
1274 pr_response_add(R_214, "UTIME <sp> YYYYMMDDhhmm[ss] <sp> path");
1275 }
1276
1277 return PR_DECLINED(cmd);
1278 }
1279
1280 /* Event listeners
1281 */
1282
site_misc_sess_reinit_ev(const void * event_data,void * user_data)1283 static void site_misc_sess_reinit_ev(const void *event_data, void *user_data) {
1284 int res;
1285
1286 /* A HOST command changed the main_server pointer, reinitialize ourselves. */
1287
1288 pr_event_unregister(&site_misc_module, "core.session-reinit",
1289 site_misc_sess_reinit_ev);
1290
1291 site_misc_engine = TRUE;
1292 pr_feat_remove("SITE MKDIR");
1293 pr_feat_remove("SITE RMDIR");
1294 pr_feat_remove("SITE SYMLINK");
1295 pr_feat_remove("SITE UTIME");
1296
1297 res = site_misc_sess_init();
1298 if (res < 0) {
1299 pr_session_disconnect(&site_misc_module,
1300 PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
1301 }
1302 }
1303
1304 /* Initialization functions
1305 */
1306
site_misc_sess_init(void)1307 static int site_misc_sess_init(void) {
1308 config_rec *c;
1309
1310 pr_event_register(&site_misc_module, "core.session-reinit",
1311 site_misc_sess_reinit_ev, NULL);
1312
1313 c = find_config(main_server->conf, CONF_PARAM, "SiteMiscEngine", FALSE);
1314 if (c) {
1315 site_misc_engine = *((unsigned int *) c->argv[0]);
1316 }
1317
1318 if (!site_misc_engine) {
1319 return 0;
1320 }
1321
1322 /* Advertise support for these SITE commands */
1323 pr_feat_add("SITE MKDIR");
1324 pr_feat_add("SITE RMDIR");
1325 pr_feat_add("SITE SYMLINK");
1326 pr_feat_add("SITE UTIME");
1327
1328 return 0;
1329 }
1330
1331 /* Module API tables
1332 */
1333
1334 static conftable site_misc_conftab[] = {
1335 { "SiteMiscEngine", set_sitemiscengine, NULL },
1336 { NULL }
1337 };
1338
1339 static cmdtable site_misc_cmdtab[] = {
1340 { CMD, C_SITE, G_WRITE, site_misc_mkdir, FALSE, FALSE, CL_MISC },
1341 { CMD, C_SITE, G_WRITE, site_misc_rmdir, FALSE, FALSE, CL_MISC },
1342 { CMD, C_SITE, G_WRITE, site_misc_symlink, FALSE, FALSE, CL_MISC },
1343 { CMD, C_SITE, G_WRITE, site_misc_utime, FALSE, FALSE, CL_MISC },
1344 { 0, NULL }
1345 };
1346
1347 module site_misc_module = {
1348 NULL, NULL,
1349
1350 /* Module API version 2.0 */
1351 0x20,
1352
1353 /* Module name */
1354 "site_misc",
1355
1356 /* Module configuration handler table */
1357 site_misc_conftab,
1358
1359 /* Module command handler table */
1360 site_misc_cmdtab,
1361
1362 /* Module authentication handler table */
1363 NULL,
1364
1365 /* Module initialization function */
1366 NULL,
1367
1368 /* Session initialization function */
1369 site_misc_sess_init,
1370
1371 /* Module version */
1372 MOD_SITE_MISC_VERSION
1373 };
1374