1 /*
2 * h_feat.c
3 *
4 * (C)1999-2011 by Marc Huber <Marc.Huber@web.de>
5 * All rights reserved.
6 *
7 * $Id: h_feat.c,v 1.20 2015/03/14 06:11:25 marc Exp marc $
8 *
9 */
10
11 /*
12 * FEAT may occur before the user has logged in, and the client is free to
13 * assume that the features returned are complete, so we may not generate
14 * the supported feature list dynamically. To quote from RFC2389:
15 *
16 * "..., when a client receives a FEAT response from an FTP server, it can
17 * assume that the only extensions the server supports are those that are
18 * listed in the FEAT response."
19 */
20
21 #define __H_FEAT_C__
22 #include "headers.h"
23
24 #include "misc/tokenize.h"
25
26 static const char rcsid[] __attribute__ ((used)) = "$Id: h_feat.c,v 1.20 2015/03/14 06:11:25 marc Exp marc $";
27
28 extern rb_tree_t *mimetypes;
29
h_feat(struct context * ctx,char * arg)30 void h_feat(struct context *ctx, char *arg __attribute__ ((unused)))
31 {
32 struct list_struct *mfs = MLST_fact;
33 struct md_method *m;
34 char **l = lang;
35 u_int i = 0;
36 static int idx_host = -1;
37 static int idx_rang = -1;
38 static int idx_rest = -1;
39 DebugIn(DEBUG_PROC);
40
41 if (idx_host < 0)
42 idx_host = get_request_index(requests, "host");
43
44 if (idx_rang < 0)
45 idx_rang = get_request_index(requests, "rang");
46
47 if (idx_rest < 0)
48 idx_rest = get_request_index(requests, "rest");
49
50 reply(ctx, MSG_211_Extensions);
51
52 #ifdef WITH_SSL
53 if (ssl_ctx)
54 reply(ctx, " AUTH TLS\r\n");
55 #endif /* WITH_SSL */
56
57 reply(ctx, " ESTA\r\n");
58
59 if (SET64_ISSET(idx_host, ctx->requests))
60 reply(ctx, " HOST\r\n");
61
62 if (SET64_ISSET(idx_rang, ctx->requests))
63 reply(ctx, " RANG STREAM\r\n");
64
65 reply(ctx, " LANG ");
66 for (; *l; i++, l++) {
67 reply(ctx, *l);
68 if (ctx->lang == i)
69 reply(ctx, "*");
70 reply(ctx, ";");
71 }
72 reply(ctx, "\r\n");
73
74 reply(ctx, " MDTM\r\n");
75 reply(ctx, " MFF Modify;UNIX.mode;UNIX.group;\r\n");
76 reply(ctx, " MFMT\r\n");
77 reply(ctx, " MLST ");
78 for (; mfs->fact; mfs++)
79 if ((mfs->flag != MLST_fact_mediatype || mimetypes)) {
80 reply(ctx, mfs->fact);
81 if (mfs->flag & ctx->mlst_facts)
82 reply(ctx, "*");
83 reply(ctx, ";");
84 }
85 reply(ctx, "\r\n");
86
87 #ifdef WITH_ZLIB
88 reply(ctx, " MODE Z\r\n");
89 #endif
90
91 replyf(ctx, " HASH ");
92 for (m = md_methods; m; m = m->next) {
93 reply(ctx, m->ftp_name);
94 if (m == ctx->md_method_hash)
95 reply(ctx, "*");
96 if (m->next)
97 reply(ctx, ";");
98 }
99 reply(ctx, "\r\n");
100
101 #ifdef WITH_SSL
102 if (ssl_ctx) {
103 reply(ctx, " PBSZ\r\n");
104 reply(ctx, " PROT\r\n");
105 }
106 #endif /* WITH_SSL */
107
108 if (SET64_ISSET(idx_rest, ctx->requests))
109 reply(ctx, " REST STREAM\r\n");
110 reply(ctx, " SIZE\r\n");
111 reply(ctx, " TVFS\r\n");
112 reply(ctx, " UTF8\r\n");
113 reply(ctx, MSG_211_End);
114
115 DebugOut(DEBUG_PROC);
116 }
117
h_opts(struct context * ctx,char * arg)118 void h_opts(struct context *ctx, char *arg)
119 {
120 char *t = arg;
121 DebugIn(DEBUG_PROC);
122 for (; *t && !isspace((int) *t); t++);
123 if (*t)
124 *t++ = 0;
125 if (!strcasecmp(arg, "MLST")) {
126 struct list_struct *mfs;
127 char *u;
128 ctx->mlst_facts = 0;
129 for (; (u = strtok(t, ";")); t = NULL)
130 if (*u) {
131 for (mfs = MLST_fact; mfs->fact && strcasecmp(mfs->fact, u); mfs++);
132 if (mfs->fact)
133 ctx->mlst_facts |= mfs->flag;
134 }
135 reply(ctx, MSG_200_Done);
136 }
137 #ifdef WITH_ZLIB
138 else if (!strcasecmp(arg, "MODE") && ctx->transfer_in_progress)
139 reply(ctx, MSG_501_Transfer_in_progress);
140 else if (!strcasecmp(arg, "MODE")) {
141 #ifndef MAXTOKENS
142 # define MAXTOKENS 32
143 #endif
144 char *argv[MAXTOKENS];
145 char **a = argv;
146 int argc = tokenize(t, argv, MAXTOKENS);
147 if (argc > 0) {
148 int level = ctx->deflate_level;
149 u_int extra = ctx->deflate_extra;
150 if (strcasecmp(argv[0], "z")) {
151 reply(ctx, MSG_501_Unknown_transfer_mode);
152 goto bye;
153 }
154 a++, argc--;
155 while (argc > 1) {
156 struct list_struct *ls = mode_z_opt;
157 while (ls->fact && strcasecmp(a[0], ls->fact))
158 ls++;
159 switch (ls->flag) {
160 case MODE_Z_ENGINE:
161 if (strcasecmp(a[1], "zlib")) {
162 replyf(ctx, MSG_501_mode_z_error, a[0], " ", a[1]);
163 goto bye;
164 }
165 break;
166 case MODE_Z_METHOD:
167 if (atoi(a[1]) != Z_DEFLATED) {
168 replyf(ctx, MSG_501_mode_z_error, a[0], " ", a[1]);
169 goto bye;
170 }
171 break;
172 case MODE_Z_LEVEL:
173 level = atoi(a[1]);
174 if (level < ctx->deflate_level_min)
175 level = ctx->deflate_level_min;
176 if (level > ctx->deflate_level_max)
177 level = ctx->deflate_level_max;
178 break;
179 case MODE_Z_EXTRA:
180 if (strcasecmp(a[1], "on"))
181 extra = 0;
182 else
183 extra = 1;
184 break;
185 default:
186 replyf(ctx, MSG_501_mode_z_error, a[0], "", a[1]);
187 }
188 argc -= 2, a += 2;
189 }
190 if (argc > 0)
191 replyf(ctx, MSG_501_mode_z_error, a[0], "", "");
192 else {
193 ctx->deflate_level = level;
194 ctx->deflate_extra = extra;
195 reply(ctx, MSG_200_Done);
196 }
197 } else
198 reply(ctx, MSG_200_Done);
199 }
200 #endif /* WITH_ZLIB */
201 else if (!strcasecmp(arg, "HASH")) {
202 if (*t) {
203 struct md_method *m = md_method_find(md_methods, t);
204 if (m)
205 ctx->md_method_hash = m;
206 else {
207 reply(ctx, MSG_501_unknown_checksum_algorithm);
208 goto bye;
209 }
210
211 }
212 replyf(ctx, "200 %s\r\n", ctx->md_method_hash->ftp_name);
213 } else
214 reply(ctx, MSG_501_No_options);
215
216 bye:
217
218 DebugOut(DEBUG_PROC);
219 }
220