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