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 /** \file wzd_ClientThread.h
27  * \brief Main loop of wzdftpd client.
28  *
29  * This file contains the code which is executed by threads, and
30  * most of the core FTP functions (see RFC 959).
31  */
32 
33 #include "wzd_all.h"
34 
35 #ifdef WIN32
36 
37 #include <winsock2.h>
38 #include <ws2tcpip.h>
39 #include <io.h>
40 #include <sys/utime.h>
41 
42 #else
43 
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 
49 #include <netdb.h> /* gethostbyaddr */
50 
51 #endif /* WIN32 */
52 
53 /** \todo XXX FIXME remove this line and use correct types !!!!
54  * this is used to convert char* to struct in6_addr
55  */
56 #define PORCUS_CAST(x) ( ((struct in6_addr*)(x)) )
57 
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <time.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 #include <errno.h>
65 #include <fcntl.h>
66 
67 #ifdef HAVE_UTIME_H
68 # include <utime.h>
69 #endif
70 
71 #ifndef WIN32
72 #include <unistd.h>
73 #include <pthread.h>
74 #endif
75 
76 #ifndef HAVE_STRTOK_R
77 # include "libwzd-base/wzd_strtok_r.h"
78 #endif
79 
80 #include "wzd_structs.h"
81 
82 #include "wzd_fs.h"
83 #include "wzd_ip.h"
84 #include "wzd_log.h"
85 #include "wzd_misc.h"
86 #include "wzd_mod.h"
87 #include "wzd_data.h"
88 #include "wzd_messages.h"
89 #include "wzd_vfs.h"
90 #include "wzd_configfile.h"
91 #include "wzd_crc32.h"
92 #include "wzd_events.h"
93 #include "wzd_file.h"
94 #include "wzd_group.h"
95 #include "wzd_libmain.h"
96 #include "wzd_list.h"
97 #include "wzd_login.h"
98 #include "wzd_perm.h"
99 #include "wzd_protocol.h"
100 #include "wzd_ratio.h"
101 #include "wzd_section.h"
102 #include "wzd_site.h"
103 #include "wzd_string.h"
104 #include "wzd_socket.h"
105 #include "wzd_threads.h"
106 #include "wzd_tls.h"
107 #include "wzd_user.h"
108 #include "wzd_utf8.h"
109 #include "wzd_ClientThread.h"
110 
111 #include <libwzd-auth/wzd_base64.h>
112 #include <libwzd-auth/wzd_md5.h>
113 
114 #include "wzd_debug.h"
115 
116 #define TELNET_SYNCH    242
117 #define TELNET_IP       244
118 
119 #define BUFFER_LEN	4096
120 
121 
122 static int test_fxp(const char * remote_ip, net_family_t family, wzd_context_t * context);
123 
124 static int fxp_is_denied(wzd_user_t * user);
125 
126 static struct thread_key_t * _key_context = NULL;
127 
128 
129 /*************** clear_read **************************/
130 
131 /** \brief Non-blocking read function
132  *
133  * Try to read length bytes in non-blocking mode for timeout seconds
134  * max. If timeout is null, performs a blocking read.
135  */
clear_read(fd_t sock,char * msg,size_t length,UNUSED int flags,unsigned int timeout,UNUSED void * vcontext)136 int clear_read(fd_t sock, char *msg, size_t length, UNUSED int flags, unsigned int timeout, UNUSED void * vcontext)
137 {
138 /*  wzd_context_t * context = (wzd_context_t*)vcontext;*/
139   int ret;
140   int save_errno;
141   fd_set fds, efds;
142   struct timeval tv;
143 
144   if (timeout==0)
145     ret = recv(sock,msg,length,0);
146   else {
147     while (1) {
148       FD_ZERO(&fds);
149       FD_ZERO(&efds);
150       FD_SET(sock,&fds);
151       FD_SET(sock,&efds);
152       tv.tv_sec = timeout; tv.tv_usec = 0;
153 
154 #if defined(_MSC_VER)
155       ret = select(0,&fds,NULL,&efds,&tv);
156 #else
157       ret = select(sock+1,&fds,NULL,&efds,&tv);
158 #endif
159       save_errno = errno;
160 
161       if (FD_ISSET(sock,&fds)) /* ok */
162         break;
163       if (FD_ISSET(sock,&efds)) {
164         if (save_errno == EINTR) continue;
165         out_log(LEVEL_CRITICAL,"Error during recv: %s\n",strerror(save_errno));
166         return -1;
167       }
168       if (!FD_ISSET(sock,&fds)) /* timeout */
169         return 0;
170       break;
171     }
172     ret = recv(sock,msg,length,0);
173   } /* timeout */
174 
175   return ret;
176 }
177 
178 /*************** clear_write *************************/
179 
180 /** \brief Non-blocking write function
181  *
182  * Try to write length bytes in non-blocking mode for timeout seconds
183  * max. If timeout is null, performs a blocking write.
184  */
clear_write(fd_t sock,const char * msg,size_t length,UNUSED int flags,unsigned int timeout,UNUSED void * vcontext)185 int clear_write(fd_t sock, const char *msg, size_t length, UNUSED int flags, unsigned int timeout, UNUSED void * vcontext)
186 {
187 /*  wzd_context_t * context = (wzd_context_t*)vcontext;*/
188   int ret;
189   int done;
190   int save_errno;
191   fd_set fds, efds;
192   struct timeval tv;
193 
194   done=0;
195   while (length>0) {
196     if (timeout==0)
197       ret = send(sock,msg+done,length,0);
198     else {
199       while (1) {
200         FD_ZERO(&fds);
201         FD_ZERO(&efds);
202         FD_SET(sock,&fds);
203         FD_SET(sock,&efds);
204         tv.tv_sec = timeout; tv.tv_usec = 0;
205 
206 #if defined(_MSC_VER)
207         ret = select(0,NULL,&fds,&efds,&tv);
208 #else
209         ret = select(sock+1,NULL,&fds,&efds,&tv);
210 #endif
211         save_errno = errno;
212 
213         if (FD_ISSET(sock,&fds)) /* break */
214           break;
215         if (FD_ISSET(sock,&efds)) {
216           if (save_errno == EINTR) continue;
217           out_log(LEVEL_CRITICAL,"Error during send: %s\n",strerror(save_errno));
218           return -1;
219         }
220         if (!FD_ISSET(sock,&fds)) /* timeout */
221         {
222           out_log(LEVEL_CRITICAL,"Timeout during send\n");
223           return 0;
224         }
225         break;
226       }
227       ret = send(sock,msg+done,length,0);
228       if (ret==-1) return ret;
229     } /* timeout */
230     done += ret;
231     length -= ret;
232   }
233 
234   return done;
235 }
236 
237 /***************** client_die ************************/
238 /** \brief Cleanup code
239  *
240  * Called whenever a connection with a client is closed (for any reason).
241  * Closes all files/sockets.
242  */
client_die(wzd_context_t * context)243 void client_die(wzd_context_t * context)
244 {
245 #ifdef DEBUG
246   out_log(LEVEL_FLOOD,"client_die(context = %p)\n",context);
247 #endif
248 
249   if (context == NULL) return;
250 
251   if (context->magic != CONTEXT_MAGIC) {
252 #ifdef DEBUG
253 out_err(LEVEL_HIGH,"clientThread: context->magic is invalid at exit\n");
254 #endif
255     return;
256   }
257 
258   /* close opened files */
259   if (context->current_action.current_file != (fd_t)-1) {
260     data_end_transfer( (context->current_action.token == TOK_STOR) /* is_upload */, 0 /* end_ok */, context);
261   }
262 
263   {
264     wzd_user_t * user = GetUserByID(context->userid);
265     wzd_string_t * event_args = NULL;
266 
267     if (user) {
268       event_args = STR(user->username);
269       event_send(mainConfig->event_mgr, EVENT_LOGOUT, 0, event_args, context);
270       str_deallocate(event_args);
271     }
272   }
273 
274 
275   out_log(LEVEL_INFO,"Client dying (socket %d)\n",context->controlfd);
276   /* close existing pasv connections */
277   if (context->pasvsock != (fd_t)-1) {
278     socket_close(context->pasvsock);
279     FD_UNREGISTER(context->pasvsock,"Client PASV socket");
280     context->pasvsock = -1;
281   }
282   if (context->datafd != (fd_t)-1) {
283 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
284     /* if TLS, shutdown TLS before closing data connection */
285     tls_close_data(context);
286 #endif
287     socket_close(context->datafd);
288     FD_UNREGISTER(context->datafd,"Client data fd");
289   }
290   context->datafd = -1;
291 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
292   /* if TLS, shutdown TLS before closing control connection */
293   tls_free(context);
294 #endif
295   socket_close(context->controlfd);
296   FD_UNREGISTER(context->controlfd,"Client socket");
297   context->controlfd = -1;
298 
299   wzd_tls_free(_key_context);
300   _key_context = NULL;
301 
302   context_remove(context_list,context);
303 }
304 
305 /*************** check_timeout ***********************/
306 
check_timeout(wzd_context_t * context)307 int check_timeout(wzd_context_t * context)
308 {
309   time_t t, delay;
310   wzd_group_t *gptr;
311   unsigned int i;
312   int ret;
313   wzd_user_t * user;
314 
315   user = GetUserByID(context->userid);
316 
317   WZD_ASSERT( user != NULL);
318   if (user == NULL) return 0;
319 
320   /* reset global ul/dl counters */
321   mainConfig->global_ul_limiter.bytes_transfered = 0;
322 #ifndef _MSC_VER
323   gettimeofday(&(mainConfig->global_ul_limiter.current_time),NULL);
324   mainConfig->global_dl_limiter.bytes_transfered = 0;
325   gettimeofday(&(mainConfig->global_dl_limiter.current_time),NULL);
326 #else
327   _ftime(&(mainConfig->global_ul_limiter.current_time));
328   mainConfig->global_dl_limiter.bytes_transfered = 0;
329   _ftime(&(mainConfig->global_dl_limiter.current_time));
330 #endif
331 
332   /* check the timeout of control connection */
333   t = time(NULL);
334   delay = t - context->idle_time_start;
335 
336   /* check timeout if transfer in progress ? */
337   if (context->current_action.token == TOK_STOR || context->current_action.token == TOK_RETR)
338   {
339     time_t data_delay;
340     data_delay = t - context->idle_time_data_start;
341     if (data_delay > HARD_XFER_TIMEOUT) {
342       /* send events here allow sfv checker to mark file as bad if
343        * partially uploaded
344        */
345       {
346         wzd_string_t * event_args = str_allocate();
347         str_sprintf(event_args,"%s %s",user->username,context->current_action.arg);
348         event_send(mainConfig->event_mgr, EVENT_POSTUPLOAD, 0, event_args, context);
349         str_deallocate(event_args);
350       }
351       file_close(context->current_action.current_file,context);
352       FD_UNREGISTER(context->current_action.current_file,"Client file (RETR or STOR)");
353       context->current_action.current_file = -1;
354       context->current_action.bytesnow = 0;
355       context->current_action.token = TOK_UNKNOWN;
356       data_close(context);
357       ret = send_message(426,context);
358 /*      limiter_free(context->current_limiter);
359       context->current_limiter = NULL;*/
360     }
361     /* during a xfer, connection timeouts are not checked */
362     return 0;
363   }
364 
365   /* if user has 'idle' flag we check nothing */
366   if (user->flags && strchr(user->flags,FLAG_IDLE))
367     return 0;
368 
369   /* first we check user specific timeout */
370   if (user->max_idle_time>0) {
371     if (delay > (time_t)user->max_idle_time) {
372       /* TIMEOUT ! */
373       send_message_with_args(421,context,"Timeout, closing connection");
374       {
375         char inet_str[256];
376         int af = (context->family == WZD_INET6) ? AF_INET6 : AF_INET;
377         inet_str[0] = '\0';
378         inet_ntop(af,context->hostip,inet_str,sizeof(inet_str));
379         log_message("TIMEOUT","%s (%s) timed out after being idle %d seconds",
380             user->username,
381             inet_str,
382             (int)delay
383             );
384       }
385       context->exitclient = 1;
386 #ifdef WZD_MULTIPROCESS
387       exit(0);
388 #else /* WZD_MULTIPROCESS */
389       return 0;
390 #endif /* WZD_MULTIPROCESS */
391     }
392   }
393 
394   /* next we check for all groups */
395   for (i=0; i<user->group_num; i++) {
396     gptr = GetGroupByID(user->groups[i]);
397     if (gptr && gptr->max_idle_time > 0) {
398       if (delay > (time_t)gptr->max_idle_time) {
399         /* TIMEOUT ! */
400         send_message_with_args(421,context,"Timeout, closing connection");
401         {
402           char inet_str[256];
403           int af = (context->family == WZD_INET6) ? AF_INET6 : AF_INET;
404           inet_str[0] = '\0';
405           inet_ntop(af,context->hostip,inet_str,sizeof(inet_str));
406           log_message("TIMEOUT","%s (%s) timed out after being idle %d seconds",
407               user->username,
408               inet_str,
409               (int)delay
410               );
411         }
412         context->exitclient = 1;
413 #ifdef WZD_MULTIPROCESS
414         exit(0);
415 #else /* WZD_MULTIPROCESS */
416         return 1;
417 #endif /* WZD_MULTIPROCESS */
418       }
419     } /* if max_idle_time*/
420   }
421 
422   return 0;
423 }
424 
425 /*************** do_chdir ****************************/
426 
do_chdir(const char * wanted_path,wzd_context_t * context)427 int do_chdir(const char * wanted_path, wzd_context_t *context)
428 {
429   int ret;
430   char allowed[WZD_MAX_PATH],path[WZD_MAX_PATH], * ptr;
431   fs_filestat_t buf;
432   wzd_user_t * user;
433 
434   user = GetUserByID(context->userid);
435 
436   if ( !(user->userperms & RIGHT_CWD) ) return E_NOPERM;
437 
438   if (!wanted_path) return E_WRONGPATH;
439   ret = checkpath_new(wanted_path,path,context);
440   if (ret) return ret;
441   snprintf(allowed,WZD_MAX_PATH,"%s/",user->rootpath);
442 
443   /* deny retrieve to permissions file */
444   if (is_hidden_file(path))
445     return E_FILE_FORBIDDEN;
446 
447   REMOVE_TRAILING_SLASH(path);
448 
449   if (!fs_file_stat(path,&buf)) {
450     if (S_ISDIR(buf.mode)) {
451       char buffer[WZD_MAX_PATH], buffer2[WZD_MAX_PATH];
452       if (wanted_path[0] == '/') { /* absolute path */
453         wzd_strncpy(buffer,wanted_path,WZD_MAX_PATH);
454       } else {
455         wzd_strncpy(buffer,context->currentpath,WZD_MAX_PATH);
456         if (buffer[strlen(buffer)-1] != '/')
457           strlcat(buffer,"/",WZD_MAX_PATH);
458         strlcat(buffer,wanted_path,WZD_MAX_PATH);
459       }
460       stripdir(buffer,buffer2,WZD_MAX_PATH-1);
461 /*out_err(LEVEL_INFO,"DIR: %s NEW DIR: %s\n",buffer,buffer2);*/
462       wzd_strncpy(context->currentpath,buffer2,WZD_MAX_PATH-1);
463     }
464     else return E_NOTDIR;
465   }
466   else return E_FILE_NOEXIST;
467 
468   ptr = stripdir(context->currentpath,path,sizeof(path));
469   if (ptr) {
470     wzd_strncpy(context->currentpath,path,WZD_MAX_PATH-1);
471   }
472 
473   return E_OK;
474 }
475 
476 /*************** waitaccept **************************/
477 
waitaccept(wzd_context_t * context)478 int waitaccept(wzd_context_t * context)
479 {
480   fd_set fds;
481   struct timeval tv;
482   fd_t sock;
483   unsigned char remote_host[16];
484   unsigned int remote_port;
485   wzd_user_t * user;
486 
487   user = GetUserByID(context->userid);
488 
489   {
490     if (user && strchr(user->flags,FLAG_TLS_DATA) && context->tls_data_mode != TLS_PRIV) {
491       send_message_with_args(501,context,"Your class must use encrypted data connections");
492       return -1;
493     }
494   }
495 
496   sock = context->pasvsock;
497   do {
498     FD_ZERO(&fds);
499     FD_SET(sock,&fds);
500     tv.tv_sec=HARD_XFER_TIMEOUT; tv.tv_usec=0L; /* FIXME - HARD_XFER_TIMEOUT should be a variable */
501 
502     if (select(sock+1,&fds,NULL,NULL,&tv) <= 0) {
503       out_err(LEVEL_FLOOD,"accept timeout to client %s:%d.\n",__FILE__,__LINE__);
504       FD_UNREGISTER(context->pasvsock,"Client PASV socket");
505       socket_close(context->pasvsock);
506       context->pasvsock = -1;
507       send_message_with_args(501,context,"PASV timeout");
508       return -1;
509     }
510   } while (!FD_ISSET(sock,&fds));
511 
512   sock = socket_accept(context->pasvsock, remote_host, &remote_port, &context->datafamily);
513   if (sock == (fd_t)-1) {
514     out_err(LEVEL_FLOOD,"accept failed to client %s:%d.\n",__FILE__,__LINE__);
515     out_err(LEVEL_FLOOD,"errno is %d:%s.\n",errno,strerror(errno));
516     FD_UNREGISTER(context->pasvsock,"Client PASV socket");
517     socket_close(context->pasvsock);
518     context->pasvsock = -1;
519     send_message_with_args(501,context,"PASV timeout");
520     return -1;
521   }
522 
523   if (fxp_is_denied(user) && test_fxp((const char*)remote_host,context->datafamily,context) != 0) {
524     memset(context->dataip,0,16);
525     FD_UNREGISTER(context->pasvsock,"Client PASV socket");
526     socket_close(context->pasvsock);
527     context->pasvsock = -1;
528     socket_close(sock);
529     sock = -1;
530     send_message_with_args(501,context,"FXP not allowed");
531     return -1;
532   }
533 
534   /** \todo check destination port for security: >= 1024 */
535 
536 
537 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
538   if (context->tls_data_mode == TLS_PRIV) {
539     int ret;
540     ret = tls_init_datamode(sock, context);
541     if (ret) {
542       out_err(LEVEL_INFO,"WARNING TLS data negotiation failed with client %s:%d.\n",__FILE__,__LINE__);
543       FD_UNREGISTER(context->pasvsock,"Client PASV socket");
544       socket_close(context->pasvsock);
545       context->pasvsock = -1;
546       socket_close(sock);
547       sock = -1;
548       send_message_with_args(426,context,"Data connection closed (SSL/TLS negotiation failed).");
549       return -1;
550     }
551   }
552 #endif
553 
554   socket_close (context->pasvsock);
555   FD_UNREGISTER(context->pasvsock,"Client PASV socket");
556   context->pasvsock = sock;
557 
558   context->datafd = sock;
559   context->datamode = DATA_PASV;
560 
561   return sock;
562 }
563 
564 /*************** waitconnect *************************/
565 
waitconnect(wzd_context_t * context)566 int waitconnect(wzd_context_t * context)
567 {
568   int sock;
569   int ret;
570 
571   {
572     wzd_user_t * user;
573     user = GetUserByID(context->userid);
574     if (user && strchr(user->flags,FLAG_TLS_DATA) && context->tls_data_mode != TLS_PRIV) {
575       send_message_with_args(501,context,"Your class must use encrypted data connections");
576       return -1;
577     }
578   }
579 
580   if (context->datafamily == WZD_INET4)
581   {
582 
583     /** \todo TODO XXX FIXME check ipv4 IP at this point ! */
584 
585     ret = send_message(150,context); /* about to open data connection */
586     sock = socket_connect(context->dataip,context->datafamily,context->dataport,context->localport-1,context->controlfd,HARD_XFER_TIMEOUT);
587     if (sock == -1) {
588       ret = send_message(425,context);
589       return -1;
590     }
591 
592 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
593     if (context->tls_data_mode == TLS_PRIV) {
594       ret = tls_init_datamode(sock, context);
595       if (ret) {
596         send_message_with_args(426,context,"Data connection closed (SSL/TLS negotiation failed).");
597         return -1;
598       }
599     }
600 #endif
601 
602   } /* context->datafamily == WZD_INET4 */
603 #if defined(IPV6_SUPPORT)
604   else if (context->datafamily == WZD_INET6)
605   {
606 
607     /** \todo TODO XXX FIXME check ipv6 IP at this point ! */
608 
609     ret = send_message(150,context); /* about to open data connection */
610     sock = socket_connect(context->dataip,context->datafamily,context->dataport,context->localport-1,context->controlfd,HARD_XFER_TIMEOUT);
611     if (sock == -1) {
612       out_log(LEVEL_FLOOD,"Error establishing PORT connection: %s (%d)\n",strerror(errno),errno);
613       ret = send_message(425,context);
614       return -1;
615     }
616 
617 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
618     if (context->tls_data_mode == TLS_PRIV) {
619       ret = tls_init_datamode(sock, context);
620       if (ret) {
621         send_message_with_args(426,context,"Data connection closed (SSL/TLS negotiation failed).");
622         return -1;
623       }
624     }
625 #endif
626 
627   } /* context->datafamily == WZD_INET6 */
628 #endif /* IPV6_SUPPORT */
629   else
630   {
631     out_err(LEVEL_CRITICAL,"Invalid protocol %s:%d\n",__FILE__,__LINE__);
632     ret = send_message(425,context);
633     return -1;
634   }
635 
636   return sock;
637 }
638 
639 /*************** list_callback ***********************/
640 
list_callback(fd_t sock,wzd_context_t * context,char * line)641 int list_callback(fd_t sock, wzd_context_t * context, char *line)
642 {
643   fd_set fds;
644   struct timeval tv;
645 
646   do {
647     FD_ZERO(&fds);
648     FD_SET(sock,&fds);
649     tv.tv_sec=HARD_XFER_TIMEOUT; tv.tv_usec=0L; /* FIXME - HARD_XFER_TIMEOUT should be a variable */
650 
651     if (select(sock+1,NULL,&fds,NULL,&tv) <= 0) {
652       out_err(LEVEL_FLOOD,"LIST timeout to client.\n");
653       socket_close(sock);
654       send_message_with_args(501,context,"LIST timeout");
655       return 0;
656     }
657   } while (!FD_ISSET(sock,&fds));
658 
659 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
660   if (context->tls_data_mode == TLS_CLEAR)
661     clear_write(sock,line,strlen(line),0,HARD_XFER_TIMEOUT,context);
662   else
663 #endif
664     (context->write_fct)(sock,line,strlen(line),0,HARD_XFER_TIMEOUT,context);
665 
666   return 1;
667 }
668 
669 /*************** do_list *****************************/
670 
do_list(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)671 int do_list(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
672 {
673   char mask[1024],cmd[WZD_MAX_PATH], *path;
674   int ret,n;
675   fd_t sock;
676   char nullch[8];
677   char * cmask;
678   const char * param;
679   wzd_user_t * user;
680   enum list_type_t listtype;
681 
682   user = GetUserByID(context->userid);
683 
684   if ( !(user->userperms & RIGHT_LIST) ) {
685     ret = send_message_with_args(550,context,"LIST","No access");
686     return E_NOPERM;
687   }
688 
689   if (!str_checklength(arg, 0, WZD_MAX_PATH-10))
690   {
691     ret = send_message_with_args(501,context,"Argument or parameter too big.");
692     return E_PARAM_BIG;
693   }
694   param = str_tochar(arg);
695 
696   if (context->pasvsock == (fd_t)-1 && context->dataport == 0)
697   {
698     ret = send_message_with_args(501,context,"No data connection available.");
699     return E_NO_DATA_CTX;
700   }
701   if (context->state == STATE_XFER) {
702     ret = send_message(491,context);
703     return E_XFER_PROGRESS;
704   }
705 
706   if (strcasecmp(str_tochar(name),"nlst")==0)
707     listtype = LIST_TYPE_SHORT;
708   else
709     listtype = LIST_TYPE_LONG;
710 
711   context->resume = 0;
712 
713   strcpy(nullch,".");
714   mask[0] = '\0';
715   if (param) {
716 
717     while (param[0]=='-') {
718       n=1;
719       while (param[n]!=' ' && param[n]!=0) {
720         switch (param[n]) {
721           case 'a':
722             listtype |= LIST_SHOW_HIDDEN;
723         }
724         n++;
725       }
726       if (param[n]==' ') param = param+n+1;
727       else param = param+n;
728     }
729 
730     wzd_strncpy(cmd,param,sizeof(cmd));
731     if (cmd[0] != '\0' && cmd[strlen(cmd)-1]=='/') cmd[strlen(cmd)-1]='\0';
732 
733     if (strrchr(cmd,'*') || strrchr(cmd,'?')) /* wildcards */
734     {
735       char *ptr;
736       if (strrchr(cmd,'/')) { /* probably not in current path - need to readjust path */
737         if (strrchr(cmd,'/') > strrchr(cmd,'*')) {
738           /* char / is AFTER *, dir style: toto / * / .., we refuse */
739           ret = send_message_with_args(501,context,"You can't put wildcards in the middle of path, only in the last part.");
740           return 1;
741         }
742         ptr = strrchr(cmd,'/');
743         strncpy(cmd,ptr+1,WZD_MAX_PATH);
744         *ptr = '\0';
745       } else { /* simple wildcard */
746         strncpy(mask,cmd,sizeof(mask));
747         cmd[0] = '\0';
748       }
749     }
750     if (strrchr(cmd,'*') || strrchr(cmd,'?')) { /* wildcards in path ? ough */
751       ret = send_message_with_args(501,context,"You can't put wildcards in the middle of path, only in the last part.");
752       return E_PARAM_INVALID;
753     }
754   } else { /* no param, assume list of current dir */
755     cmd[0] = '\0';
756     param = nullch;
757   }
758 
759   if (param[0]=='/') param++;
760   if (param[0]=='/') {
761     ret = send_message_with_args(501,context,"Too many / in the path - is it a joke?");
762     return E_PARAM_INVALID;
763   }
764 
765   cmask = strrchr(mask,'/');
766   if (cmask) {	/* search file in path (with /), but without wildcards */
767     *cmask='\0';
768     strlcat(cmd,"/",WZD_MAX_PATH);
769     strlcat(cmd,mask,WZD_MAX_PATH);
770     strncpy(mask,cmask,sizeof(mask));
771   }
772 
773 /*#ifdef DEBUG
774 printf("path before: '%s'\n",cmd);
775 #endif*/
776 
777   path = wzd_malloc(WZD_MAX_PATH+1);
778   if ((ret = checkpath_new(cmd,path,context)) || !strncmp(mask,"..",2)) {
779     switch (ret) {
780     case E_NOTDIR:
781       /* return 501 for syntax error, see rfc3659 at section 7.2.1 */
782       ret = send_message_with_args(501,context,"Not a directory");
783       break;
784     case E_WRONGPATH:
785       ret = send_message_with_args(550,context,"LIST","Invalid path");
786       break;
787     case E_FILE_NOEXIST:
788       ret = send_message_with_args(550,context,"LIST","No such file or directory (no access?)");
789       break;
790     case E_FILE_FORBIDDEN:
791     case E_NOPERM:
792       ret = send_message_with_args(550,context,"LIST","Negative on that, Houston (access denied)");
793       break;
794     default:
795       ret = send_message_with_args(501,context,"LIST failed (syntax error?)");
796       break;
797     }
798     wzd_free(path);
799     return E_PARAM_INVALID;
800   }
801 
802   REMOVE_TRAILING_SLASH(path);
803 
804 /*#ifdef DEBUG
805 printf("path: '%s'\n",path);
806 #endif*/
807 
808   /* CHECK PERM */
809   ret = _checkPerm(path,RIGHT_LIST,user);
810 
811   if (ret) { /* no access */
812     ret = send_message_with_args(550,context,"LIST","No access");
813     wzd_free(path);
814     return E_NOPERM;
815   }
816 
817   if (context->pasvsock == (fd_t)-1) { /* PORT ! */
818 
819     /** \todo TODO check that ip is correct - no trying to fxp LIST ??!! */
820 
821     sock = waitconnect(context);
822     if (sock == (fd_t)-1) {
823       /* note: reply is done in waitconnect() */
824       wzd_free(path);
825       return E_CONNECTTIMEOUT;
826     }
827 
828   } else { /* PASV ! */
829     ret = send_message(150,context); /* about to open data connection */
830     if ((sock=waitaccept(context)) == (fd_t)-1) {
831       /* note: reply is done in waitaccept() */
832       wzd_free(path);
833       return E_PASV_FAILED;
834     }
835     context->pasvsock = -1;
836   }
837   FD_REGISTER(sock,"Client LIST socket");
838 
839   context->state = STATE_XFER;
840 
841   if (strlen(mask)==0) strcpy(mask,"*");
842 
843   if (list(sock,context,listtype,path,mask,list_callback))
844     ret = send_message(226,context);
845   else
846     ret = send_message_with_args(501,context,"Error processing list");
847 
848   wzd_free(path);
849 
850 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
851   if (context->tls_data_mode == TLS_PRIV)
852     ret = tls_close_data(context);
853 #endif
854   ret = socket_close(sock);
855   FD_UNREGISTER(sock,"Client LIST socket");
856   context->datafd = -1;
857   context->idle_time_start = time(NULL);
858   context->state = STATE_UNKNOWN;
859 
860   return E_OK;
861 }
862 
863 
864 /*************** do_mlsd *****************************/
865 
do_mlsd(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)866 int do_mlsd(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
867 {
868   int ret;
869   wzd_user_t * user;
870   fd_t sock;
871   char * path;
872 
873   user = GetUserByID(context->userid);
874 
875   if ( !(user->userperms & RIGHT_LIST) ) {
876     ret = send_message_with_args(550,context,"MLSD","No access");
877     return E_NOPERM;
878   }
879 
880   if (context->pasvsock == (fd_t)-1 && context->dataport == 0)
881   {
882     ret = send_message_with_args(501,context,"No data connection available.");
883     return E_NO_DATA_CTX;
884   }
885   if (context->state == STATE_XFER) {
886     ret = send_message(491,context);
887     return E_XFER_PROGRESS;
888   }
889 
890   path = wzd_malloc(WZD_MAX_PATH+1);
891   ret = checkpath_new(str_tochar(param),path,context);
892   if (ret != 0) {
893     switch (ret) {
894     case E_NOTDIR:
895       /* return 501 for syntax error, see rfc3659 at section 7.2.1 */
896       ret = send_message_with_args(501,context,"Not a directory");
897       break;
898     case E_WRONGPATH:
899       ret = send_message_with_args(550,context,"MLSD","Invalid path");
900       break;
901     case E_FILE_NOEXIST:
902       ret = send_message_with_args(550,context,"MLSD","No such file or directory (no access?)");
903       break;
904     case E_FILE_FORBIDDEN:
905     case E_NOPERM:
906       ret = send_message_with_args(550,context,"MLSD","Negative on that, Houston (access denied)");
907       break;
908     default:
909       ret = send_message_with_args(501,context,"MLSD failed (syntax error?)");
910       break;
911     }
912     wzd_free(path);
913     return E_PARAM_INVALID;
914   }
915 
916   REMOVE_TRAILING_SLASH(path);
917 
918   /* CHECK PERM */
919   ret = _checkPerm(path,RIGHT_LIST,user);
920 
921   if (ret) { /* no access */
922     ret = send_message_with_args(550,context,"LIST","No access");
923     wzd_free(path);
924     return E_NOPERM;
925   }
926 
927   if (context->pasvsock == (fd_t)-1) { /* PORT ! */
928 
929     /** \todo TODO check that ip is correct - no trying to fxp LIST ??!! */
930 
931     sock = waitconnect(context);
932     if (sock == (fd_t)-1) {
933       /* note: reply is done in waitconnect() */
934       wzd_free(path);
935       return E_CONNECTTIMEOUT;
936     }
937 
938   } else { /* PASV ! */
939     ret = send_message(150,context); /* about to open data connection */
940     if ((sock=waitaccept(context)) == (fd_t)-1) {
941       /* note: reply is done in waitaccept() */
942       wzd_free(path);
943       return E_PASV_FAILED;
944     }
945     context->pasvsock = -1;
946   }
947   FD_REGISTER(sock,"Client MLSD socket");
948 
949   context->state = STATE_XFER;
950 
951 
952 
953   if (!mlsd_directory(path,sock,list_callback,context))
954     ret = send_message(226,context);
955   else
956     ret = send_message_with_args(501,context,"Error processing list");
957 
958 
959   wzd_free(path);
960 
961 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
962   if (context->tls_data_mode == TLS_PRIV)
963     ret = tls_close_data(context);
964 #endif
965   ret = socket_close(sock);
966   FD_UNREGISTER(sock,"Client MLSD socket");
967   context->datafd = -1;
968   context->idle_time_start = time(NULL);
969   context->state = STATE_UNKNOWN;
970 
971   return E_OK;
972 }
973 
974 /*************** do_mlst *****************************/
975 
do_mlst(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)976 int do_mlst(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
977 {
978   int ret;
979   wzd_user_t * user;
980   char * path;
981   char * str;
982 
983   user = GetUserByID(context->userid);
984 
985   /* stat has the same behaviour as LIST */
986   if ( !(user->userperms & RIGHT_LIST) ) {
987     ret = send_message_with_args(550,context,"MLST","No access");
988     return E_NOPERM;
989   }
990 
991   if (!param || strlen(str_tochar(param))==0)
992   {
993     ret = send_message_with_args(501,context,"Usage: MLST filename");
994     return E_PARAM_BIG;
995   }
996 
997   if (!str_checklength(param, 1, WZD_MAX_PATH-10))
998   {
999     ret = send_message_with_args(501,context,"Argument or parameter too big.");
1000     return E_PARAM_BIG;
1001   }
1002 
1003   context->state = STATE_COMMAND;
1004 
1005   path = wzd_malloc(WZD_MAX_PATH+1);
1006   ret = checkpath_new(str_tochar(param),path,context);
1007   if (ret != 0) {
1008     switch (ret) {
1009       /* \todo enable MLST command to work on files and not just directories */
1010     case E_NOTDIR:
1011       /* return 501 for syntax error, see rfc3659 at section 7.2.1 */
1012       ret = send_message_with_args(501,context,"Not a directory");
1013       break;
1014     case E_WRONGPATH:
1015       ret = send_message_with_args(550,context,"MLST","Invalid path");
1016       break;
1017     case E_FILE_NOEXIST:
1018       ret = send_message_with_args(550,context,"MLST","No such file or directory (no access?)");
1019       break;
1020     case E_FILE_FORBIDDEN:
1021     case E_NOPERM:
1022       ret = send_message_with_args(550,context,"MLST","Negative on that, Houston (access denied)");
1023       break;
1024     default:
1025       ret = send_message_with_args(501,context,"MLST failed (syntax error?)");
1026       break;
1027     }
1028     wzd_free(path);
1029     return E_PARAM_INVALID;
1030   }
1031 
1032   REMOVE_TRAILING_SLASH(path);
1033 
1034   if ( (str = mlst_single_file(path, context)) == NULL) {
1035     ret = send_message_with_args(501,context,"Error occurred");
1036     wzd_free(path);
1037     return E_PARAM_INVALID;
1038   }
1039 
1040   strcat(str,"\r\n"); /* TODO check size */
1041 
1042 
1043   {
1044     wzd_string_t * buffer = str_allocate();
1045     str_sprintf(buffer,"250- Listing %s\r\n",str_tochar(param));
1046     send_message_raw(str_tochar(buffer),context);
1047     str_deallocate(buffer);
1048   }
1049 
1050   send_message_raw(str,context);
1051 
1052   ret = send_message_raw("250 End\r\n",context);
1053 
1054   context->idle_time_start = time(NULL);
1055   context->state = STATE_UNKNOWN;
1056 
1057   wzd_free(path);
1058   wzd_free(str);
1059 
1060   return E_OK;
1061 }
1062 
1063 /*************** do_opts *****************************/
do_opts(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)1064 int do_opts(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
1065 {
1066   const char *ptr;
1067   int ret;
1068 
1069   ptr = str_tochar(param);
1070 
1071   if (strncasecmp(ptr,"UTF8",4)==0)
1072   {
1073     ptr += 4;
1074     if (*ptr++ != ' ') goto label_opts_error;
1075 
1076 #ifdef HAVE_UTF8
1077     if (strncasecmp(ptr,"ON",2)==0)
1078     {
1079       context->connection_flags |= CONNECTION_UTF8;
1080       ret = send_message_with_args(200, context, "UTF8 OPTS ON");
1081       return 0;
1082     }
1083     else if (strncasecmp(ptr,"OFF",2)==0)
1084     {
1085       context->connection_flags &= ~(CONNECTION_UTF8);
1086       ret = send_message_with_args(200, context, "UTF8 OPTS OFF");
1087       return 0;
1088     }
1089 #endif
1090     /* let it go to error return */
1091   } /* UTF8 */
1092   if (strncasecmp(ptr,"MLST",4)==0)
1093   {
1094     /** \todo XXX FIXME implement options support for MLST */
1095     ret = send_message_with_args(200, context, "MLST OPTS Type;Size;Modify;Perm;UNIX.mode;");
1096     return 0;
1097   } /* MLST */
1098 
1099 label_opts_error:
1100   ret = send_message_with_args(501,context,"OPTS option not recognized");
1101 
1102   return 0;
1103 }
1104 
1105 /*************** do_stat *****************************/
1106 
do_stat(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)1107 int do_stat(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
1108 {
1109   char mask[1024],cmd[WZD_MAX_PATH], *path;
1110   int ret,n;
1111   fd_t sock;
1112   char nullch[8];
1113   char * cmask;
1114   const char *param;
1115   wzd_user_t * user;
1116   enum list_type_t listtype;
1117   tls_data_mode_t old_data_mode;
1118 
1119   user = GetUserByID(context->userid);
1120 
1121   /* stat has the same behaviour as LIST */
1122   if ( !(user->userperms & RIGHT_LIST) ) {
1123     ret = send_message_with_args(550,context,"LIST","No access");
1124     return E_NOPERM;
1125   }
1126 
1127 
1128   if (!str_checklength(arg, 1, WZD_MAX_PATH-10))
1129   {
1130     ret = send_message_with_args(501,context,"Argument or parameter too big.");
1131     return E_PARAM_BIG;
1132   }
1133   param = str_tochar(arg);
1134 
1135   listtype = LIST_TYPE_LONG;
1136 
1137   context->resume = 0;
1138   context->state = STATE_COMMAND;
1139 
1140   strcpy(nullch,".");
1141   mask[0] = '\0';
1142   if (param) {
1143 
1144     while (param[0]=='-') {
1145       n=1;
1146       while (param[n]!=' ' && param[n]!=0) {
1147         switch (param[n]) {
1148           case 'a':
1149             listtype |= LIST_SHOW_HIDDEN;
1150         }
1151         n++;
1152       }
1153       if (param[n]==' ') param = param+n+1;
1154       else param = param+n;
1155     }
1156 
1157     wzd_strncpy(cmd,param,sizeof(cmd));
1158     if (cmd[strlen(cmd)-1]=='/') cmd[strlen(cmd)-1]='\0';
1159 
1160     if (strrchr(cmd,'*') || strrchr(cmd,'?')) /* wildcards */
1161     {
1162       char *ptr;
1163       if (strrchr(cmd,'/')) { /* probably not in current path - need to readjust path */
1164         if (strrchr(cmd,'/') > strrchr(cmd,'*')) {
1165           /* char / is AFTER *, dir style: toto / * / .., we refuse */
1166           ret = send_message_with_args(501,context,"You can't put wildcards in the middle of path, only in the last part.");
1167           return 1;
1168         }
1169         ptr = strrchr(cmd,'/');
1170         strncpy(cmd,ptr+1,WZD_MAX_PATH);
1171         *ptr = '\0';
1172       } else { /* simple wildcard */
1173         strncpy(mask,cmd,sizeof(mask));
1174         cmd[0] = '\0';
1175       }
1176     }
1177     if (strrchr(cmd,'*') || strrchr(cmd,'?')) { /* wildcards in path ? ough */
1178       ret = send_message_with_args(501,context,"You can't put wildcards in the middle of path, only in the last part.");
1179       return E_PARAM_INVALID;
1180     }
1181   } else { /* no param, assume list of current dir */
1182     cmd[0] = '\0';
1183     param = nullch;
1184   }
1185 
1186   if (param[0]=='/') param++;
1187   if (param[0]=='/') {
1188     ret = send_message_with_args(501,context,"Too many / in the path - is it a joke?");
1189     return E_PARAM_INVALID;
1190   }
1191 
1192   cmask = strrchr(mask,'/');
1193   if (cmask) {	/* search file in path (with /), but without wildcards */
1194     *cmask='\0';
1195     strlcat(cmd,"/",WZD_MAX_PATH);
1196     strlcat(cmd,mask,WZD_MAX_PATH);
1197     strncpy(mask,cmask,sizeof(mask));
1198   }
1199 
1200 /*#ifdef DEBUG
1201 printf("path before: '%s'\n",cmd);
1202 #endif*/
1203 
1204   path = wzd_malloc(WZD_MAX_PATH + 1);
1205   if (checkpath_new(cmd,path,context) || !strncmp(mask,"..",2)) {
1206     ret = send_message_with_args(501,context,"Invalid filter/path");
1207     wzd_free(path);
1208     return E_PARAM_INVALID;
1209   }
1210 
1211   REMOVE_TRAILING_SLASH(path);
1212 
1213 /*#ifdef DEBUG
1214 printf("path: '%s'\n",path);
1215 #endif*/
1216 
1217   /* CHECK PERM */
1218   ret = _checkPerm(path,RIGHT_LIST,user);
1219 
1220   if (ret) { /* no access */
1221     ret = send_message_with_args(550,context,"STAT","No access");
1222     wzd_free(path);
1223     return E_NOPERM;
1224   }
1225 
1226   sock = context->controlfd;
1227 
1228   if (strlen(mask)==0) strcpy(mask,"*");
1229 
1230   /* \todo XXX FIXME horrible workaround to avoid sending clear data inside ssl stream */
1231   old_data_mode = context->tls_data_mode;
1232   context->tls_data_mode = (context->connection_flags & CONNECTION_TLS) ? TLS_PRIV : TLS_CLEAR;
1233 
1234   send_message_raw("213-Status of .:\r\n",context);
1235   send_message_raw("total 0\r\n",context);
1236   if (list(sock,context,listtype,path,mask,list_callback))
1237     ret = send_message_raw("213 End of Status\r\n",context);
1238   else
1239     ret = send_message_raw("213 Error processing list\r\n",context);
1240 
1241   context->idle_time_start = time(NULL);
1242   context->state = STATE_UNKNOWN;
1243   context->tls_data_mode = old_data_mode;
1244 
1245   wzd_free(path);
1246 
1247   return E_OK;
1248 }
1249 
1250 /*************** do_mkdir ****************************/
1251 
do_mkdir(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)1252 int do_mkdir(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
1253 {
1254   char  * cmd = NULL, * path = NULL;
1255   char * buffer = NULL;
1256   int ret;
1257   wzd_user_t * user;
1258   const char *param;
1259 
1260   if (!str_checklength(arg,1,WZD_MAX_PATH-1))
1261   {
1262     ret = send_message_with_args(501,context,"Invalid path");
1263     return E_PARAM_INVALID;
1264   }
1265   param = str_tochar(arg);
1266 
1267   cmd = wzd_malloc(WZD_MAX_PATH+1);
1268   path = wzd_malloc(WZD_MAX_PATH+1);
1269   buffer = wzd_malloc(WZD_MAX_PATH+1);
1270 
1271   user = GetUserByID(context->userid);
1272 
1273   if ( !(user->userperms & RIGHT_MKDIR) ) { ret = E_NOPERM; goto label_error_mkdir; }
1274 
1275   if (strcmp(param,"/")==0) { ret = E_WRONGPATH; goto label_error_mkdir; }
1276 
1277   if (param[0] != '/') {
1278     strcpy(cmd,".");
1279     if (checkpath_new(cmd,path,context)) { ret = E_WRONGPATH; goto label_error_mkdir; }
1280     if (path[strlen(path)-1]!='/') strcat(path,"/");
1281     strlcat(path,param,WZD_MAX_PATH);
1282   } else {
1283     wzd_strncpy(cmd,param,WZD_MAX_PATH);
1284     ret = checkpath_new(cmd,path,context);
1285     if (ret != E_FILE_NOEXIST) { ret = E_WRONGPATH; goto label_error_mkdir; }
1286     if (path[strlen(path)-1]!='/') strcat(path,"/");
1287 /*    if (path[strlen(path)-1]=='/') path[strlen(path)-1]='\0';*/
1288   }
1289   REMOVE_TRAILING_SLASH(path);
1290 
1291   ret = checkpath_new(param,buffer,context);
1292   if (ret != E_FILE_NOEXIST) goto label_error_mkdir;
1293 
1294 #if DEBUG
1295   if (ret || errno) {
1296     if (ret != E_FILE_NOEXIST)
1297       out_err(LEVEL_FLOOD,"Making directory '%s' (%d, %s %d %d)\n",buffer,ret,strerror(errno),errno,ENOENT);
1298     switch (ret) {
1299     case E_USER_IDONTEXIST: out_log(LEVEL_HIGH,"mkdir: user does not exist !\n"); break;
1300     case E_PARAM_NULL: out_log(LEVEL_HIGH,"mkdir: no input parameter\n"); break;
1301     case E_PARAM_BIG: out_log(LEVEL_HIGH,"mkdir: parameter too long\n"); break;
1302     case E_WRONGPATH: out_log(LEVEL_HIGH,"mkdir: wrong path\n"); break;
1303     case E_FILE_NOEXIST: break; /* not an error ! */
1304     case E_NOPERM: out_log(LEVEL_HIGH,"mkdir: no permission\n"); break;
1305     default:
1306       break;
1307     }
1308   }
1309   else
1310     out_err(LEVEL_FLOOD,"Making directory '%s' (%d)\n",buffer,ret);
1311 #endif
1312 
1313   {
1314     wzd_string_t * event_args = str_allocate();
1315     str_sprintf(event_args,"%s %s",user->username,buffer);
1316     ret = event_send(mainConfig->event_mgr, EVENT_PREMKDIR, 0, event_args, context);
1317     str_deallocate(event_args);
1318   }
1319 
1320   if (ret != EVENT_OK && ret != EVENT_BREAK) {
1321     out_log(LEVEL_NORMAL, "MKDIR denied by hook (returned %d)\n", ret);
1322     ret = send_message_with_args(501,context,"MKDIR denied");
1323     return E_XFER_REJECTED;
1324   }
1325 
1326 
1327   if (buffer[strlen(buffer)-1]=='/')
1328     buffer[strlen(buffer)-1]='\0';
1329 
1330   /* deny retrieve to permissions file */
1331   if (is_hidden_file(path)) {
1332     wzd_free(buffer);
1333     wzd_free(path);
1334     wzd_free(cmd);
1335     ret = send_message_with_args(553,context,"Forbidden!");
1336     return E_FILE_FORBIDDEN;
1337   }
1338 
1339   /** \bug why this test ? it breaks mkdir inside symlinks ! */
1340 /*  if (strcmp(path,buffer) != 0) { ret = E_MKDIR_PARSE; goto label_error_mkdir; }*/
1341 
1342   /* check section path-filter */
1343   {
1344     char *ptr;
1345     wzd_section_t * section;
1346     wzd_strncpy(path,buffer,WZD_MAX_PATH);
1347     ptr = strrchr(path,'/');
1348     if (ptr && ptr!=&path[0]) {
1349       *ptr='\0';
1350       /* we can reuse cmd */
1351       if (param[0] != '/') {
1352         unsigned int length;
1353         strncpy(cmd,context->currentpath,WZD_MAX_PATH-1-strlen(param));
1354         length = strlen(cmd);
1355         if (cmd[length-1]!='/') {
1356           cmd[length++] = '/';
1357         }
1358         strncpy(cmd+length,param,WZD_MAX_PATH-1-length);
1359       } else {
1360         strncpy(cmd,param,WZD_MAX_PATH);
1361       }
1362       /* we need to give the ftp-relative path here */
1363       section = section_find(mainConfig->section_list,cmd);
1364       if (section && !section_check_filter(section,ptr+1))
1365       {
1366         out_err(LEVEL_FLOOD,"path [%s] does not match path-filter\n",ptr+1);
1367         ret = send_message_with_args(553,context,"Dirname does not match pathfilter");
1368         wzd_free(buffer);
1369         wzd_free(path);
1370         wzd_free(cmd);
1371         return E_MKDIR_PATHFILTER;
1372       }
1373     }
1374   }
1375 
1376   context->current_action.token = TOK_MKD;
1377   strncpy(context->current_action.arg,buffer,HARD_LAST_COMMAND_LENGTH);
1378   context->current_action.current_file = -1;
1379 
1380   ret = file_mkdir(buffer,0755,context); /* TODO umask ? - should have a variable here */
1381 
1382   if (ret) {
1383     if (ret != E_NOPERM)
1384       out_err(LEVEL_FLOOD,"MKDIR returned %d (%s)\n",errno,strerror(errno));
1385     goto label_error_mkdir; /* keep current ret value for later use */
1386   } else {
1387     const char *groupname=NULL;
1388     if (user->group_num > 0) {
1389       groupname = GetGroupByID(user->groups[0])->groupname;
1390     }
1391     file_chown(buffer,user->username,groupname,context);
1392 
1393     /* send message header */
1394     send_message_raw("257- Command okay\r\n",context);
1395     {
1396       wzd_string_t * event_args = STR(buffer);
1397       event_send(mainConfig->event_mgr, EVENT_MKDIR, 257, event_args, context);
1398       str_deallocate(event_args);
1399     }
1400     ret = send_message_with_args(257,context,param,"created");
1401 
1402     if (param[0] != '/') {
1403       strcpy(buffer,context->currentpath);
1404       strlcat(buffer,"/",WZD_MAX_PATH);
1405       strlcat(buffer,param,WZD_MAX_PATH);
1406     } else {
1407       strcpy(buffer,param);
1408     }
1409     stripdir(buffer,path,WZD_MAX_PATH-1);
1410 
1411     log_message("NEWDIR","\"%s\" \"%s\" \"%s\" \"%s\"",
1412         path, /* ftp-absolute path */
1413         user->username,
1414         (groupname)?groupname:"No Group",
1415         user->tagline
1416         );
1417   }
1418   context->idle_time_start = time(NULL);
1419   wzd_free(buffer);
1420   wzd_free(path);
1421   wzd_free(cmd);
1422 
1423   return E_OK;
1424 
1425 label_error_mkdir:
1426   if (ret == E_NOPERM)
1427     snprintf(buffer,WZD_MAX_PATH-1,"Could not create dir: permission denied");
1428   else
1429     snprintf(buffer,WZD_MAX_PATH-1,"Could not create dir '%s' (%d)",(param)?param:"(NULL)",ret);
1430   send_message_with_args(553,context,buffer);
1431   wzd_free(buffer);
1432   wzd_free(path);
1433   wzd_free(cmd);
1434   return ret;
1435 }
1436 
1437 /*************** do_rmdir ****************************/
1438 
do_rmdir(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)1439 int do_rmdir(UNUSED wzd_string_t *name, wzd_string_t * arg, wzd_context_t * context)
1440 {
1441   char path[WZD_MAX_PATH], buffer[WZD_MAX_PATH];
1442   fs_filestat_t s;
1443   int ret;
1444   wzd_user_t * user;
1445   const char *param;
1446 
1447   if (!str_checklength(arg,1,WZD_MAX_PATH-1))
1448   {
1449     ret = send_message_with_args(501,context,"Invalid path");
1450     return E_PARAM_INVALID;
1451   }
1452   param = str_tochar(arg);
1453 
1454   user = GetUserByID(context->userid);
1455 
1456   if ( !(user->userperms & RIGHT_RMDIR) ) { ret = E_NOPERM;; goto label_error_rmdir; }
1457 
1458 
1459   if (checkpath_new(param,path,context)) { ret = E_WRONGPATH; goto label_error_rmdir; }
1460 
1461   /* if path is / terminated, lstat will return the dir itself in case
1462    * of a symlink
1463    */
1464   if (path[strlen(path)-1]=='/')
1465     path[strlen(path)-1]='\0';
1466 
1467   /* deny retrieve to permissions file */
1468   if (is_hidden_file(path)) {
1469     ret = send_message_with_args(553,context,"Forbidden!");
1470     return E_FILE_FORBIDDEN;
1471   }
1472 
1473   if (fs_file_lstat(path,&s)) { ret = E_FILE_NOEXIST; goto label_error_rmdir; }
1474   if (!S_ISDIR(s.mode)) {
1475     ret = send_message_with_args(553,context,"Not a directory");
1476     return E_NOTDIR;
1477   }
1478 
1479   /* check permissions */
1480   ret = file_rmdir(path,context);
1481 
1482   if (ret) {
1483     out_err(LEVEL_FLOOD,"RMDIR returned %d (%s)\n",errno,strerror(errno));
1484     ret = E_PARAM_INVALID; goto label_error_rmdir;
1485   } else {
1486     /* send message header */
1487     send_message_raw("258- Command okay\r\n",context);
1488     {
1489       wzd_string_t * event_args = str_allocate();
1490       str_sprintf(event_args,"%s %s",user->username,path);
1491       event_send(mainConfig->event_mgr, EVENT_RMDIR, 258, event_args, context);
1492       str_deallocate(event_args);
1493     }
1494     ret = send_message_with_args(258,context,param,"Removed");
1495 
1496     {
1497       const char *groupname=NULL;
1498       char tbuf[WZD_MAX_PATH], path[WZD_MAX_PATH];
1499 
1500       if (user->group_num > 0) {
1501         groupname = GetGroupByID(user->groups[0])->groupname;
1502       }
1503 
1504       if (param[0] != '/') {
1505         strcpy(tbuf,context->currentpath);
1506         strlcat(tbuf,"/",WZD_MAX_PATH);
1507         strlcat(tbuf,param,WZD_MAX_PATH);
1508       } else {
1509         strcpy(tbuf,param);
1510       }
1511       stripdir(tbuf,path,WZD_MAX_PATH-1);
1512 
1513       log_message("DELDIR","\"%s\" \"%s\" \"%s\" \"%s\"",
1514           path, /* ftp-absolute path */
1515           user->username,
1516           (groupname)?groupname:"No Group",
1517           user->tagline
1518           );
1519     }
1520 
1521   }
1522 
1523   context->idle_time_start = time(NULL);
1524 
1525   return E_OK;
1526 
1527 label_error_rmdir:
1528   snprintf(buffer,WZD_MAX_PATH-1,"Could not delete dir '%s'",(param)?param:"(NULL)");
1529   send_message_with_args(553,context,buffer);
1530   return ret;
1531 }
1532 
1533 /*************** do_port *****************************/
do_port(UNUSED wzd_string_t * name,wzd_string_t * args,wzd_context_t * context)1534 int do_port(UNUSED wzd_string_t *name, wzd_string_t *args, wzd_context_t * context)
1535 {
1536   int a0,a1,a2,a3;
1537   unsigned int p1, p2;
1538   int ret;
1539   wzd_user_t * user;
1540 
1541   if (context->pasvsock != (fd_t)-1) {
1542     socket_close(context->pasvsock);
1543     context->pasvsock = -1;
1544   }
1545   if (!args) {
1546     ret = send_message_with_args(501,context,"Invalid parameters");
1547     return E_PARAM_NULL;
1548   }
1549   if ((sscanf(str_tochar(args),"%d,%d,%d,%d,%d,%d",
1550           &a0,&a1,&a2,&a3,
1551           &p1,&p2))<6) {
1552     ret = send_message(502,context);
1553     return E_PARAM_INVALID;
1554   }
1555 
1556   context->dataip[0] = (unsigned char)a0;
1557   context->dataip[1] = (unsigned char)a1;
1558   context->dataip[2] = (unsigned char)a2;
1559   context->dataip[3] = (unsigned char)a3;
1560 
1561   user = GetUserByID(context->userid);
1562 
1563   if (fxp_is_denied(user) && test_fxp((const char*)context->dataip,WZD_INET4,context) != 0) {
1564     memset(context->dataip,0,16);
1565     ret = send_message_with_args(501,context,"FXP not allowed");
1566     return E_NOPERM;
1567   }
1568 
1569   /** \todo check destination port for security: >= 1024 */
1570 
1571   context->dataport = ((p1&0xff)<<8) | (p2&0xff);
1572   context->datafamily = WZD_INET4;
1573   ret = send_message_with_args(200,context,"Command okay");
1574   return E_OK;
1575 }
1576 
1577 /*************** do_pasv *****************************/
do_pasv(UNUSED wzd_string_t * name,UNUSED wzd_string_t * args,wzd_context_t * context)1578 int do_pasv(UNUSED wzd_string_t *name, UNUSED wzd_string_t *args, wzd_context_t * context)
1579 {
1580   int ret;
1581   unsigned long addr;
1582   unsigned int size,port;
1583   struct sockaddr_in sai;
1584   unsigned char *myip;
1585   unsigned char pasv_bind_ip[16];
1586   unsigned char buffer[16];
1587   int offset=0;
1588   int count=0;
1589   int all_ports_used=1;
1590 
1591   size = sizeof(struct sockaddr_in);
1592   port = mainConfig->pasv_low_range; /* use pasv range min */
1593 
1594   /* close existing pasv connections */
1595   if (context->pasvsock != (fd_t)-1) {
1596     socket_close(context->pasvsock);
1597     FD_UNREGISTER(context->pasvsock,"Client PASV socket");
1598 /*    port = context->pasvsock+1; *//* FIXME force change of socket */
1599     context->pasvsock = -1;
1600   }
1601 
1602   /* create socket */
1603   if ((context->pasvsock=socket(AF_INET,SOCK_STREAM,0)) == (fd_t)-1) {
1604     context->pasvsock = -1;
1605     ret = send_message(425,context);
1606     return E_NO_DATA_CTX;
1607   }
1608 
1609   myip = getmyip(context->controlfd, context->family, buffer); /* FIXME use a variable to get pasv ip ? */
1610 
1611   if (mainConfig->pasv_ip[0] == 0) {
1612 #if defined(IPV6_SUPPORT)
1613       if (IN6_IS_ADDR_V4MAPPED(PORCUS_CAST(myip)) )
1614         memcpy(pasv_bind_ip,myip+12,4);
1615       else
1616 #endif /* IPV6_SUPPORT */
1617         memcpy(pasv_bind_ip,myip,4);
1618   } else {
1619 #if defined(IPV6_SUPPORT)
1620     if (IN6_IS_ADDR_V4MAPPED(PORCUS_CAST(context->hostip)))
1621       offset = 12;
1622 #endif
1623     /* do NOT send pasv_ip if used from private network */
1624     if (context->hostip[offset+0]==10 ||
1625       (context->hostip[offset+0] == 172 && context->hostip[offset+1] == 16) ||
1626       (context->hostip[offset+0] == 192 && context->hostip[offset+1] == 168 && context->hostip[offset+2] == 0) ||
1627       (context->hostip[offset+0] == 127 && context->hostip[offset+1] == 0 && context->hostip[offset+2] == 0 && context->hostip[offset+3] == 1))
1628     {
1629 #if defined(IPV6_SUPPORT)
1630       if (IN6_IS_ADDR_V4MAPPED(PORCUS_CAST(myip)))
1631         memcpy(pasv_bind_ip,myip+12,4);
1632       else
1633 #endif /* IPV6_SUPPORT */
1634         memcpy(pasv_bind_ip,myip,4);
1635     }
1636     else
1637 #if defined(IPV6_SUPPORT)
1638       if (IN6_IS_ADDR_V4MAPPED(PORCUS_CAST(mainConfig->pasv_ip)))
1639         memcpy(pasv_bind_ip,mainConfig->pasv_ip+12,4);
1640       else
1641 #endif /* IPV6_SUPPORT */
1642         memcpy(pasv_bind_ip,mainConfig->pasv_ip,4);
1643   }
1644 /*  out_err(LEVEL_CRITICAL,"PASV_IP: %d.%d.%d.%d\n",
1645       pasv_bind_ip[0], pasv_bind_ip[1], pasv_bind_ip[2], pasv_bind_ip[3]);*/
1646 
1647   port = mainConfig->pasv_low_range; /* use pasv range min */
1648   count = mainConfig->pasv_high_range - mainConfig->pasv_low_range + 1;
1649 #ifndef WIN32
1650   port = port + (random()) % count; /* we try to change starting port for random */
1651 #else
1652   port = port + (rand()) % count; /* we try to change starting port for random */
1653 #endif
1654   while (count-- > 0) {
1655     memset(&sai,0,size);
1656 
1657     sai.sin_family = AF_INET;
1658     sai.sin_port = htons((unsigned short)port);
1659     /* XXX TODO FIXME bind to specific address works, but not for NAT */
1660     /* XXX TODO FIXME always bind to 'myip' ?! */
1661     addr = INADDR_ANY;
1662 /*    memcpy( (void*)&addr, pasv_bind_ip, sizeof(unsigned long));*/
1663 
1664     memcpy(&sai.sin_addr.s_addr,&addr,sizeof(unsigned long));
1665 
1666     if (bind(context->pasvsock,(struct sockaddr *)&sai,size)==0) {
1667       /* found a free port, stop searching */
1668       all_ports_used = 0;
1669       break;
1670     }
1671     port++; /* retry with next port */
1672 
1673     if (port > mainConfig->pasv_high_range) {
1674       /* reached the top of the range, continue searching from the bottom */
1675       port = mainConfig->pasv_low_range;
1676     }
1677   }
1678 
1679   if (all_ports_used) {
1680     /* all ports are in use, return an error */
1681     out_log(LEVEL_HIGH, "PASV: all possible PASV ports are in use\n");
1682     socket_close(context->pasvsock);
1683     context->pasvsock = -1;
1684     ret = send_message(425, context);
1685     return E_NO_DATA_CTX;
1686   }
1687 
1688   /* sanity check */
1689   if (port < mainConfig->pasv_low_range || port > mainConfig->pasv_high_range)
1690   {
1691     out_log(LEVEL_CRITICAL, "PASV: attempted to bind to port out of range (%d not in [%d , %d])\n",
1692         port, mainConfig->pasv_low_range, mainConfig->pasv_high_range);
1693         socket_close(context->pasvsock);
1694         context->pasvsock = -1;
1695         ret = send_message(425, context);
1696         return E_NO_DATA_CTX;
1697   }
1698 
1699   /* sanity check */
1700   if (port >= 65536) {
1701     out_log(LEVEL_CRITICAL, "PASV: attempted to bind to invalid port 65536\n");
1702     socket_close(context->pasvsock);
1703     context->pasvsock = -1;
1704     ret = send_message(425, context);
1705     return E_NO_DATA_CTX;
1706   }
1707 
1708   if (listen(context->pasvsock,1)<0) {
1709     out_log(LEVEL_CRITICAL,"Major error during listen: errno %d error %s\n",errno,strerror(errno));
1710     socket_close(context->pasvsock);
1711     context->pasvsock = -1;
1712     ret = send_message(425, context);
1713     return E_NO_DATA_CTX;
1714   }
1715 
1716   FD_REGISTER(context->pasvsock,"Client PASV socket");
1717 
1718   context->datafamily = WZD_INET4;
1719   myip = getmyip(context->controlfd, context->family, buffer); /* FIXME use a variable to get pasv ip ? */
1720 
1721   ret = send_message_with_args(227,context,pasv_bind_ip[0], pasv_bind_ip[1], pasv_bind_ip[2], pasv_bind_ip[3],(port>>8)&0xff, port&0xff);
1722 
1723 #if 0
1724   if (mainConfig->pasv_ip[0] == 0) {
1725     ret = send_message_with_args(227,context,myip[0], myip[1], myip[2], myip[3],(port>>8)&0xff, port&0xff);
1726   } else {
1727     /* do NOT send pasv_ip if used from private network */
1728     if (context->hostip[0]==10 ||
1729       (context->hostip[0] == 172 && context->hostip[1] == 16) ||
1730       (context->hostip[0] == 192 && context->hostip[1] == 168 && context->hostip[2] == 0) ||
1731       (context->hostip[0] == 127 && context->hostip[1] == 0 && context->hostip[2] == 0 && context->hostip[3] == 1))
1732       ret = send_message_with_args(227,context,myip[0], myip[1], myip[2], myip[3],(port>>8)&0xff, port&0xff);
1733     else
1734       ret = send_message_with_args(227,context,mainConfig->pasv_ip[0], mainConfig->pasv_ip[1],
1735           mainConfig->pasv_ip[2], mainConfig->pasv_ip[3],(port>>8)&0xff, port&0xff);
1736   }
1737 #endif
1738 
1739   if (strcasecmp("cpsv",str_tochar(name))==0)
1740     context->tls_role = TLS_CLIENT_MODE;
1741 
1742   return E_OK;
1743 }
1744 
1745 /*************** do_eprt *****************************/
do_eprt(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)1746 int do_eprt(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
1747 {
1748 #if defined(IPV6_SUPPORT)
1749   int ret;
1750   char sep;
1751   char net_prt;
1752   char * net_addr, * s_tcp_port;
1753   char * ptr;
1754   unsigned int tcp_port;
1755   struct in_addr addr4;
1756   struct in6_addr addr6;
1757   char * param, * orig_param;
1758   wzd_user_t * user;
1759 
1760   if (context->pasvsock != (fd_t)-1) {
1761     socket_close(context->pasvsock);
1762     context->pasvsock = -1;
1763   }
1764   /* context->resume = 0; */
1765   if (!arg || strlen(str_tochar(arg)) <= 7) {
1766     ret = send_message(502,context);
1767     ret = send_message_with_args(501,context,"Invalid argument");
1768     return E_PARAM_INVALID;
1769   }
1770 
1771   orig_param = param = strdup(str_tochar(arg));
1772 
1773   sep = *param++;
1774   net_prt = *param++;
1775   if ( (*param++) != sep || (net_prt != '1' && net_prt != '2') ) {
1776     ret = send_message_with_args(501,context,"Invalid argument");
1777     free(orig_param);
1778     return E_PARAM_INVALID;
1779   }
1780 
1781   net_addr = param;
1782   while (*param && (*param) != sep ) param++;
1783   if ( !*param ) {
1784     ret = send_message_with_args(501,context,"Invalid argument");
1785     free(orig_param);
1786     return E_PARAM_INVALID;
1787   }
1788 
1789   *param = '\0';
1790   param++;
1791 
1792   s_tcp_port = param;
1793   while (*param && (*param) != sep ) param++;
1794   if ( !*param || *param != sep ) {
1795     ret = send_message_with_args(501,context,"Invalid argument");
1796     free(orig_param);
1797     return E_PARAM_INVALID;
1798   }
1799 
1800   *param = '\0';
1801 
1802   tcp_port = strtoul(s_tcp_port,&ptr,0);
1803   if (*ptr) {
1804     ret = send_message_with_args(501,context,"Invalid port");
1805     free(orig_param);
1806     return E_PARAM_INVALID;
1807   }
1808 
1809   /* resolve net_addr to context->dataip */
1810   switch (net_prt) {
1811   case '1':
1812     if ( (ret=inet_pton(AF_INET,net_addr,&addr4)) <= 0 )
1813     {
1814       ret = send_message_with_args(501,context,"Invalid host");
1815       free(orig_param);
1816       return E_PARAM_INVALID;
1817     }
1818     memcpy(context->dataip,(const char *)&addr4.s_addr,4);
1819     context->datafamily = WZD_INET4;
1820     break;
1821   case '2':
1822     if ( (ret=inet_pton(AF_INET6,net_addr,&addr6)) <= 0 )
1823     {
1824       ret = send_message_with_args(501,context,"Invalid host");
1825       free(orig_param);
1826       return E_PARAM_INVALID;
1827     }
1828     memcpy(context->dataip,addr6.s6_addr,16);
1829     context->datafamily = WZD_INET6;
1830     break;
1831   default:
1832     ret = send_message_with_args(501,context,"Invalid protocol");
1833     free(orig_param);
1834     return E_PARAM_INVALID;
1835   }
1836 
1837   context->dataport = tcp_port;
1838 
1839   user = GetUserByID(context->userid);
1840 
1841   if (fxp_is_denied(user) && test_fxp((const char*)context->dataip,context->datafamily,context) != 0) {
1842     memset(context->dataip,0,16);
1843     ret = send_message_with_args(501,context,"FXP not allowed");
1844     free(orig_param);
1845     return E_NOPERM;
1846   }
1847 
1848   /** \todo check destination port for security: >= 1024 */
1849 
1850   free(orig_param);
1851   ret = send_message_with_args(200,context,"Command okay");
1852 #else /* defined(IPV6_SUPPORT) */
1853   send_message(502,context);
1854 #endif
1855   return E_OK;
1856 }
1857 
1858 /*************** do_epsv *****************************/
do_epsv(UNUSED wzd_string_t * name,UNUSED wzd_string_t * arg,wzd_context_t * context)1859 int do_epsv(UNUSED wzd_string_t *name, UNUSED wzd_string_t *arg, wzd_context_t * context)
1860 {
1861   int ret;
1862   unsigned int size,port;
1863 #if defined(IPV6_SUPPORT)
1864   struct sockaddr_in6 sai6;
1865 #else
1866   struct sockaddr_in sai;
1867   unsigned long addr;
1868 #endif
1869   unsigned char *myip;
1870   unsigned char pasv_bind_ip[16];
1871   unsigned char buffer[16];
1872 
1873 #if !defined(IPV6_SUPPORT)
1874   size = sizeof(struct sockaddr_in);
1875 #else
1876   size = sizeof(struct sockaddr_in6);
1877 #endif
1878   port = mainConfig->pasv_low_range; /* use pasv range min */
1879 
1880   /* close existing pasv connections */
1881   if (context->pasvsock != (fd_t)-1) {
1882     socket_close(context->pasvsock);
1883 /*    port = context->pasvsock+1; *//* FIXME force change of socket */
1884     context->pasvsock = -1;
1885   }
1886 
1887   /* create socket */
1888 #if !defined(IPV6_SUPPORT)
1889   if ((context->pasvsock = socket(PF_INET,SOCK_STREAM,0)) == (fd_t)-1)
1890 #else
1891   if ((context->pasvsock = socket(PF_INET6,SOCK_STREAM,0)) == (fd_t)-1)
1892 #endif
1893   {
1894     context->pasvsock = -1;
1895     ret = send_message(425,context);
1896     return E_NO_DATA_CTX;
1897   }
1898 
1899   myip = getmyip(context->controlfd, context->family, buffer); /* FIXME use a variable to get pasv ip ? */
1900 
1901   if (mainConfig->pasv_ip[0] == 0) {
1902     memcpy(pasv_bind_ip,myip,sizeof(pasv_bind_ip));
1903   } else {
1904     /* do NOT send pasv_ip if used from private network */
1905     /** \todo TODO XXX FIXME private networks are not the same in ipv6 */
1906     if (context->hostip[0]==10 ||
1907       (context->hostip[0] == 172 && context->hostip[1] == 16) ||
1908       (context->hostip[0] == 192 && context->hostip[1] == 168 && context->hostip[2] == 0) ||
1909       (context->hostip[0] == 127 && context->hostip[1] == 0 && context->hostip[2] == 0 && context->hostip[3] == 1))
1910       memcpy(pasv_bind_ip,myip,sizeof(pasv_bind_ip));
1911     else
1912       memcpy(pasv_bind_ip,mainConfig->pasv_ip,sizeof(pasv_bind_ip));
1913   }
1914 /*  out_err(LEVEL_CRITICAL,"PASV_IP: %d.%d.%d.%d\n",
1915       pasv_bind_ip[0], pasv_bind_ip[1], pasv_bind_ip[2], pasv_bind_ip[3]);*/
1916 
1917   while (port <= mainConfig->pasv_high_range) { /* use pasv range max */
1918 #if !defined(IPV6_SUPPORT)
1919     memset(&sai,0,size);
1920 
1921     sai.sin_family = AF_INET;
1922     sai.sin_port = htons((unsigned short)port);
1923     /* XXX TODO FIXME bind to specific address works, but not for NAT */
1924     /* XXX TODO FIXME always bind to 'myip' ?! */
1925     addr = INADDR_ANY;
1926 /*    memcpy( (void*)&addr, pasv_bind_ip, sizeof(unsigned long));*/
1927 
1928     memcpy(&sai.sin_addr.s_addr,&addr,sizeof(unsigned long));
1929 
1930     if (bind(context->pasvsock,(struct sockaddr *)&sai,size)==0) break;
1931 #else /* IPV6_SUPPORT */
1932     memset(&sai6,0,size);
1933 
1934     sai6.sin6_family = AF_INET6;
1935     sai6.sin6_port = htons(port);
1936     sai6.sin6_flowinfo = 0;
1937 /*     sai6.sin6_addr = in6addr_any;*/ /* FIXME VISUAL */
1938     memset(&sai6.sin6_addr,0,16);
1939     /* XXX TODO FIXME bind to specific address works, but not for NAT */
1940     /* XXX TODO FIXME always bind to 'myip' ?! */
1941 /*    addr = INADDR_ANY;*/
1942 
1943 /*    memcpy(&sai.sin_addr.s_addr,&addr,sizeof(unsigned long));*/
1944 
1945     if (bind(context->pasvsock,(struct sockaddr *)&sai6,size)==0) break;
1946 
1947 #endif /* IPV6_SUPPORT */
1948     port++; /* retry with next port */
1949   }
1950   if (port > mainConfig->pasv_high_range || port >= 65536) {
1951     out_log(LEVEL_CRITICAL,"EPSV: could not find any available port for binding");
1952     socket_close(context->pasvsock);
1953     context->pasvsock = -1;
1954     ret = send_message(425,context);
1955     return E_NO_DATA_CTX;
1956   }
1957 
1958   if (listen(context->pasvsock,1)<0) {
1959     out_log(LEVEL_CRITICAL,"EPSV: could not listen on port %d: errno %d error %s\n",port,errno,strerror(errno));
1960     socket_close(context->pasvsock);
1961     context->pasvsock = -1;
1962     ret = send_message(425,context);
1963     return E_NO_DATA_CTX;
1964   }
1965 
1966   FD_REGISTER(context->pasvsock,"Client PASV socket");
1967 
1968   myip = getmyip(context->controlfd, context->family, buffer); /* FIXME use a variable to get pasv ip ? */
1969 
1970 #if !defined(IPV6_SUPPORT)
1971   context->datafamily = WZD_INET4;
1972   ret = send_message_with_args(227,context,pasv_bind_ip[0], pasv_bind_ip[1], pasv_bind_ip[2], pasv_bind_ip[3],(port>>8)&0xff, port&0xff);
1973 #else
1974   context->datafamily = WZD_INET6;
1975   {
1976     char buf[256];
1977     snprintf(buf,256,"229 Entering Passive Mode (|||%d|)\r\n",port);
1978     ret = send_message_raw(buf,context);
1979   }
1980 #endif
1981 
1982 #if 0
1983   if (mainConfig->pasv_ip[0] == 0) {
1984     ret = send_message_with_args(227,context,myip[0], myip[1], myip[2], myip[3],(port>>8)&0xff, port&0xff);
1985   } else {
1986     /* do NOT send pasv_ip if used from private network */
1987     if (context->hostip[0]==10 ||
1988       (context->hostip[0] == 172 && context->hostip[1] == 16) ||
1989       (context->hostip[0] == 192 && context->hostip[1] == 168 && context->hostip[2] == 0) ||
1990       (context->hostip[0] == 127 && context->hostip[1] == 0 && context->hostip[2] == 0 && context->hostip[3] == 1))
1991       ret = send_message_with_args(227,context,myip[0], myip[1], myip[2], myip[3],(port>>8)&0xff, port&0xff);
1992     else
1993       ret = send_message_with_args(227,context,mainConfig->pasv_ip[0], mainConfig->pasv_ip[1],
1994           mainConfig->pasv_ip[2], mainConfig->pasv_ip[3],(port>>8)&0xff, port&0xff);
1995   }
1996 #endif
1997   return E_OK;
1998 }
1999 
2000 /*************** do_retr *****************************/
2001 /** \brief Prepares a data retrieval transfer.
2002  *
2003  * Ensures that a data connection is available, checks user permissions,
2004  * sends EVENT_PREDOWNLOAD, and opens file.
2005  * The real transfer is handled by data_execute().
2006  */
do_retr(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2007 int do_retr(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2008 {
2009   char path[WZD_MAX_PATH];
2010   int fd;
2011   u64_t bytestot, bytesnow, byteslast;
2012   fd_t sock;
2013   int ret;
2014   wzd_user_t * user;
2015   const char *param;
2016   connection_state_t restorestate;
2017 
2018   param = str_tochar(arg);
2019   user = GetUserByID(context->userid);
2020 
2021   if ( !(user->userperms & RIGHT_RETR) ) {
2022     ret = send_message_with_args(550,context,"RETR","No access");
2023     return E_NOPERM;
2024   }
2025 
2026 /* TODO FIXME send all error or any in this function ! */
2027   /* we must have a data connetion */
2028   if ((context->pasvsock == (fd_t)-1) && (context->dataport == 0)) {
2029     ret = send_message_with_args(501,context,"No data connection available - issue PORT or PASV first");
2030     return E_NO_DATA_CTX;
2031   }
2032   if (context->state == STATE_XFER) {
2033     ret = send_message(491,context);
2034     return E_XFER_PROGRESS;
2035   }
2036 
2037   if (!param || strlen(param)==0) {
2038     ret = send_message_with_args(501,context,"Incorrect filename");
2039     return E_PARAM_INVALID;
2040   }
2041 
2042   if (strlen(param)>WZD_MAX_PATH-1) {
2043     ret = send_message_with_args(501,context,"Filename too long");
2044     return E_PARAM_BIG;
2045   }
2046 
2047   /*
2048    * Ignore some checkpath_new errorst for the moment,
2049    * test_path will do this after the predownload hook runs
2050    * in case the hook changes something
2051    */
2052   ret = checkpath_new(param,path,context);
2053 
2054   if ((ret != 0) && (ret != E_NOPERM) && (ret != E_FILE_NOEXIST))
2055   {
2056     ret = send_message_with_args(501,context,"Invalid file name");
2057     return E_PARAM_INVALID;
2058   }
2059 
2060   /* we need to put context into TOK_RETR state before the hook runs
2061    * so that any cookie parsing in the hook works correctly
2062    */
2063   restorestate = context->current_action.token;
2064   context->current_action.token = TOK_RETR;
2065   strncpy(context->current_action.arg,path,HARD_LAST_COMMAND_LENGTH);
2066 
2067 
2068   {
2069     wzd_string_t * event_args = str_allocate();
2070     str_sprintf(event_args,"%s %s",user->username,path);
2071     ret = event_send(mainConfig->event_mgr, EVENT_PREDOWNLOAD, 0, event_args, context);
2072     str_deallocate(event_args);
2073   }
2074 
2075   if (ret != EVENT_OK && ret != EVENT_BREAK) {
2076     out_log(LEVEL_NORMAL, "Download denied by hook (returned %d)\n", ret);
2077     ret = send_message_with_args(501,context,"Download denied");
2078     context->current_action.token = restorestate;
2079     return E_XFER_REJECTED;
2080   }
2081 
2082   /* restore the context state in case we exit before downloading*/
2083   context->current_action.token = restorestate;
2084 
2085   if (test_path(path,context)) {
2086     ret = send_message_with_args(501,context,"Invalid file name");
2087     return E_PARAM_INVALID;
2088   }
2089 
2090 
2091   /* trailing / ? */
2092   if (path[strlen(path)-1]=='/')
2093     path[strlen(path)-1] = '\0';
2094 
2095   /* deny retrieve to permissions file */
2096   if (is_hidden_file(path)) {
2097     ret = send_message_with_args(501,context,"Forbidden");
2098     return E_FILE_FORBIDDEN;
2099   }
2100 
2101   /* check user ratio */
2102   if (ratio_check_download(path,context)) {
2103     ret = send_message_with_args(501,context,"Insufficient credits - Upload first");
2104     return E_CREDS_INSUFF;
2105   }
2106 
2107 
2108   if ((fd=file_open(path,O_RDONLY,RIGHT_RETR,context))==-1) { /* XXX allow access to files being uploaded ? */
2109     ret = send_message_with_args(550,context,param,"nonexistant file or permission denied");
2110 /*    socket_close(sock);*/
2111     return E_FILE_NOEXIST;
2112   }
2113   FD_REGISTER(fd,"Client file (RETR)");
2114 
2115   /* get length */
2116   bytestot = file_seek(fd,0,SEEK_END);
2117   if ((off_t)bytestot == (off_t)-1) /* happens with 0-length files */
2118     bytestot = 0;
2119   bytesnow = byteslast=context->resume;
2120 
2121   if (context->pasvsock == (fd_t)-1) { /* PORT ! */
2122 
2123     /* \todo TODO IP-check needed (FXP ?!) */
2124     sock = waitconnect(context);
2125     if (sock == (fd_t)-1) {
2126       file_close(fd,context);
2127       FD_UNREGISTER(fd,"Client file (RETR)");
2128       /* note: reply is done in waitconnect() */
2129       return E_CONNECTTIMEOUT;
2130     }
2131 
2132   } else { /* PASV ! */
2133     /* FIXME */
2134 /*    sprintf(cmd, "150 Opening BINARY data connection for '%s' (%ld bytes).\r\n",
2135       param, bytestot);*/
2136     ret = send_message(150,context);
2137     if ((sock=waitaccept(context)) == (fd_t)-1) {
2138       file_close(fd,context);
2139       FD_UNREGISTER(fd,"Client file (RETR)");
2140       /* note: reply is done in waitaccept() */
2141       return E_PASV_FAILED;
2142     }
2143   }
2144   FD_REGISTER(sock,"Client data socket (RETR)");
2145 
2146   context->datafd = sock;
2147 
2148   file_seek(fd,(fs_off_t)context->resume,SEEK_SET);
2149 
2150   out_log(LEVEL_FLOOD,"Download: User %s starts downloading %s (%" PRIu64 " bytes)\n", user->username,param,bytestot);
2151 
2152   context->state = STATE_XFER;
2153   context->current_action.token = TOK_RETR;
2154   strncpy(context->current_action.arg,path,HARD_LAST_COMMAND_LENGTH);
2155   context->current_action.current_file = fd;
2156   context->current_action.bytesnow = 0;
2157   context->idle_time_data_start = context->current_action.tm_start = time(NULL);
2158   gettimeofday(&context->current_action.tv_start,NULL);
2159 
2160 /*  if (user->max_dl_speed)
2161     context->current_limiter = limiter_new(user->max_dl_speed);
2162   else
2163     context->current_limiter = NULL;*/
2164 
2165 /*  if (user->max_dl_speed)
2166   {*/
2167     context->current_dl_limiter.maxspeed = user->max_dl_speed;
2168     context->current_dl_limiter.bytes_transfered = 0;
2169 #ifndef _MSC_VER
2170     gettimeofday(&context->current_dl_limiter.current_time,NULL);
2171 #else
2172     _ftime(&context->current_dl_limiter.current_time);
2173 #endif
2174 /*  }
2175   else
2176     context->current_dl_limiter.maxspeed = 0;*/
2177 
2178   /* we increment the counter of downloaded files at the beggining
2179    * of the download
2180    */
2181   user->stats.files_dl_total++;
2182 
2183   context->resume=0;
2184   context->idle_time_start = time(NULL);
2185 
2186   if (CFG_GET_OPTION(mainConfig,CFG_OPT_EXPERIMENTAL)) {
2187     if (context->transfer_thread != NULL) {
2188       out_log(LEVEL_HIGH,"ERROR a transfer thread is already started\n");
2189       data_end_transfer(0 /* is_upload */, 0 /* end_ok */, context);
2190       ret = send_message(426,context);
2191       return E_XFER_PROGRESS;
2192     }
2193     context->is_transferring = 1;
2194     ret = data_start_thread_retr(context);
2195 /*    ret = do_local_retr(context);*/
2196   }
2197 
2198   return E_OK;
2199 }
2200 
2201 /*************** do_stor *****************************/
2202 /** \brief Store file on the FTP server
2203  *
2204  * Check permissions, open a data connection and stores data
2205  * in a file on the server. If the file does not exist, it is created,
2206  * otherwise it depends if a resume marker was received (see REST).
2207  *
2208  * The corresponding FTP commands are STOR (RFC959 p29) and APPE (RFC959 p29)
2209  */
do_stor(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2210 int do_stor(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2211 {
2212   char path[WZD_MAX_PATH],path2[WZD_MAX_PATH];
2213   int fd;
2214   u64_t bytesnow, byteslast;
2215   fd_t sock;
2216   int ret;
2217   wzd_user_t * user;
2218   const char *param;
2219   unsigned long open_flags;
2220 
2221   param = str_tochar(arg);
2222 
2223   user = GetUserByID(context->userid);
2224 
2225   if ( !(user->userperms & RIGHT_STOR) ) {
2226     ret = send_message_with_args(550,context,"STOR","No access");
2227     return E_NOPERM;
2228   }
2229 
2230 /* TODO FIXME send all error or any in this function ! */
2231   /* we must have a data connection */
2232   if ((context->pasvsock == (fd_t)-1) && (context->dataport == 0)) {
2233     ret = send_message_with_args(503,context,"Issue PORT or PASV First");
2234     return E_NO_DATA_CTX;
2235   }
2236   if (context->state == STATE_XFER) {
2237     ret = send_message(491,context);
2238     return E_XFER_PROGRESS;
2239   }
2240 
2241   if (!param || strlen(param)==0) {
2242     ret = send_message_with_args(501,context,"Incorrect filename");
2243     return E_PARAM_INVALID;
2244   }
2245 
2246   if (strlen(param)>WZD_MAX_PATH-1) {
2247     ret = send_message_with_args(501,context,"Filename too long");
2248     return E_PARAM_BIG;
2249   }
2250 
2251   if (param[0]=='/') { /* absolute path */
2252     strcpy(path,user->rootpath);
2253   } else { /* absolute path */
2254     /* FIXME these 2 lines forbids STOR dir/filename style - normal ? */
2255 /*   XXX if (strrchr(param,'/'))
2256       param = strrchr(param,'/')+1; XXX */
2257 
2258     strcpy(path2,".");
2259     if (checkpath_new(path2,path,context)) {
2260       ret = send_message_with_args(501,context,"Incorrect filename");
2261       return E_PARAM_INVALID;
2262     }
2263     if (path[strlen(path)-1] != '/') strcat(path,"/");
2264   } /* absolute path */
2265   strlcat(path,param,WZD_MAX_PATH);
2266 
2267   /* TODO call checkpath again ? see do_mkdir */
2268 
2269   /* deny retrieve to permissions file */
2270   if (is_hidden_file(path)) {
2271     ret = send_message_with_args(501,context,"Forbidden");
2272     return E_FILE_FORBIDDEN;
2273   }
2274 
2275   {
2276     wzd_string_t * event_args = str_allocate();
2277     str_sprintf(event_args,"%s %s",user->username,path);
2278     ret = event_send(mainConfig->event_mgr, EVENT_PREUPLOAD, 0, event_args, context);
2279     str_deallocate(event_args);
2280   }
2281 
2282   if (ret != EVENT_OK && ret != EVENT_BREAK) {
2283     out_log(LEVEL_NORMAL, "Upload denied by hook (returned %d)\n", ret);
2284     ret = send_message_with_args(501,context,"Upload denied");
2285     return E_XFER_REJECTED;
2286   }
2287 
2288 
2289   /* overwrite protection */
2290   /* TODO make permissions per-dir + per-group + per-user ? */
2291 /*  if (context->userinfo.perms & PERM_OVERWRITE) {
2292     fp=file_open(path,"r",RIGHT_STOR,context),
2293     if (!fp) {
2294       fclose(fp);
2295       return 2;
2296     }
2297   }*/
2298   if (strcasecmp(str_tochar(name),"appe")==0)
2299     context->resume = (unsigned long)-1;
2300 
2301   open_flags = O_WRONLY|O_CREAT;
2302 
2303   if ((fd=file_open(path,open_flags,RIGHT_STOR,context))==-1) {
2304     ret = send_message_with_args(501,context,"Nonexistant file or permission denied");
2305     return E_FILE_NOEXIST;
2306   }
2307   FD_REGISTER(fd,"Client file (STOR)");
2308 
2309   if (context->pasvsock == (fd_t)-1) { /* PORT ! */
2310     sock = waitconnect(context);
2311     if (sock == (fd_t)-1) {
2312       file_close(fd,context);
2313       FD_UNREGISTER(fd,"Client file (STOR)");
2314       /* note: reply is done in waitconnect() */
2315       return E_CONNECTTIMEOUT;
2316     }
2317   } else { /* PASV ! */
2318     ret = send_message(150,context);
2319     if ((sock=waitaccept(context)) == (fd_t)-1) {
2320       file_close(fd,context);
2321       FD_UNREGISTER(fd,"Client file (STOR)");
2322       /* note: reply is done in waitaccept() */
2323       return E_PASV_FAILED;
2324     }
2325   }
2326   FD_REGISTER(sock,"Client data socket (STOR)");
2327 
2328   context->datafd = sock;
2329 
2330   /* set owner */
2331   {
2332     const char *groupname=NULL;
2333     if (user->group_num > 0) {
2334       groupname = GetGroupByID(user->groups[0])->groupname;
2335     }
2336     file_chown (path,user->username,groupname,context);
2337   }
2338 
2339   bytesnow = byteslast = 0;
2340   if (context->resume == (unsigned long)-1)
2341     file_seek(fd,0,SEEK_END);
2342   else
2343     file_seek(fd,(fs_off_t)context->resume,SEEK_SET);
2344 
2345   out_err(LEVEL_FLOOD,"Download: User %s starts uploading %s\n",
2346     user->username,param);
2347 
2348   context->state = STATE_XFER;
2349   context->current_action.token = TOK_STOR;
2350   strncpy(context->current_action.arg,path,HARD_LAST_COMMAND_LENGTH);
2351   context->current_action.current_file = fd;
2352   context->current_action.bytesnow = 0;
2353   context->idle_time_data_start = context->current_action.tm_start = time(NULL);
2354   gettimeofday(&context->current_action.tv_start,NULL);
2355 
2356   context->current_ul_limiter.maxspeed = user->max_ul_speed;
2357   context->current_ul_limiter.bytes_transfered = 0;
2358 #ifndef WIN32 /* FIXME VISUAL */
2359   gettimeofday(&context->current_ul_limiter.current_time,NULL);
2360 #else
2361   _ftime(&context->current_ul_limiter.current_time);
2362 #endif
2363 
2364   context->resume=0;
2365   context->idle_time_start = time(NULL);
2366 
2367   if (CFG_GET_OPTION(mainConfig,CFG_OPT_EXPERIMENTAL)) {
2368     if (context->transfer_thread != NULL) {
2369       out_log(LEVEL_HIGH,"ERROR a transfer thread is already started\n");
2370       data_end_transfer(0 /* is_upload */, 0 /* end_ok */, context);
2371       ret = send_message(426,context);
2372       return E_XFER_PROGRESS;
2373     }
2374     context->is_transferring = 1;
2375     ret = data_start_thread_stor(context);
2376 /*    ret = do_local_stor(context);*/
2377   }
2378 
2379   return E_OK;
2380 }
2381 
2382 /*************** do_mdtm *****************************/
do_mdtm(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2383 int do_mdtm(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2384 {
2385   char path[WZD_MAX_PATH], tm[32];
2386   fs_filestat_t s;
2387   int ret;
2388 
2389   if (!str_checklength(param,1,WZD_MAX_PATH-1)) {
2390     ret = send_message_with_args(501,context,"Incorrect argument");
2391     return E_PARAM_INVALID;
2392   }
2393 
2394   if (!checkpath_new(str_tochar(param),path,context)) {
2395     if (path[strlen(path)-1]=='/')
2396       path[strlen(path)-1]='\0';
2397 
2398     /* deny retrieve to permissions file */
2399     if (is_hidden_file(path)) {
2400       ret = send_message_with_args(501,context,"Forbidden");
2401       return E_FILE_FORBIDDEN;
2402     }
2403 
2404     if (fs_file_stat(path,&s)==0) {
2405       context->resume = 0L;
2406       strftime(tm,sizeof(tm),"%Y%m%d%H%M%S",gmtime(&s.mtime));
2407       ret = send_message_with_args(213,context,tm);
2408       return E_OK;
2409     }
2410   }
2411   ret = send_message_with_args(501,context,"File inexistent or no access?");
2412   return E_FILE_NOEXIST;
2413 }
2414 
2415 /*************** do_size *****************************/
do_moda(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2416 int do_moda(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2417 {
2418 #ifdef HAVE_STRPTIME
2419   extern char *strptime (__const char *__restrict __s,
2420     __const char *__restrict __fmt, struct tm *__tp);
2421 #endif
2422   int ret, command_ok=0;
2423   char * facts, * fact, * value, * ptr;
2424   struct tm tm_atime, tm_mtime;
2425   struct utimbuf utime_buf = {0, 0};
2426   char * filename;
2427   char path[WZD_MAX_PATH];
2428 
2429   if (!param) {
2430     ret = send_message_with_args(501,context,"Invalid syntax");
2431     return E_PARAM_INVALID;
2432   }
2433 
2434   facts = strdup(str_tochar(param));
2435   filename = strstr(facts,"; ");
2436   if (!filename) {
2437     free(facts);
2438     ret = send_message_with_args(501,context,"Invalid syntax");
2439     return E_PARAM_INVALID;
2440   }
2441   filename++; /* skip ';' */
2442   *filename++ = '\0';
2443 
2444   if (checkpath_new(filename,path,context)) {
2445     free(facts);
2446     ret = send_message_with_args(501,context,"Invalid filename");
2447     return E_PARAM_INVALID;
2448   }
2449   if (path[strlen(path)-1]=='/')
2450     path[strlen(path)-1]='\0';
2451 
2452   /** \todo XXX open file to avoid race conditions */
2453 
2454   fact = strtok_r(facts,"=",&ptr);
2455   if (!fact) {
2456     free(facts);
2457     ret = send_message_with_args(501,context,"Invalid syntax");
2458     return E_PARAM_INVALID;
2459   }
2460 
2461   while (fact) {
2462     value = strtok_r(NULL,";",&ptr);
2463     if (!value) {
2464       free(facts);
2465       ret = send_message_with_args(501,context,"Invalid syntax");
2466       return E_PARAM_INVALID;
2467     }
2468 
2469     /* test 'fact' and make action */
2470     /** \todo XXX it would be a good idea to make 'atomic' modifications, or to lock file ! */
2471 
2472 /**** accessed *******/
2473     if (strcmp(fact,"accessed")==0) {
2474       memset(&tm_atime,0,sizeof(struct tm));
2475       ptr=strptime(value,"%Y%m%d%H%M%S",&tm_atime);
2476       if (ptr == NULL || *ptr != '\0') {
2477         snprintf(path,WZD_MAX_PATH,"Invalid value for fact '%s', aborting",fact);
2478         ret = send_message_with_args(501,context,path);
2479         return E_PARAM_INVALID;
2480       }
2481 
2482       utime_buf.actime = mktime(&tm_atime);
2483       ret = utime(path,&utime_buf);
2484 
2485       if (ret) {
2486         snprintf(path,WZD_MAX_PATH,"Error in fact %s: '%s', aborting",fact,value);
2487         free(facts);
2488         ret = send_message_with_args(501,context,path);
2489         return E_PARAM_INVALID;
2490       }
2491       command_ok++;
2492 
2493     } else
2494 /**** modify *******/
2495     if (strcmp(fact,"modify")==0) {
2496       memset(&tm_mtime,0,sizeof(struct tm));
2497       ptr=strptime(value,"%Y%m%d%H%M%S",&tm_mtime);
2498       if (ptr == NULL || *ptr != '\0') {
2499         snprintf(path,WZD_MAX_PATH,"Invalid value for fact '%s', aborting",fact);
2500         ret = send_message_with_args(501,context,path);
2501         return E_PARAM_INVALID;
2502       }
2503 
2504       utime_buf.modtime = mktime(&tm_mtime);
2505       ret = utime(path,&utime_buf);
2506 
2507       if (ret) {
2508         snprintf(path,WZD_MAX_PATH,"Error in fact %s: '%s', aborting",fact,value);
2509         free(facts);
2510         ret = send_message_with_args(501,context,path);
2511         return E_PARAM_INVALID;
2512       }
2513       command_ok++;
2514 
2515     } else
2516 /**** unknown *******/
2517     {
2518       snprintf(path,WZD_MAX_PATH,"Unsupported fact '%s', aborting",fact);
2519       free(facts);
2520       ret = send_message_with_args(501,context,path);
2521       return E_PARAM_INVALID;
2522     }
2523 
2524     fact = strtok_r(NULL,"=",&ptr);
2525   }
2526 
2527   free(facts);
2528 
2529   if (command_ok)
2530     ret = send_message_with_args(200,context,"Command okay");
2531   else
2532     ret = send_message_with_args(501,context,"Not yet implemented");
2533 
2534   return E_PARAM_INVALID;
2535 }
2536 
2537 /*************** do_size *****************************/
do_size(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2538 int do_size(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2539 {
2540   char path[WZD_MAX_PATH];
2541   char buffer[1024];
2542   fs_filestat_t s;
2543   int ret;
2544 
2545   if (!str_checklength(param,1,WZD_MAX_PATH-1)) {
2546     ret = send_message_with_args(501,context,"Incorrect argument");
2547     return E_PARAM_INVALID;
2548   }
2549   if (!checkpath_new(str_tochar(param),path,context)) {
2550     if (path[strlen(path)-1]=='/')
2551       path[strlen(path)-1]='\0';
2552 
2553   /* deny retrieve to permissions file */
2554     if (is_hidden_file(path)) {
2555       ret = send_message_with_args(501,context,"Forbidden");
2556       return E_FILE_FORBIDDEN;
2557     }
2558 
2559 
2560     if (fs_file_stat(path,&s)==0) {
2561       snprintf(buffer,1024,"%" PRIu64,s.size);
2562       ret = send_message_with_args(213,context,buffer);
2563       return E_OK;
2564     }
2565   }
2566   ret = send_message_with_args(501,context,"File inexistent or no access?");
2567   return E_FILE_NOEXIST;
2568 }
2569 
2570 /*************** do_abor *****************************/
2571 /** \brief Abort current transfer
2572  *
2573  * Abort current service command and any associated transfer of data.
2574  * The command connection is not closed, but the data connection is closed.
2575  *
2576  * The corresponding FTP command is ABOR (RFC959 p30)
2577  */
do_abor(UNUSED wzd_string_t * name,UNUSED wzd_string_t * arg,wzd_context_t * context)2578 int do_abor(UNUSED wzd_string_t *name, UNUSED wzd_string_t *arg, wzd_context_t * context)
2579 {
2580   int ret;
2581   wzd_user_t * user;
2582 
2583   user = GetUserByID(context->userid);
2584 
2585   if (context->pasvsock != (fd_t)-1 && context->datafd != context->pasvsock) {
2586     socket_close(context->pasvsock);
2587     FD_UNREGISTER(context->pasvsock,"Client PASV socket");
2588     context->pasvsock=-1;
2589   }
2590   if (context->current_action.current_file != (fd_t)-1) {
2591     /* transfer aborted, we should send a 426 */
2592     ret = send_message(426,context);
2593     out_xferlog(context, 0 /* incomplete */);
2594 
2595     if (context->current_action.token == TOK_STOR || context->current_action.token == TOK_RETR) {
2596       data_end_transfer((context->current_action.token == TOK_STOR) ? 1:0 /* is_upload */, 0 /* end_ok */, context);
2597     }
2598 
2599   }
2600   ret = send_message(226,context);
2601   return E_OK;
2602 }
2603 
2604 /*************** do_cwd ******************************/
do_cwd(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2605 int do_cwd(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2606 {
2607   int ret;
2608   const char *param;
2609 
2610   param = str_tochar(arg);
2611   context->resume = 0;
2612   if (strcmp(str_tochar(name),"cdup")==0) param="..";
2613 
2614   if (param == NULL) {
2615     param = "/";
2616   }
2617   /* avoir error if current is "/" and action is ".." */
2618   if (!strcmp("..",param)
2619       && ( !strcmp("/",context->currentpath)
2620          || ( (strlen(context->currentpath)<=3) && (context->currentpath[2]==':') ) )
2621       ) {
2622     ret = send_message_with_args(250,context,context->currentpath," now current directory.");
2623     return E_OK;
2624   }
2625   if ( (ret=do_chdir(param,context)) ) {
2626     switch (ret) {
2627     case E_NOTDIR:
2628       /* return 501 for syntax error, see rfc3659 at section 7.2.1 */
2629       ret = send_message_with_args(501,context,param?param:"(null)","Not a directory");
2630       break;
2631     case E_WRONGPATH:
2632       ret = send_message_with_args(550,context,param?param:"(null)","Invalid path");
2633       break;
2634     case E_FILE_NOEXIST:
2635       ret = send_message_with_args(550,context,param?param:"(null)","No such file or directory (no access?)");
2636       break;
2637     case E_FILE_FORBIDDEN:
2638     case E_NOPERM:
2639       ret = send_message_with_args(550,context,param?param:"(null)","Negative on that, Houston (access denied)");
2640       break;
2641     default:
2642       ret = send_message_with_args(501,context,param?param:"(null)","CWD failed (syntax error?)");
2643       break;
2644     }
2645     return E_OK;
2646   }
2647   /** \bug we have to ensure that the reply is RFC compliant */
2648   ret = send_message_with_args(250,context,context->currentpath," now current directory.");
2649   return E_OK;
2650 }
2651 
2652 /*************** do_dele *****************************/
do_dele(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2653 int do_dele(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2654 {
2655   char path[WZD_MAX_PATH];
2656   int ret;
2657   fs_filestat_t s;
2658   u64_t file_size;
2659   wzd_user_t * user, * owner;
2660 
2661   if (!str_checklength(param,1,WZD_MAX_PATH-1)) {
2662     ret = send_message_with_args(501,context,"Syntax error");
2663     return E_PARAM_INVALID;
2664   }
2665 
2666   user = GetUserByID(context->userid);
2667   if (!user) {
2668     ret = send_message_with_args(501,context,"Mama says I don't exist!");
2669     return E_USER_IDONTEXIST;
2670   }
2671 
2672   if ( !(user->userperms & RIGHT_DELE) ) {
2673     ret = send_message_with_args(501,context,"Permission denied");
2674     return E_NOPERM;
2675   }
2676 
2677   if (checkpath_new(str_tochar(param),path,context)) {
2678     ret = send_message_with_args(501,context,"Permission denied or inexistant file");
2679     return E_FILE_NOEXIST;
2680   }
2681 
2682   if (path[strlen(path)-1]=='/') path[strlen(path)-1]='\0';
2683 
2684   /* deny retrieve to permissions file */
2685   if (is_hidden_file(path)) {
2686     ret = send_message_with_args(501,context,"Forbidden");
2687     return E_FILE_FORBIDDEN;
2688   }
2689 
2690   if (fs_file_lstat(path,&s)) {
2691     /* non-existent file ? */
2692     ret = send_message_with_args(501,context,"File does not exist");
2693     return E_FILE_NOEXIST;
2694   }
2695   if (S_ISDIR(s.mode)) {
2696     ret = send_message_with_args(501,context,"This is a directory!");
2697     return E_ISDIR;
2698   }
2699   if (S_ISREG(s.mode))
2700     file_size = s.size;
2701   else
2702     file_size = 0;
2703   owner = file_getowner(path,context);
2704 
2705   context->current_action.token = TOK_DELE;
2706   out_err(LEVEL_FLOOD,"Removing file '%s'\n",path);
2707 
2708   ret = file_remove(path,context);
2709 
2710   /* decrement user credits and upload stats */
2711   /* we should adjust stats for REAL OWNER of file */
2712   if (!ret && file_size)
2713   {
2714     if (owner) {
2715       if (strcmp(owner->username,"nobody"))
2716       {
2717         if (owner->ratio) {
2718           if (owner->credits > owner->ratio*file_size)
2719             owner->credits -= (owner->ratio * file_size);
2720           else
2721             owner->credits = 0;
2722         }
2723         if (owner->stats.bytes_ul_total > file_size)
2724           owner->stats.bytes_ul_total -= file_size;
2725         else
2726           owner->stats.bytes_ul_total = 0;
2727         if (owner->stats.files_ul_total)
2728           owner->stats.files_ul_total--;
2729       }
2730     }
2731   }
2732 
2733   if (!ret) {
2734     {
2735       wzd_string_t * event_args = STR(path);
2736       event_send(mainConfig->event_mgr, EVENT_DELE, 250, event_args, context);
2737       str_deallocate(event_args);
2738     }
2739     ret = send_message_with_args(250,context,"DELE"," command successful");
2740 
2741     context->idle_time_start = time(NULL);
2742   } else
2743     ret = send_message_with_args(501,context,"DELE failed");
2744 
2745   context->current_action.token = TOK_UNKNOWN;
2746   return ret;
2747 }
2748 
2749 /*************** do_pret *****************************/
do_pret(UNUSED wzd_string_t * name,UNUSED wzd_string_t * param,wzd_context_t * context)2750 int do_pret(UNUSED wzd_string_t *name, UNUSED wzd_string_t *param, wzd_context_t * context)
2751 {
2752   int ret;
2753 
2754   /* TODO XXX FIXME PRET *MUST* be sent before the PASV command */
2755 
2756   /* TODO check next token (RETR STOR STOU LIST NLST APPE) and
2757    * run specific commands ...
2758    */
2759   /* e.g: if RETR, open file to have it in cache ? */
2760 
2761   ret = send_message_with_args(200,context,"Command okay");
2762   return E_OK;
2763 }
2764 
2765 /*************** do_print_message ********************/
do_print_message(UNUSED wzd_string_t * name,UNUSED wzd_string_t * filename,wzd_context_t * context)2766 int do_print_message(UNUSED wzd_string_t *name, UNUSED wzd_string_t *filename, wzd_context_t * context)
2767 {
2768   int cmd;
2769   int ret;
2770   char buffer[WZD_BUFFER_LEN];
2771   wzd_string_t * str;
2772 
2773   cmd = identify_token(str_tochar(name));
2774   switch (cmd) {
2775     case TOK_PWD:
2776       context->resume = 0;
2777       /** \todo allow msg 257 customization */
2778       /*ret = send_message(257,context);*/
2779       str = str_allocate();
2780       str_sprintf(str,"257 \"%s\" is current directory.\r\n",context->currentpath);
2781 #ifdef HAVE_UTF8
2782       if (context->connection_flags & CONNECTION_UTF8)
2783       {
2784         if (!str_is_valid_utf8(str))
2785           str_local_to_utf8(str,local_charset());
2786       }
2787 #endif
2788       ret = send_message_raw(str_tochar(str),context);
2789       str_deallocate(str);
2790       break;
2791     case TOK_ALLO:
2792     case TOK_NOOP:
2793       ret = send_message_with_args(200,context,"Command okay");
2794       break;
2795     case TOK_FEAT:
2796       snprintf(buffer,sizeof(buffer),"Extensions supported:\n%s",SUPPORTED_FEATURES);
2797       ret = send_message_with_args(211,context,buffer);
2798       break;
2799     case TOK_SYST:
2800       context->resume = 0;
2801       ret = send_message(215,context);
2802       break;
2803   }
2804   return E_OK;
2805 }
2806 
2807 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
2808 /*************** do_pbsz *****************************/
do_pbsz(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2809 int do_pbsz(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2810 {
2811   int ret;
2812   const char *arg;
2813 
2814   arg = str_tochar(param);
2815   /** \todo TOK_BSZ: if user is NOT in TLS mode, insult him */
2816   /** \todo TOK_BSZ: use argument */
2817   ret = send_message_with_args(200,context,"PBSZ command okay");
2818   return E_OK;
2819 }
2820 #else
do_pbsz(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2821 int do_pbsz(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2822 {
2823   send_message(502,context);
2824   return E_PARAM_INVALID;
2825 }
2826 #endif
2827 
2828 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
2829 /*************** do_prot *****************************/
do_prot(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2830 int do_prot(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2831 {
2832   int ret;
2833   const char *arg;
2834 
2835   arg = str_tochar(param);
2836   /** \todo TOK_PROT: if user is NOT in TLS mode, insult him */
2837   if (strcasecmp("P",arg)==0)
2838     context->tls_data_mode = TLS_PRIV;
2839   else if (strcasecmp("C",arg)==0)
2840     context->tls_data_mode = TLS_CLEAR;
2841   else {
2842     ret = send_message_with_args(550,context,"PROT","must be C or P");
2843     return E_PARAM_INVALID;
2844   }
2845   ret = send_message_with_args(200,context,"PROT command okay");
2846   return E_OK;
2847 }
2848 #else
do_prot(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2849 int do_prot(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2850 {
2851   send_message(502,context);
2852   return E_PARAM_INVALID;
2853 }
2854 #endif
2855 
2856 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
2857 /*************** do_sscn *****************************/
do_sscn(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2858 int do_sscn(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2859 {
2860   int ret;
2861   const char *arg;
2862 
2863   arg = str_tochar(param);
2864   if (!arg || strlen(arg)==0 || strcasecmp(arg,"off")==0) {
2865     context->tls_role = TLS_SERVER_MODE;
2866     ret = send_message_with_args(200,context,"SSCN:SERVER METHOD");
2867     return E_OK;
2868   }
2869   if (strcasecmp(arg,"on")==0) {
2870     context->tls_role = TLS_CLIENT_MODE;
2871     ret = send_message_with_args(200,context,"SSCN:CLIENT METHOD");
2872     return E_OK;
2873   }
2874 
2875   ret = send_message_with_args(550,context,"SSCN","Invalid argument");
2876   return E_PARAM_INVALID;
2877 }
2878 #else
do_sscn(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2879 int do_sscn(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2880 {
2881   return E_PARAM_INVALID;
2882 }
2883 #endif
2884 
2885 /*************** do_quit *****************************/
do_quit(UNUSED wzd_string_t * name,UNUSED wzd_string_t * arg,wzd_context_t * context)2886 int do_quit(UNUSED wzd_string_t *name, UNUSED wzd_string_t *arg, wzd_context_t * context)
2887 {
2888   int ret;
2889 
2890   ret = send_message(221,context);
2891   {
2892     const char * groupname = NULL;
2893     wzd_user_t * user;
2894     const char * remote_host;
2895     struct hostent *h;
2896     char inet_str[256];
2897     int af = (context->family == WZD_INET6) ? AF_INET6 : AF_INET;
2898 
2899     user = GetUserByID(context->userid);
2900 
2901     if (user->group_num > 0) groupname = GetGroupByID(user->groups[0])->groupname;
2902     inet_str[0] = '\0';
2903     inet_ntop(af,context->hostip,inet_str,sizeof(inet_str));
2904     h = gethostbyaddr((char*)&context->hostip,sizeof(context->hostip),af);
2905     if (h==NULL)
2906       remote_host = inet_str;
2907     else
2908       remote_host = h->h_name;
2909     log_message("LOGOUT","%s (%s) \"%s\" \"%s\" \"%s\"",
2910         remote_host,
2911         inet_str,
2912         user->username,
2913         (groupname)?groupname:"No Group",
2914         user->tagline
2915         );
2916   }
2917   context->exitclient=1;
2918   /* check if pending xfers */
2919 
2920   return E_OK;
2921 }
2922 
2923 /*************** do_rest *****************************/
do_rest(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2924 int do_rest(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2925 {
2926   int ret;
2927   u64_t ull;
2928   char *ptr;
2929 
2930   if (!arg) {
2931     ret = send_message_with_args(501,context,"Invalid REST marker");
2932     return E_PARAM_INVALID;
2933   }
2934   ull = strtoull(str_tochar(arg), &ptr, 0);
2935   if (ptr==str_tochar(arg) || *ptr!='\0')
2936   {
2937     ret = send_message_with_args(501,context,"Invalid REST marker");
2938     return E_PARAM_INVALID;
2939   } else {
2940     char buf[256];
2941     snprintf(buf,256,"Restarting at %" PRIu64 ". Send STORE or RETRIEVE.",ull);
2942     ret = send_message_with_args(350,context,buf);
2943     context->resume = ull;
2944   }
2945   return E_OK;
2946 }
2947 
2948 /*************** do_rnfr *****************************/
do_rnfr(UNUSED wzd_string_t * name,wzd_string_t * filename,wzd_context_t * context)2949 int do_rnfr(UNUSED wzd_string_t *name, wzd_string_t *filename, wzd_context_t * context)
2950 {
2951   char path[WZD_MAX_PATH];
2952   int ret;
2953   wzd_user_t * user;
2954 
2955   user = GetUserByID(context->userid);
2956 
2957   if (!user || !(user->userperms & RIGHT_RNFR)) {
2958     ret = send_message_with_args(550,context,"RNFR","permission denied");
2959     return E_FILE_NOEXIST;
2960   }
2961 
2962 
2963   if (!filename || strlen(str_tochar(filename))==0 || strlen(str_tochar(filename))>=WZD_MAX_PATH || checkpath_new(str_tochar(filename),path,context)) {
2964     ret = send_message_with_args(550,context,"RNFR","file does not exist");
2965     return E_FILE_NOEXIST;
2966   }
2967 
2968   if (path[strlen(path)-1]=='/') path[strlen(path)-1]='\0';
2969 
2970   /* deny retrieve to permissions file */
2971   if (is_hidden_file(path)) {
2972     ret = send_message_with_args(501,context,"Forbidden");
2973     return E_FILE_FORBIDDEN;
2974   }
2975 
2976   context->current_action.token = TOK_RNFR;
2977   strncpy(context->current_action.arg,path,HARD_LAST_COMMAND_LENGTH);
2978   context->current_action.current_file = -1;
2979   context->current_action.bytesnow = 0;
2980   context->current_action.tm_start = time(NULL);
2981 
2982   ret = send_message_with_args(350,context,"OK, send RNTO");
2983   return E_OK;
2984 }
2985 
2986 /*************** do_rnto *****************************/
do_rnto(UNUSED wzd_string_t * name,wzd_string_t * filename,wzd_context_t * context)2987 int do_rnto(UNUSED wzd_string_t *name, wzd_string_t *filename, wzd_context_t * context)
2988 {
2989   char path[WZD_MAX_PATH];
2990   int ret;
2991   wzd_user_t * user;
2992 
2993   user = GetUserByID(context->userid);
2994 
2995   if (!user || !(user->userperms & RIGHT_RNFR)) {
2996     ret = send_message_with_args(550,context,"RNTO","permission denied");
2997     return E_FILE_NOEXIST;
2998   }
2999 
3000 
3001   if (!filename || strlen(str_tochar(filename))==0 || strlen(str_tochar(filename))>=WZD_MAX_PATH) {
3002     ret = send_message_with_args(553,context,"RNTO","wrong file name?");
3003     return E_PARAM_INVALID;
3004   }
3005   if (context->current_action.token != TOK_RNFR) {
3006     ret = send_message_with_args(553,context,"RNTO","send RNFR before!");
3007     return E_PARAM_INVALID;
3008   }
3009 
3010   checkpath_new(str_tochar(filename),path,context);
3011   if (path[strlen(path)-1]=='/') path[strlen(path)-1]='\0';
3012 
3013   /* deny retrieve to permissions file */
3014   if (is_hidden_file(path)) {
3015     ret = send_message_with_args(501,context,"Forbidden");
3016     return E_FILE_FORBIDDEN;
3017   }
3018   context->current_action.token = TOK_UNKNOWN;
3019   context->current_action.current_file = -1;
3020   context->current_action.bytesnow = 0;
3021 
3022   ret = file_rename(context->current_action.arg,path,context);
3023   if (ret) {
3024     ret = send_message_with_args(550,context,"RNTO","command failed");
3025   } else {
3026     ret = send_message_with_args(250,context,"RNTO"," command okay");
3027     context->idle_time_start = time(NULL);
3028   }
3029   return E_OK;
3030 }
3031 
3032 /*************** do_type *****************************/
do_type(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)3033 int do_type(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
3034 {
3035   int ret;
3036 
3037   context->resume = 0;
3038   if (!param) {
3039     ret = send_message_with_args(501,context,"Invalid TYPE marker");
3040     return E_PARAM_INVALID;
3041   }
3042   if (strcasecmp(str_tochar(param),"I")==0)
3043     context->current_xfer_type = BINARY;
3044   else if (strcasecmp(str_tochar(param),"A")==0)
3045     context->current_xfer_type = ASCII;
3046   else {
3047     ret = send_message(502,context);
3048     return E_PARAM_INVALID;
3049   }
3050   ret = send_message_with_args(200,context,"Command okay");
3051   return E_OK;
3052 }
3053 
3054 /*************** do_xcrc *****************************/
do_xcrc(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)3055 int do_xcrc(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
3056 {
3057   char path[WZD_MAX_PATH];
3058   char buffer[1024];
3059   const char * ptr;
3060   char * ptest;
3061   fs_filestat_t s;
3062   int ret;
3063   unsigned long crc = 0;
3064   unsigned long startpos = 0;
3065   unsigned long length = (unsigned long)-1;
3066   const char *param;
3067 
3068   if (!str_checklength(arg,1,WZD_MAX_PATH-1)) {
3069     ret = send_message_with_args(501,context,"Syntax error");
3070     return E_PARAM_INVALID;
3071   }
3072   param = str_tochar(arg);
3073 
3074   /* get filename and args:
3075    * "filename" must be quoted
3076    * startpos and length are optional
3077    */
3078   ptr = param;
3079   if (*ptr == '"') {
3080     ptr++;
3081     while (*ptr && *ptr != '"') ptr++;
3082     if (!*ptr) {
3083       ret = send_message_with_args(501,context,"Syntax error");
3084       return E_PARAM_INVALID;
3085     }
3086     memcpy(buffer,param+1,ptr-param-1);
3087     buffer[ptr-param-1] = '\0';
3088     ptr++;
3089     /* optional: read startpos AND length */
3090     startpos = strtoul(ptr,&ptest,0);
3091     if (ptest && ptest != ptr)
3092     {
3093       ptr = ptest;
3094       length = strtoul(ptr,&ptest,0);
3095       if (!ptest || ptest == ptr) {
3096         ret = send_message_with_args(501,context,"Syntax error");
3097         return E_PARAM_INVALID;
3098       } else { /* optional: read start checksum */
3099         ptr = ptest;
3100         crc = strtoul(ptr,&ptest,16);
3101         if (!ptest || ptest == ptr)
3102           crc = 0;
3103       }
3104     } else
3105       startpos = 0;
3106     param = buffer;
3107   }
3108 
3109   if (!checkpath_new(param,path,context)) {
3110     if (path[strlen(path)-1]=='/')
3111       path[strlen(path)-1]='\0';
3112 
3113   /* deny retrieve to permissions file */
3114     if (is_hidden_file(path)) {
3115       ret = send_message_with_args(501,context,"Forbidden");
3116       return E_FILE_FORBIDDEN;
3117     }
3118 
3119 
3120     if (fs_file_stat(path,&s)==0) {
3121       ret = calc_crc32(path,&crc,startpos,length);
3122       snprintf(buffer,1024,"%lX",crc);
3123 /*      snprintf(buffer,1024,"%d %lX\r\n",250,crc);*/
3124 /*      ret = send_message_raw(buffer,context);*/
3125       ret = send_message_with_args(250,context,buffer,"");
3126       return E_OK;
3127     }
3128   }
3129   ret = send_message_with_args(550,context,"XCRC","File inexistent or no access?");
3130   return E_FILE_NOEXIST;
3131 }
3132 
3133 /*************** do_xmd5 *****************************/
do_xmd5(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)3134 int do_xmd5(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
3135 {
3136   char path[WZD_MAX_PATH];
3137   char buffer[1024];
3138   const char * ptr;
3139   char * ptest;
3140   fs_filestat_t s;
3141   int ret;
3142   unsigned char crc[16];
3143   char md5str[33];
3144   unsigned long startpos = 0;
3145   unsigned long length = (unsigned long)-1;
3146   unsigned int i;
3147   const char *param;
3148 
3149   if (!str_checklength(arg,1,WZD_MAX_PATH-1)) {
3150     ret = send_message_with_args(501,context,"Syntax error");
3151     return E_PARAM_INVALID;
3152   }
3153   param = str_tochar(arg);
3154 
3155   for (i=0; i<16; i++)
3156     crc[i] = 0;
3157 
3158   /* get filename and args:
3159    * "filename" must be quoted
3160    * startpos and length are optional
3161    */
3162   ptr = param;
3163   if (*ptr == '"') {
3164     ptr++;
3165     while (*ptr && *ptr != '"') ptr++;
3166     if (!*ptr) {
3167       ret = send_message_with_args(501,context,"Syntax error");
3168       return E_PARAM_INVALID;
3169     }
3170     memcpy(buffer,param+1,ptr-param-1);
3171     buffer[ptr-param-1] = '\0';
3172     ptr++;
3173     /* optional: read startpos AND length */
3174     startpos = strtoul(ptr,&ptest,0);
3175     if (ptest && ptest != ptr)
3176     {
3177       ptr = ptest;
3178       length = strtoul(ptr,&ptest,0);
3179       if (!ptest || ptest == ptr) {
3180         ret = send_message_with_args(501,context,"Syntax error");
3181         return E_PARAM_INVALID;
3182       } else { /* optional: read start checksum */
3183         ptr = ptest;
3184         strtomd5((char*)ptr,&ptest,crc);
3185         if (!ptest || ptest == ptr)
3186           memset(crc,0,16);
3187       }
3188     } else
3189       startpos = 0;
3190     param = buffer;
3191   }
3192 
3193   if (!checkpath_new(param,path,context)) {
3194     if (path[strlen(path)-1]=='/')
3195       path[strlen(path)-1]='\0';
3196 
3197   /* deny retrieve to permissions file */
3198     if (is_hidden_file(path)) {
3199       ret = send_message_with_args(501,context,"Forbidden");
3200       return E_FILE_FORBIDDEN;
3201     }
3202 
3203 
3204     if (fs_file_stat(path,&s)==0) {
3205       ret = calc_md5(path,crc,startpos,length);
3206       for (i=0; i<16; i++)
3207         snprintf(md5str+i*2,3,"%02x",crc[i]);
3208       ret = send_message_with_args(250,context,md5str,"");
3209       return E_OK;
3210     }
3211   }
3212   ret = send_message_with_args(550,context,"XMD5","File inexistent or no access?");
3213   return E_FILE_NOEXIST;
3214 }
3215 
3216 /*************** do_help *****************************/
do_help(UNUSED wzd_string_t * name,UNUSED wzd_string_t * arg,wzd_context_t * context)3217 int do_help(UNUSED wzd_string_t *name, UNUSED wzd_string_t *arg, wzd_context_t * context)
3218 {
3219   /* TODO maybe add HELP SITE? */
3220   send_message_with_args(214,context);
3221 
3222   return E_OK;
3223 }
3224 
3225 
3226 /*****************************************************/
3227 /*************** client main proc ********************/
3228 /*****************************************************/
3229 /** @brief Client main loop
3230  *
3231  * Calls do_login(context) to handle the login, and then enters the main
3232  * loop.
3233  *
3234  * Each loop consist of checking if the control connection is ready for
3235  * reading, and if data connection is ready for reading/writing. If both
3236  * are ready, the control connection is always handled first.
3237  * Data are handled in the separate function data_execute().
3238  *
3239  * Control data are first translated to current charset if needed, then the
3240  * first token is parsed and sent to commands_find() to identify the command.
3241  *
3242  * The exit is done using client_die().
3243  */
clientThreadProc(void * arg)3244 void * clientThreadProc(void *arg)
3245 {
3246   struct timeval tv;
3247   fd_set fds_r,fds_w,efds;
3248   unsigned long max_wait_time;
3249   wzd_context_t * context;
3250   char *buffer = NULL;
3251   int save_errno;
3252   fd_t sockfd;
3253   int ret;
3254   wzd_user_t * user;
3255   wzd_command_t * command;
3256   wzd_string_t * command_buffer;
3257   struct ftp_command_t * ftp_command;
3258 #ifndef _MSC_VER
3259   int oldtype;
3260 #endif
3261 
3262   context = arg;
3263   sockfd = context->controlfd;
3264   context->last_file.name[0] = '\0';
3265   context->last_file.token = TOK_UNKNOWN;
3266   context->data_buffer = wzd_malloc(mainConfig->data_buffer_length);
3267 
3268 #ifdef WIN32
3269   context->thread_id = GetCurrentThreadId();
3270 #else
3271   context->thread_id = pthread_self();
3272 #endif
3273  _tls_store_context(context);
3274 
3275   out_log(LEVEL_INFO,"Client speaking to socket %d\n",sockfd);
3276 #ifndef WIN32
3277 #ifdef WZD_MULTITHREAD
3278   pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
3279   pthread_cleanup_push((void (*) (void *))client_die, (void *) context);
3280 #endif /* WZD_MULTITHREAD */
3281 #endif
3282 
3283   ret = do_login(context);
3284 
3285   if (ret) {
3286 #if defined (WIN32)
3287     client_die(context);
3288 #else
3289     /* on other platforms, the cleanup function will be executed
3290      * using pthread_cleanup_pop
3291      */
3292     pthread_exit(NULL);
3293 #endif /* WIN32 */
3294     return NULL;
3295   }
3296 
3297   context->state = STATE_COMMAND;
3298 
3299   user = GetUserByID(context->userid);
3300 
3301   /* user+pass ok */
3302   send_message_raw("230- Command okay\r\n",context);
3303   {
3304     wzd_string_t * event_args = STR(user->username);
3305     event_send(mainConfig->event_mgr, EVENT_LOGIN, 230, event_args, context);
3306     str_deallocate(event_args);
3307   }
3308   ret = send_message(230,context);
3309 
3310   /* update last login time */
3311   time(&user->last_login);
3312 
3313   context->control_buffer = buffer = malloc(WZD_BUFFER_LEN);
3314 
3315   /* get value for server tick */
3316   max_wait_time = config_get_integer(mainConfig->cfg_file, "GLOBAL", "client tick", &ret);
3317   if (ret != CF_OK) {
3318     max_wait_time = DEFAULT_CLIENT_TICK;
3319   }
3320 
3321   /* main loop */
3322   context->exitclient=0;
3323   context->idle_time_start = time(NULL);
3324 
3325   user = GetUserByID(context->userid);
3326   while (!context->exitclient) {
3327 #ifdef DEBUG
3328     if (check_context(context) != 0) {
3329       out_log(LEVEL_CRITICAL,"CRITICAL check_context failed\n");
3330       context->exitclient = 1;
3331       break;
3332     }
3333 #endif /* DEBUG */
3334 
3335     /* check for finished transfers
3336      * Remember that this will be checked every max_wait_time seconds
3337      * (default: DEFAULT_CLIENT_TICK = 10), so at this point the transfer
3338      * can be finished while the thread is waiting to be joined
3339      */
3340     if (context->transfer_thread != NULL &&
3341         context->is_transferring == 0) {
3342       void * return_value;
3343 
3344       out_log(LEVEL_FLOOD,"DEBUG waiting for transfer thread\n");
3345 
3346       wzd_thread_join(context->transfer_thread,&return_value);
3347       context->transfer_thread = NULL;
3348 
3349       free(context->transfer_thread);
3350       context->transfer_thread = NULL;
3351     }
3352 
3353     save_errno = 666;
3354     /* 1. read */
3355     FD_ZERO(&fds_r);
3356     FD_ZERO(&fds_w);
3357     FD_ZERO(&efds);
3358     /* set control fd */
3359     FD_SET(sockfd,&fds_r);
3360     FD_SET(sockfd,&efds);
3361     /* set data fd */
3362     if (context->transfer_thread == NULL) {
3363       ret = data_set_fd(context,&fds_r,&fds_w,&efds);
3364     }
3365     if ((signed)sockfd > ret) ret = sockfd;
3366 
3367     tv.tv_sec=max_wait_time; tv.tv_usec=0L;
3368     /* bug in windows implementation of select(): when aborting a data connection,
3369      * next calls to select() always return immediatly, causing wzdftpd
3370      * to use 100% cpu (infinite loop).
3371      * The solution is not to use efds
3372      */
3373 /*    ret = select(ret+1,&fds_r,&fds_w,&efds,&tv);*/
3374     ret = select(ret+1,&fds_r,&fds_w,NULL,&tv);
3375     FD_ZERO(&efds);
3376     save_errno = errno;
3377 
3378     if (ret==-1) {
3379      if (errno == EINTR) continue;
3380       else {
3381         out_log(LEVEL_CRITICAL,"Major error during recv: control fd %d errno %d error %s\n",sockfd,save_errno,strerror(save_errno));
3382         context->exitclient = 1;
3383       }
3384     }
3385     /* TODO XXX FIXME is this empty if() intentional ?? */
3386     if (FD_ISSET(sockfd,&efds)) {
3387 /*      if (save_errno == EINTR) continue;*/
3388 /*      out_log(LEVEL_CRITICAL,"Major error during recv: errno %d error %s\n",save_errno,strerror(save_errno));*/
3389 /*out_err(LEVEL_CRITICAL,"ret %d sockfd: %d %d datafd %d %d\n",ret,sockfd,FD_ISSET(sockfd,&efds),context->datafd,FD_ISSET(context->datafd,&efds));
3390 out_err(LEVEL_CRITICAL,"read %d %d write %d %d error %d %d\n",FD_ISSET(sockfd,&fds_r),FD_ISSET(context->datafd,&fds_r),
3391     FD_ISSET(sockfd,&fds_w),FD_ISSET(context->datafd,&fds_w),
3392     FD_ISSET(sockfd,&efds),FD_ISSET(context->datafd,&efds));*/
3393 /*      continue;*/
3394     }
3395     if (context->transfer_thread == NULL) {
3396       ret = data_check_fd(context,&fds_r,&fds_w,&efds);
3397     }
3398     if (ret == -1) {
3399       /* we had an error reading data connection */
3400       /** \todo should be remove data descriptors and so ? */
3401     }
3402 
3403     if (!FD_ISSET(sockfd,&fds_r)) {
3404       /* we check for data iff control is not set - control is prior */
3405       if (ret==1) {
3406         if (context->current_action.token == TOK_UNKNOWN) {
3407           /* we are receiving / sending data without RETR/STOR */
3408           continue;
3409         }
3410         /* we have data ready */
3411         ret = data_execute(context,user,&fds_r,&fds_w);
3412         continue;
3413       }
3414       /* nothing to read */
3415       /* XXX CHECK FOR TIMEOUT: control & data if needed */
3416       /* check timeout */
3417       if (check_timeout(context)) break;
3418       continue;
3419     }
3420     ret = (context->read_fct)(sockfd,buffer,WZD_BUFFER_LEN-1,0,0,context); /* timeout = 0, we know there's something to read */
3421 
3422     /* remote host has closed session */
3423     if (ret==0 || ret==-1) {
3424       out_log(LEVEL_FLOOD,"Host disconnected improperly!\n");
3425       context->exitclient=1;
3426       break;
3427     }
3428 
3429     /* this replace the memset (bzero ?) some lines before */
3430     buffer[ret] = '\0';
3431 
3432     cleanup_ftp_command(buffer,ret);
3433 
3434     if (buffer[0]=='\0') continue;
3435 
3436     command_buffer = STR(buffer);
3437 
3438     str_trim_right(command_buffer);
3439 
3440     set_action(context,str_tochar(command_buffer));
3441 
3442 /*    context->idle_time_start = time(NULL);*/
3443 #ifdef DEBUG
3444 out_err(LEVEL_FLOOD,"<thread %ld> <- '%s'\n",(unsigned long)context->pid_child,str_tochar(command_buffer));
3445 #endif
3446 
3447     /* reset current reply */
3448     reply_clear(context);
3449 
3450     /* parse and identify command */
3451     ftp_command = parse_ftp_command(command_buffer);
3452 
3453     if (ftp_command != NULL) {
3454       command = ftp_command->command;
3455 
3456       /** For FTP commands, the default permission (if not specified)
3457        * is to ALLOW users to use command, unless restricted !
3458        */
3459       if (command->perms && commands_check_permission(command,context)) {
3460         ret = send_message_with_args(501,context,"Permission Denied");
3461         free_ftp_command(ftp_command);
3462         continue;
3463       }
3464 
3465       if (command->command)
3466         ret = (*(command->command))(ftp_command->command_name,ftp_command->args,context);
3467       else { /* external command */
3468         char buffer_command[4096];
3469         wzd_group_t * group = NULL;
3470 
3471         if (user->group_num > 0) group = GetGroupByID(user->groups[0]);
3472         cookie_parse_buffer(str_tochar(command->external_command), user, group, context, buffer_command, sizeof(buffer_command));
3473         chop(buffer_command);
3474 
3475         /* add arguments given on CLI to event */
3476         if (str_length(ftp_command->args)>0) {
3477           strlcat(buffer_command, " ", sizeof(buffer_command));
3478           strlcat(buffer_command, str_tochar(ftp_command->args), sizeof(buffer_command));
3479         }
3480 
3481         ret = event_exec(buffer_command,context);
3482       }
3483 
3484       /** \todo When all functions use reply_push, test reply and send error if -1 */
3485       ret = reply_send(context);
3486     } else { /* no command found */
3487       ret = send_message(502,context);
3488       str_deallocate(command_buffer);
3489     }
3490     free_ftp_command(ftp_command);
3491 
3492   } /* while (!exitclient) */
3493 
3494 
3495 #ifdef WZD_MULTITHREAD
3496 #ifndef WIN32
3497   pthread_cleanup_pop(1); /* 1 means the cleanup fct is executed !*/
3498 #else
3499   client_die(context);
3500 #endif /* _MSC_VER */
3501 #else /* WZD_MULTITHREAD */
3502   client_die(context);
3503 #endif /* WZD_MULTITHREAD */
3504 
3505   return NULL;
3506 }
3507 
3508 /** \brief Test if remote address is different from the connected client (i.e,
3509  * if client is trying to transfer files to use site-to-site transfer)
3510  *
3511  * \return 1 if transfer is FXP
3512  */
test_fxp(const char * remote_ip,net_family_t family,wzd_context_t * context)3513 static int test_fxp(const char * remote_ip, net_family_t family, wzd_context_t * context)
3514 {
3515   switch (family) {
3516     case WZD_INET4:
3517       return (memcmp(remote_ip,context->hostip,4) != 0);
3518     case WZD_INET6:
3519       return (memcmp(remote_ip,context->hostip,16) != 0);
3520     default:
3521       out_log(LEVEL_HIGH,"ERROR test_fxp called with invalid family\n");
3522   };
3523   return -1;
3524 }
3525 
fxp_is_denied(wzd_user_t * user)3526 static int fxp_is_denied(wzd_user_t * user)
3527 {
3528   return (strchr(user->flags,FLAG_FXP_DISABLE) != NULL);
3529 }
3530 
3531 /** \brief Store context in Thread Local Store (TLS)
3532  *
3533  * \param[in] context Client context
3534  * \return 0 if ok
3535  */
_tls_store_context(wzd_context_t * context)3536 int _tls_store_context(wzd_context_t * context)
3537 {
3538   if (_key_context == NULL) {
3539     _key_context = wzd_tls_allocate();
3540     if (_key_context == NULL) return -1;
3541   }
3542 
3543   if (wzd_tls_setspecific(_key_context, context) != 0) {
3544     out_log(LEVEL_HIGH,"ERROR Could not store context in TLS\n");
3545     wzd_tls_free(_key_context);
3546     _key_context = NULL;
3547     return -1;
3548   }
3549   return 0;
3550 }
3551 
3552 /** \brief Get current context from Thread Local Storage (TLS)
3553  *
3554  * \return
3555  * - a valid wzd_context_t structure if found
3556  * - NULL if the value was not found in TLS
3557  */
_tls_get_context(void)3558 wzd_context_t * _tls_get_context(void)
3559 {
3560   if (_key_context != NULL)
3561     return wzd_tls_getspecific(_key_context);
3562 
3563   return NULL;
3564 }
3565 
3566 /** \brief Remove current context from Thread Local Storage (TLS)
3567  *
3568  * This is used by threads to release properly TLS resources used
3569  * to store context pointer.
3570  *
3571  * \return 0 if ok
3572  */
_tls_remove_context(void)3573 int _tls_remove_context(void)
3574 {
3575   if (_key_context != NULL)
3576     return wzd_tls_remove(_key_context);
3577 
3578   return 0;
3579 }
3580 
3581 
3582 
3583