1 /*
2  * h_mff.c
3  *
4  * (C)2002-2011 by Marc Huber <Marc.Huber@web.de>
5  * All rights reserved.
6  *
7  * $Id: h_mff.c,v 1.13 2015/03/14 06:11:25 marc Exp marc $
8  *
9  */
10 
11 #include "headers.h"
12 
13 static const char rcsid[] __attribute__ ((used)) = "$Id: h_mff.c,v 1.13 2015/03/14 06:11:25 marc Exp marc $";
14 
h_mff(struct context * ctx,char * arg)15 void h_mff(struct context *ctx, char *arg)
16 {
17     char *t;
18 
19     DebugIn(DEBUG_COMMAND);
20 
21     t = arg;
22 
23     while (*arg && !isspace((int) *arg))
24 	arg++;
25 
26     if (!isspace((int) *arg)) {
27 	reply(ctx, MSG_500_arguments_required);
28 	DebugOut(DEBUG_COMMAND);
29 	return;
30     }
31 
32     *arg++ = 0;
33 
34     if (*arg) {
35 	char *u, *v;
36 	int flags = 0;
37 #define flag_modify	1
38 #define flag_unix_mode	2
39 #define flag_unix_group	4
40 	time_t modify = 0;
41 	mode_t unix_mode = 0;
42 	gid_t unix_group = 0;
43 
44 	for (; (u = strtok(t, ";")); t = NULL)
45 	    if (*u && (v = strchr(u, '='))) {
46 		*v++ = 0;
47 
48 		if (!strcasecmp(u, "Modify")) {
49 		    struct tm tm;
50 		    memset(&tm, 0, sizeof(tm));
51 
52 		    if (6 != sscanf(v, "%4d%2d%2d%2d%2d%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec))
53 			goto syntax_error;
54 		    tm.tm_year -= 1900;
55 		    tm.tm_mon--;
56 		    flags |= flag_modify;
57 		    modify = mktime(&tm);
58 		} else if (!strcasecmp(u, "UNIX.mode")) {
59 		    u_int mode;
60 		    if (1 != sscanf(v, "%o", &mode))
61 			goto syntax_error;
62 		    flags |= flag_unix_mode;
63 		    unix_mode = (mode_t) mode;
64 
65 		} else if (!strcasecmp(u, "UNIX.group")) {
66 		    int i = NGROUPS_MAX;
67 		    u_int ug;
68 
69 		    if (1 == sscanf(v, "%u", &ug))
70 			for (i = 0; i < ctx->gids_size && (gid_t) ug != ctx->gids[i]; i++);
71 		    else
72 			for (i = 0; i < ctx->gids_size && strcmp(arg, lookup_gid(ctx, ctx->gids[i])); i++);
73 
74 		    if (i < ctx->gids_size) {
75 			flags |= flag_unix_group;
76 			unix_group = (gid_t) i;
77 		    }
78 		} else {
79 		    replyf(ctx, MSG_504_Parameter_not_implemented, u);
80 		    goto bye;
81 		}
82 	    } else if (*u) {
83 	      syntax_error:
84 		reply(ctx, MSG_501_Syntax_error);
85 		goto bye;
86 	    }
87 
88 	if (flags) {
89 	    struct stat st;
90 	    if ((t = buildpath(ctx, arg)) && (!pickystat(ctx, &st, t))
91 		&& (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode))) {
92 		if ((flags & flag_modify) && st.st_uid == ctx->uid) {
93 		    struct utimbuf ut;
94 		    ut.actime = st.st_atime;
95 		    ut.modtime = modify;
96 		    utime(t, &ut);
97 		}
98 
99 		if (flags & ~flag_modify) {
100 		    int fn = open(t, O_RDONLY | O_NOFOLLOW);
101 		    if (fn > -1) {
102 			if (!fstat(fn, &st) && st.st_uid == ctx->uid) {
103 			    if (flags & flag_unix_group) {
104 				seteuid(0);
105 				if (fchown(fn, (uid_t) - 1, unix_group)) {
106 				    // FIXME
107 				}
108 				seteuid(ctx->uid);
109 			    }
110 			    if (flags & flag_unix_mode)
111 				fchmod(fn, unix_mode | (S_ISDIR(st.st_mode)
112 							? ctx->chmod_dirmask : ctx->chmod_filemask));
113 			    fstat(fn, &st);
114 
115 			}
116 			close(fn);
117 		    }
118 		} else
119 		    stat(t, &st);
120 
121 		reply(ctx, "213 ");
122 		if (flags & flag_modify) {
123 		    char s[30];
124 		    strftime(s, sizeof(s), "%Y%m%d%H%M%S", gmtime(&st.st_mtime));
125 		    replyf(ctx, "Modify=%s;", s);
126 		}
127 
128 		if (flags & flag_unix_mode)
129 		    replyf(ctx, "UNIX.mode=0%o;", 0777 & (u_int) st.st_mode);
130 
131 		if (flags & flag_unix_group)
132 		    replyf(ctx, "UNIX.group=%s;", lookup_gid(ctx, st.st_gid));
133 
134 		replyf(ctx, " %s\r\n", arg);
135 	    } else
136 		reply(ctx, MSG_550_Permission_denied);
137 	} else
138 	    replyf(ctx, "213  %s\r\n", arg);
139     } else
140 	reply(ctx, MSG_500_missing_filename);
141 
142   bye:
143     DebugOut(DEBUG_COMMAND);
144 }
145