1 /* vi:ai:et:ts=8 sw=2
2 */
3 /*
4 * wzdftpd - a modular and cool ftp server
5 * Copyright (C) 2002-2004 Pierre Chifflier
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (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, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * As a special exemption, Pierre Chifflier
22 * and other respective copyright holders give permission to link this program
23 * with OpenSSL, and distribute the resulting executable, without including
24 * the source code for OpenSSL in the source distribution.
25 */
26
27 #include "wzd_all.h"
28
29 #ifndef WZD_USE_PCH
30
31 #if defined(WIN32)
32 #include <winsock2.h>
33 #else
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #endif
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <time.h>
44 #include <fcntl.h> /* O_RDONLY */
45
46 #include "wzd_structs.h"
47 #include "wzd_messages.h"
48 #include "wzd_misc.h"
49 #include "wzd_log.h"
50
51 #include "wzd_cache.h"
52 #include "wzd_section.h"
53 #include "wzd_string.h"
54 #include "wzd_utf8.h"
55 #include "wzd_vfs.h"
56
57 #include "wzd_debug.h"
58
59 #endif /* WZD_USE_PCH */
60
61 #define DEFAULT_MSG "No message for this code"
62
63 #define BUFFER_LEN 4096
64
65 char *msg_tab[HARD_MSG_LIMIT];
66
init_default_messages(void)67 void init_default_messages(void)
68 {
69 memset(msg_tab,0,HARD_MSG_LIMIT*sizeof(char *));
70
71 msg_tab[150] = strdup("Status OK, about to open data connection.");
72
73 msg_tab[200] = strdup("%s"); /* Command okay */
74 msg_tab[211] = strdup("%s");
75 msg_tab[213] = strdup("%s"); /* mdtm */
76 msg_tab[214] = strdup("The following commands can be used:\n"
77 "SITE TYPE PORT PASV EPRT EPSV ABOR PWD ALLO FEAT NOOP\n"
78 "SYST RNFR RNTO CWD LIST STAT MKD RMD RETR STOR REST\n"
79 "MDTM SIZE DELE PRET XCRC XMD5 OPTS HELP QUIT\n"
80 "Help OK"); /* TODO sort */
81 msg_tab[215] = strdup("UNIX Type: L8");
82 msg_tab[220] = strdup("wzd server ready.");
83 msg_tab[221] = strdup("Cya !");
84 msg_tab[226] = strdup("Closing data connection.\r\n%msg\r\n- [Section: %sectionname] - [Free: %spacefree] - [Dl: %usertotal_dl2] - [Ul: %usertotal_ul2] -");
85 msg_tab[227] = strdup("Entering Passive Mode (%hhu,%hhu,%hhu,%hhu,%hu,%hu)"); /* DON'T TOUCH ! */
86 msg_tab[230] = strdup("User logged in, proceed.");
87 msg_tab[234] = strdup("AUTH command OK. Initializing %s mode"); /* SSL init */
88 msg_tab[235] = strdup("%s%s");
89 msg_tab[250] = strdup("%s%s");
90 /* msg_tab[257] = strdup("\"%s\" %s");*/
91 msg_tab[258] = strdup("\"%s\" %s");
92
93 msg_tab[331] = strdup("User %s okay, need password.");
94 msg_tab[334] = strdup("%s%s");
95 msg_tab[350] = strdup("%s"); /* "Restarting at %ld. Send STORE or RETRIEVE.", or "OK, send RNTO" */
96
97 msg_tab[421] = strdup("%s"); /* Service not available, closing control connection. */
98 msg_tab[425] = strdup("Can't open data connection.");
99 msg_tab[426] = strdup("Error occured, data connection closed.");
100 msg_tab[431] = strdup("%s"); /* Unable to accept security mechanism. */
101 msg_tab[451] = strdup("Transmission error occured.");
102 msg_tab[491] = strdup("Data connection already active.");
103
104 msg_tab[501] = strdup("%s");
105 msg_tab[502] = strdup("Command not implemented.");
106 msg_tab[503] = strdup("%s");
107 msg_tab[530] = strdup("%s"); /* Not logged in." */
108 msg_tab[535] = strdup("%s"); /* Not logged in." */
109 msg_tab[550] = strdup("%s: %s");
110 msg_tab[553] = strdup("Requested action not taken: %s");
111 }
112
free_messages(void)113 void free_messages(void)
114 {
115 int i;
116
117 for (i=0; i<HARD_MSG_LIMIT; i++)
118 {
119 if (msg_tab[i]) {
120 free(msg_tab[i]);
121 msg_tab[i]=0;
122 }
123 }
124 }
125
getMessage(int code,int * must_free)126 const char * getMessage(int code, int *must_free)
127 {
128 const char * ptr;
129 char * file_buffer;
130 unsigned long filesize, size;
131 u64_t sz64;
132
133 if (code < 0 || code > HARD_MSG_LIMIT)
134 return DEFAULT_MSG;
135 *must_free = 0;
136 ptr = msg_tab[code];
137 if (!ptr || strlen(ptr)==0) return DEFAULT_MSG;
138 if (ptr[0]=='+') { /* returns file content */
139 wzd_cache_t * fp;
140 fp = wzd_cache_open(ptr+1,O_RDONLY,0644);
141 if (!fp) return DEFAULT_MSG;
142 sz64 = wzd_cache_getsize(fp);
143 if (sz64 > INT_MAX) {
144 out_log(LEVEL_HIGH,"%s:%d couldn't allocate " PRIu64 " bytes for message %d\n",__FILE__,__LINE__,code);
145 wzd_cache_close(fp);
146 *must_free = 0;
147 return NULL;
148 }
149 filesize = (unsigned int) sz64;
150 file_buffer = wzd_malloc(filesize+1);
151 if ( (size=wzd_cache_read(fp,file_buffer,filesize))!=filesize ) {
152 wzd_free(file_buffer);
153 wzd_cache_close(fp);
154 return DEFAULT_MSG;
155 }
156 file_buffer[filesize]='\0';
157 wzd_cache_close(fp);
158 *must_free = 1;
159 return file_buffer;
160 }
161 return ptr;
162 }
163
setMessage(const char * newMessage,int code)164 void setMessage(const char *newMessage, int code)
165 {
166 if (code < 0 || code > HARD_MSG_LIMIT) return;
167 if (msg_tab[code]) free(msg_tab[code]);
168 msg_tab[code] = (char*)newMessage;
169 }
170
171 /*************** send_message ************************/
172
send_message(int code,wzd_context_t * context)173 int send_message(int code, wzd_context_t * context)
174 {
175 wzd_string_t * str;
176 int ret;
177
178 str = format_message(context,code);
179 #ifdef DEBUG
180 out_err(LEVEL_FLOOD,"<thread %ld> -> %s",(unsigned long)context->pid_child,str_tochar(str));
181 #endif
182 ret = (context->write_fct)(context->controlfd,str_tochar(str),str_length(str),0,HARD_XFER_TIMEOUT,context);
183
184 str_deallocate(str);
185
186 return ret;
187 }
188
189 /*************** send_message_with_args **************/
190
send_message_with_args(int code,wzd_context_t * context,...)191 int send_message_with_args(int code, wzd_context_t * context, ...)
192 {
193 va_list argptr;
194 wzd_string_t * str;
195 int ret;
196
197 va_start(argptr,context); /* note: ansi compatible version of va_start */
198 str = v_format_message(context,code,argptr);
199 #ifdef HAVE_UTF8
200 if (context->connection_flags & CONNECTION_UTF8)
201 {
202 if (!str_is_valid_utf8(str))
203 str_local_to_utf8(str,local_charset());
204 }
205 #endif
206 va_end (argptr);
207 #ifdef DEBUG
208 out_err(LEVEL_FLOOD,"<thread %ld> ->ML %s",(unsigned long)context->pid_child,str_tochar(str));
209 #endif
210 ret = (context->write_fct)(context->controlfd,str_tochar(str),str_length(str),0,HARD_XFER_TIMEOUT,context);
211
212 str_deallocate(str);
213 return 0;
214 }
215
216 /*************** send_message_raw ********************/
217
send_message_raw(const char * msg,wzd_context_t * context)218 int send_message_raw(const char *msg, wzd_context_t * context)
219 {
220 int ret;
221
222 if (!msg || strlen(msg)==0) return 0;
223
224 #ifdef DEBUG
225 if (msg[strlen(msg)-1]!='\n')
226 out_err(LEVEL_FLOOD,"<thread %ld> -> %s\n",(unsigned long)context->pid_child,msg);
227 else
228 out_err(LEVEL_FLOOD,"<thread %ld> -> %s",(unsigned long)context->pid_child,msg);
229 #endif
230 ret = (context->write_fct)(context->controlfd,msg,strlen(msg),0,HARD_XFER_TIMEOUT,context);
231
232 return ret;
233 }
234
235
send_message_formatted(int code,wzd_context_t * context,const char * format,...)236 int send_message_formatted(int code, wzd_context_t * context, const char * format, ...)
237 {
238 va_list argptr;
239 wzd_string_t * str;
240 wzd_string_t ** str_list, ** it;
241 int ret;
242
243 if (!format || code<0) return -1;
244
245 va_start(argptr, format);
246
247 str = str_allocate();
248 ret = str_vsprintf(str, format, argptr);
249
250 if (ret < 0) return -1;
251
252 /* convert str to unicode if connection is in UTF-8 mode */
253 #ifdef HAVE_UTF8
254 if (context->connection_flags & CONNECTION_UTF8)
255 {
256 if (!str_is_valid_utf8(str))
257 str_local_to_utf8(str,local_charset());
258 }
259 #endif
260
261 if (ret < 0) return -1;
262
263 /* split lines and send formatted message to client */
264 str_list = str_split(str, "\r\n", 0);
265 str_deallocate(str);
266
267 it = str_list;
268
269 if (*(it+1) == NULL) { /* one line */
270 out_log(LEVEL_FLOOD, "send_message_formatted UL -> [%d %s]\n", code, str_tochar(*it));
271 str_prepend_printf(*it,"%.3d ",code);
272 str_append(*it,"\r\n");
273 ret = (context->write_fct)(context->controlfd,str_tochar(*it),str_length(*it),0,HARD_XFER_TIMEOUT,context);
274 } else { /* multi-line */
275 out_log(LEVEL_FLOOD, "send_message_formatted ML -> [%d-%s]\n", code, str_tochar(*it));
276 it++;
277 for (; *it; it++) {
278 if (*(it+1) == NULL) { /* last line */
279 out_log(LEVEL_FLOOD, "send_message_formatted ML -> [%d %s]\n", code, str_tochar(*it));
280 str_prepend_printf(*it,"%.3d ",code);
281 str_append(*it,"\r\n");
282 ret = (context->write_fct)(context->controlfd,str_tochar(*it),str_length(*it),0,HARD_XFER_TIMEOUT,context);
283 } else {
284 out_log(LEVEL_FLOOD, "send_message_formatted ML -> [ %s]\n", str_tochar(*it));
285 str_prepend_printf(*it,"%.3d-",code);
286 str_append(*it,"\r\n");
287 ret = (context->write_fct)(context->controlfd,str_tochar(*it),str_length(*it),0,HARD_XFER_TIMEOUT,context);
288 }
289 }
290 }
291
292
293 va_end(argptr);
294 str_deallocate_array(str_list);
295 return 0;
296 }
297
298 /** \brief Allocate memory for a struct wzd_reply_t */
reply_alloc(void)299 struct wzd_reply_t * reply_alloc(void)
300 {
301 struct wzd_reply_t * reply;
302
303 reply = wzd_malloc(sizeof(struct wzd_reply_t));
304 reply->code = 0;
305 reply->_reply = NULL;
306 reply->sent = 0;
307
308 return reply;
309 }
310
311 /** \brief Free memory used by struct wzd_reply_t */
reply_free(struct wzd_reply_t * reply)312 void reply_free(struct wzd_reply_t * reply)
313 {
314 WZD_ASSERT_VOID(reply != NULL);
315 if (reply == NULL) return;
316
317 wzd_free(reply->_reply);
318 wzd_free(reply);
319 }
320
321 /** \brief Clear the stored reply */
reply_clear(wzd_context_t * context)322 void reply_clear(wzd_context_t * context)
323 {
324 WZD_ASSERT_VOID(context != NULL);
325 WZD_ASSERT_VOID(context->reply != NULL);
326
327 if (context == NULL) return;
328 if (context->reply == NULL) return;
329
330 context->reply->code = 0;
331 /* re-use allocated string if any */
332 if (context->reply->_reply != NULL)
333 str_erase(context->reply->_reply,0,-1);
334 else
335 context->reply->_reply = str_allocate();
336 context->reply->sent = 0;
337 }
338
339 /** \brief Set the current reply code */
reply_set_code(wzd_context_t * context,int code)340 void reply_set_code(wzd_context_t * context, int code)
341 {
342 WZD_ASSERT_VOID(context != NULL);
343 WZD_ASSERT_VOID(context->reply != NULL);
344
345 context->reply->code = code;
346 }
347
348 /** \brief Get the current reply code */
reply_get_code(wzd_context_t * context)349 int reply_get_code(wzd_context_t * context)
350 {
351 WZD_ASSERT(context != NULL);
352 WZD_ASSERT(context->reply != NULL);
353
354 return context->reply->code;
355 }
356
357 /** \brief Add a message to the stored reply */
reply_push(wzd_context_t * context,const char * s)358 int reply_push(wzd_context_t * context, const char * s)
359 {
360 WZD_ASSERT(context != NULL);
361 WZD_ASSERT(context->reply != NULL);
362 WZD_ASSERT(s != NULL);
363
364 if (context == NULL ||
365 context->reply == NULL ||
366 s == NULL)
367 return -1;
368
369 if (context->reply->_reply == NULL) {
370 context->reply->_reply = STR(s);
371 } else {
372 str_append(context->reply->_reply,s);
373 }
374
375 return 0;
376 }
377
378 /** \brief Send formatted reply to client.
379 *
380 * \a code must be set
381 */
reply_send(wzd_context_t * context)382 int reply_send(wzd_context_t * context)
383 {
384 int ret;
385
386 WZD_ASSERT(context != NULL);
387 WZD_ASSERT(context->reply != NULL);
388
389 if (context == NULL ||
390 context->reply == NULL)
391 return -1;
392
393 if (context->reply->code <= 0) return -1;
394
395 if (context->reply->sent != 0) {
396 out_log(LEVEL_NORMAL,"WARNING reply already sent, discarding second (or more) reply\n");
397 return -1;
398 }
399
400 ret = send_message_formatted(context->reply->code,context,str_tochar(context->reply->_reply));
401
402 context->reply->sent++;
403
404 return 0;
405 }
406
407