1 /* vi:ai:et:ts=8 sw=2
2  */
3 /*
4  * wzdftpd - a modular and cool ftp server
5  * Copyright (C) 2002-2004  Pierre Chifflier
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exemption, Pierre Chifflier
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 
27 #include "wzd_all.h"
28 
29 #ifndef WZD_USE_PCH
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 
38 #ifdef WIN32
39 #include <winsock2.h>
40 #include <process.h> /* _getpid() */
41 #include <direct.h> /* _rmdir() */
42 #include <sys/utime.h>
43 #else
44 #include <unistd.h>
45 #include <sys/resource.h>
46 
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
51 
52 #include <utime.h>
53 
54 #include <dirent.h> /* opendir, readdir, closedir */
55 #endif
56 
57 #include <errno.h>
58 #include <signal.h>
59 #include <fcntl.h>
60 
61 #include "wzd_structs.h"
62 
63 #include "wzd_commands.h"
64 #include "wzd_log.h"
65 #include "wzd_misc.h"
66 #include "wzd_messages.h"
67 #include "wzd_site.h"
68 #include "wzd_site_group.h"
69 #include "wzd_site_user.h"
70 #include "wzd_vars.h"
71 #include "wzd_vfs.h"
72 #include "wzd_cache.h"
73 #include "wzd_configfile.h"
74 #include "wzd_events.h"
75 #include "wzd_file.h"
76 #include "wzd_fs.h"
77 #include "wzd_group.h"
78 #include "wzd_dir.h"
79 #include "wzd_mod.h"
80 #include "wzd_perm.h"
81 #include "wzd_tls.h"
82 #include "wzd_user.h"
83 
84 #include <libwzd-auth/wzd_tls.h> /* XXX test only */
85 
86 #include "wzd_debug.h"
87 
88 #else /* WZD_USE_PCH */
89 
90 #ifdef WIN32
91 # include <sys/utime.h>
92 #endif
93 
94 #endif /* WZD_USE_PCH */
95 
96 #include "wzd_version.h"
97 
98 extern int serverstop;
99 extern time_t server_start;
100 
101 #define	BUFFER_LEN	4096
102 
103 void do_site_print_file_raw(const char *filename, wzd_context_t *context);
104 
105 /********************* do_site_test ************************/
106 
do_site_test(wzd_string_t * command,wzd_string_t * param,wzd_context_t * context)107 int do_site_test(wzd_string_t *command, wzd_string_t *param, wzd_context_t * context)
108 {
109   int ret;
110 
111 /*  backend_commit_changes();*/
112 /*if (context->userinfo.flags)
113   out_err(LEVEL_CRITICAL,"FLAGS '%s'\n",context->userinfo.flags);*/
114 #if 0
115   {
116     wzd_sfv_file sfv;
117     char buffer[BUFFER_LEN];
118     /* convert file to absolute path, remember _setPerm wants ABSOLUTE paths ! */
119     if ( (ret = checkpath(command,buffer,context)) == 0 ) {
120       buffer[strlen(buffer)-1] = '\0'; /* remove '/', appended by checkpath */
121       sfv_init(&sfv);
122       ret = sfv_read(buffer,&sfv);
123       sfv_free(&sfv);
124       ret = sfv_check(buffer);
125     }
126   }
127 #endif
128   /* prints some stats */
129   out_err(LEVEL_INFO,"# Connections: %ld\n",mainConfig->stats.num_connections);
130   out_err(LEVEL_INFO,"# Childs     : %ld\n",mainConfig->stats.num_childs);
131   ret = 0;
132 
133   fd_dump();
134 
135 #if 0
136   {
137     char buffer[WZD_MAX_PATH+1];
138     ret = checkpath_new(command, buffer, context);
139     if (!ret)
140       out_err(LEVEL_INFO,"[%s] => [%s]\n",command,buffer);
141     else
142       out_err(LEVEL_INFO,"[%s] : error %d\n",command,ret);
143   }
144 #endif
145 
146 /*  ret = module_unload(&mainConfig->module,command);*/
147 
148 /*  libtest(); ret = 0; */
149 
150 #if 0
151   {
152     wzd_user_t *me = GetUserByID(context->userid);
153     ret = check_certificate(me->username,me->userpass);
154   }
155 #endif
156 
157 #if 0
158   {
159     fs_dir_t * dir;
160     char buffer[WZD_MAX_PATH+1];
161     fs_fileinfo_t * finfo;
162 
163     ret = checkpath_new(context->currentpath, buffer, context);
164 
165     ret = fs_dir_open(buffer,&dir);
166 
167     while (!ret) {
168       ret = fs_dir_read(dir,&finfo);
169     }
170 
171     ret = fs_dir_close(dir);
172 
173     ret = 0;
174   }
175 #endif
176 
177 #ifdef HAVE_GNUTLS
178   ret = tls_dh_params_regenerate();
179 #endif
180 
181   out_err(LEVEL_CRITICAL,"Ret: %d\n",ret);
182 
183 /*
184   ret = send_message_with_args(200,context,"TEST command ok");
185   */
186 
187   reply_push(context,"TEST command ok");
188   reply_set_code(context,200);
189   reply_send(context);
190 
191   return 0;
192 }
193 
194 /** Display generic help (controlled by the "help_file" config variable).
195  *
196  * If an argument is provided, the specific help function for this command is called. The help
197  * function must have been registered when calling \ref commands_add
198  */
do_site_help_command(wzd_string_t * command,wzd_string_t * command_line,wzd_context_t * context)199 int do_site_help_command(wzd_string_t *command, wzd_string_t *command_line, wzd_context_t * context)
200 {
201   wzd_string_t *argument, *str;
202   wzd_command_t * c;
203 
204   argument = str_tok(command_line," \t\r\n");
205 
206   if (argument) {
207     str_prepend(argument,"site_");
208     c = commands_find(mainConfig->commands_list,argument);
209     if (c == NULL) {
210       reply_push(context,"command does not exist");
211       reply_set_code(context,501);
212       return 0;
213     }
214 
215     if (c->help_function == NULL) {
216       reply_push(context,"command does not provide help");
217       reply_set_code(context,200);
218       return 0;
219     }
220 
221     return (c->help_function)(command,command_line,context);
222   }
223 
224   /* generic help */
225   str = config_get_string(mainConfig->cfg_file,"GLOBAL","help_file",NULL);
226   if (str != NULL) {
227     do_site_print_file_raw(str_tochar(str),context);
228     str_deallocate(str);
229     return 0;
230   }
231 
232   reply_push(context,"command ok");
233   reply_set_code(context,200);
234 
235   return 0;
236 }
237 
238 /********************* do_site_help ************************/
239 
do_site_help(const char * site_command,wzd_context_t * context)240 void do_site_help(const char *site_command, wzd_context_t * context)
241 {
242   char buffer[BUFFER_LEN];
243 
244   send_message_raw("501-\r\n",context);
245   if (strcasecmp(site_command,"backend")==0) {
246     send_message_raw("operations on backend\r\n",context);
247     send_message_raw("site backend command backend_name\r\n",context);
248     send_message_raw("command can be one of:\r\n",context);
249     send_message_raw(" close   (unloads backend)\r\n",context);
250     send_message_raw(" commit  (commits changes synchronously)\r\n",context);
251     send_message_raw(" init    (loads new backend)\r\n",context);
252     send_message_raw(" reload  (close and init)\r\n",context);
253     send_message_raw("\r\n",context);
254     send_message_raw("e.g: site backend commit plaintext\r\n",context);
255     send_message_raw("\r\n",context);
256     send_message_raw(" THIS IS A DANGEROUS COMMAND\r\n",context);
257   } else
258   if (strcasecmp(site_command,"checkperm")==0) {
259     send_message_raw("checks access for a user on a file/dir\r\n",context);
260     send_message_raw("site checkperm user file rights\r\n",context);
261     send_message_raw(" rights can be one of:\r\n",context);
262     send_message_raw(" RIGHT_LIST\r\n",context);
263     send_message_raw(" RIGHT_CWD\r\n",context);
264     send_message_raw(" RIGHT_RETR\r\n",context);
265     send_message_raw(" RIGHT_STOR\r\n",context);
266     send_message_raw(" RIGHT_RNFR\r\n",context);
267     send_message_raw("e.g: site checkperm toto dir RIGHT_CWD\r\n",context);
268   } else
269   if (strcasecmp(site_command,"chgrp")==0) {
270     send_message_raw("change the group of a file or directory\r\n",context);
271     send_message_raw("usage: site chgrp group file1 [file2 ...]\r\n",context);
272     send_message_raw("e.g: site chgrp admin file1\r\n",context);
273   } else
274   if (strcasecmp(site_command,"chmod")==0) {
275     send_message_raw("change permissions of a file or directory\r\n",context);
276     send_message_raw("usage: site chmod mode file1 [file2 ...]\r\n",context);
277     send_message_raw("e.g: site chmod 644 file1\r\n",context);
278   } else
279   if (strcasecmp(site_command,"chown")==0) {
280     send_message_raw("change the owner of a file or directory\r\n",context);
281     send_message_raw("usage: site chown user file1 [file2 ...]\r\n",context);
282     send_message_raw("e.g: site chown toto file1\r\n",context);
283   } else
284   if (strcasecmp(site_command,"chpass")==0) {
285     send_message_raw("change the password of a user\r\n",context);
286     send_message_raw("site chpass [user] new_pass\r\n",context);
287   } else
288   if (strcasecmp(site_command,"grpkill")==0) {
289     send_message_raw("kill all connected users from a group\r\n",context);
290     send_message_raw("site grpkill groupname\r\n",context);
291   } else
292   if (strcasecmp(site_command,"link")==0) {
293     send_message_raw("create/remove symbolink links\r\n",context);
294     send_message_raw("site link create dir linkname\r\n",context);
295     send_message_raw("site link remove linkname\r\n",context);
296   } else
297   if (strcasecmp(site_command,"msg")==0) {
298     send_message_raw("manage directory messages\r\n",context);
299     send_message_raw("site msg show\r\n",context);
300     send_message_raw("site msg new msg_line\r\n",context);
301     send_message_raw("site msg append msg_line\r\n",context);
302     send_message_raw("site msg convert file\r\n",context);
303     send_message_raw("site msg delete\r\n",context);
304   } else
305   if (strcasecmp(site_command,"perm")==0) {
306     send_message_raw("manage permissions\r\n",context);
307     send_message_raw("site perm show (show all permissions)\r\n",context);
308     send_message_raw("site perm show name (show permissions for commands starting with perm_name)\r\n",context);
309     send_message_raw("site perm add name perms\r\n",context);
310     send_message_raw("site perm change name perms\r\n",context);
311     send_message_raw("site perm remove\r\n",context);
312     send_message_raw("\r\n",context);
313     send_message_raw("ex: site perm add site_newcmd +O\r\n",context);
314   } else
315   if (strcasecmp(site_command,"user")==0) {
316     send_message_raw("show user info\r\n",context);
317     send_message_raw("site user username\r\n",context);
318   } else
319   if (strcasecmp(site_command,"vars")==0) {
320     send_message_raw("access server variables\r\n",context);
321     send_message_raw("site vars get varname\r\n",context);
322   } else
323   if (strcasecmp(site_command,"vars_user")==0) {
324     send_message_raw("access user variables\r\n",context);
325     send_message_raw("site vars_user get user varname\r\n",context);
326   } else
327   if (strcasecmp(site_command,"vars_group")==0) {
328     send_message_raw("access group variables\r\n",context);
329     send_message_raw("site vars_group get group varname\r\n",context);
330   } else
331   {
332     snprintf(buffer,BUFFER_LEN,"Syntax error in command %s\r\n",site_command);
333     send_message_raw(buffer,context);
334   }
335   send_message_raw("501 \r\n",context);
336 }
337 
338 /********************* do_site_backend *********************/
339 /** backend: close / reload / init / commit
340  */
do_site_backend(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)341 int do_site_backend(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
342 {
343   wzd_string_t * command, *name;
344   int ret;
345 
346   command = str_tok(command_line," \t\r\n");
347   if (!command) {
348     do_site_help("backend",context);
349     return 1;
350   }
351   name = str_tok(command_line," \t\r\n");
352   if (!name) {
353     do_site_help("backend",context);
354     str_deallocate(command);
355     return 1;
356   }
357   if (strcasecmp(str_tochar(command),"close")==0) {
358     str_deallocate(command);
359     ret = backend_close(str_tochar(name));
360     if (ret) {
361       ret = send_message_with_args(501,context,"Could not close backend");
362     } else {
363       ret = send_message_with_args(200,context,"Backend close successfully");
364     }
365     str_deallocate(name);
366     return 0;
367   } /* close */
368   if (strcasecmp(str_tochar(command),"init")==0) {
369     str_deallocate(command);
370 #if 0
371     ret = backend_init(str_tochar(name));
372     if (ret) {
373       ret = send_message_with_args(501,context,"Could not init backend");
374     } else {
375       ret = send_message_with_args(200,context,"Backend loaded successfully");
376     }
377 #endif
378     ret = send_message_with_args(501,context,"Not yet implemented");
379     str_deallocate(name);
380     return 0;
381   } /* init */
382   if (strcasecmp(str_tochar(command),"reload")==0) {
383     str_deallocate(command);
384     ret = backend_reload(str_tochar(name));
385     if (ret) {
386       ret = send_message_with_args(501,context,"Could not reload backend ** WARNING you could have NO backend NOW");
387     } else {
388       ret = send_message_with_args(200,context,"Backend reloaded successfully");
389     }
390     str_deallocate(name);
391     return 0;
392   } /* reload */
393   if (strcasecmp(str_tochar(command),"commit")==0) {
394     str_deallocate(command);
395     ret = backend_commit_changes(str_tochar(name));
396     if (ret) {
397       ret = send_message_with_args(501,context,"Could not commit backend");
398     } else {
399       ret = send_message_with_args(200,context,"Backend committed successfully");
400     }
401     str_deallocate(name);
402     return 0;
403   } /* commit */
404   do_site_help("backend",context);
405   str_deallocate(command);
406   str_deallocate(name);
407   return 0;
408 }
409 
410 /********************* do_site_chacl ***********************/
411 /** chacl: user mode file1 [file2 ...]
412  */
413 
do_site_chacl(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)414 int do_site_chacl(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
415 {
416   char buffer[BUFFER_LEN];
417   wzd_string_t * mode, *username, *filename;
418   int ret;
419   wzd_user_t * user;
420   unsigned long long_perms;
421   char str_perms[64];
422   char * endptr;
423 
424   username = str_tok(command_line," \t\r\n");
425   if (!username) {
426     do_site_help("chacl",context);
427     return 1;
428   }
429   /* check that username exists */
430   user = GetUserByName( str_tochar(username) );
431   str_deallocate(username);
432   if ( !user ) {
433     ret = send_message_with_args(501,context,"User does not exist");
434     return 1;
435   }
436   mode = str_tok(command_line," \t\r\n");
437   if (!mode) {
438     do_site_help("chacl",context);
439     return 1;
440   }
441   /* TODO check that mode is ok */
442   if (strlen(str_tochar(mode)) > 15) {
443     do_site_help("chacl",context);
444     str_deallocate(mode);
445     return 1;
446   }
447   long_perms = strtoul(str_tochar(mode),&endptr,8);
448   if (endptr != str_tochar(mode)) {
449     snprintf(str_perms,63,"%c%c%c",
450         (long_perms & 01) ? 'r' : '-',
451         (long_perms & 02) ? 'w' : '-',
452         (long_perms & 04) ? 'x' : '-'
453         );
454   } else
455     strncpy(str_perms,str_tochar(mode),63);
456   str_deallocate(mode);
457 
458   while ( (filename = str_tok(command_line," \t\r\n")) )
459   {
460     /* convert file to absolute path, remember _setPerm wants ABSOLUTE paths ! */
461     if (!checkpath(str_tochar(filename),buffer,context))
462     {
463       _setPerm(buffer,user->username,0,0,str_perms,(unsigned long)-1,context);
464     }
465     str_deallocate(filename);
466   }
467 
468   snprintf(buffer,BUFFER_LEN,"acl successfully set");
469   ret = send_message_with_args(200,context,buffer);
470   return 0;
471 }
472 
473 /********************* do_site_chgrp ***********************/
474 /** chgrp: group file1 [file2 ...]
475  */
476 
do_site_chgrp(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)477 int do_site_chgrp(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
478 {
479   char * buffer;
480   wzd_string_t * groupname, *filename;
481   int ret;
482   wzd_group_t * group;
483 
484   groupname = str_tok(command_line," \t\r\n");
485   if (!groupname) {
486     do_site_help("chgrp",context);
487     return 1;
488   }
489   /* check that groupname exists */
490   group=GetGroupByName(str_tochar(groupname));
491   if ( !group ) {
492     ret = send_message_with_args(501,context,"Group does not exist");
493     str_deallocate(groupname);
494     return 1;
495   }
496 
497   buffer = malloc(WZD_MAX_PATH+1);
498 
499   while ( (filename = str_tok(command_line," \t\r\n")) )
500   {
501     /* convert file to absolute path, remember _setPerm wants ABSOLUTE paths ! */
502     if (!checkpath(str_tochar(filename),buffer,context))
503     {
504       _setPerm(buffer,0,0,str_tochar(groupname),0,(unsigned long)-1,context);
505     }
506     str_deallocate(filename);
507   }
508 
509   snprintf(buffer,WZD_MAX_PATH,"group changed to '%s'",str_tochar(groupname));
510   ret = send_message_with_args(200,context,buffer);
511 
512   free(buffer);
513   str_deallocate(groupname);
514   return 0;
515 }
516 
517 /********************* do_site_chmod ***********************/
518 /** chmod: mode file1 [file2 ...]
519  */
do_site_chmod(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)520 int do_site_chmod(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
521 {
522   char * buffer;
523   char * endptr;
524   const char * mode;
525   wzd_string_t * str_mode, *filename;
526   int ret;
527   unsigned long long_perms;
528 
529   str_mode = str_tok(command_line," \t\r\n");
530   if (!str_mode) {
531     do_site_help("chmod",context);
532     return 1;
533   }
534   mode = str_tochar(str_mode);
535   /* TODO check that mode is ok */
536   if (strlen(mode) > 15) {
537     do_site_help("chmod",context);
538     str_deallocate(str_mode);
539     return 1;
540   }
541   long_perms = strtoul(mode,&endptr,8);
542 
543   if (endptr == mode) {
544     unsigned short error = 0, i;
545     unsigned int mask = 1 << 8;
546     /* try to read perm in text mode ? */
547     long_perms = 0;
548     for (i = 0; i<3; i++) {
549       if (*mode == 'r') { long_perms += mask; }
550       else if (*mode != '-') { error = 1; break; }
551       mask >>= 1; mode++;
552       if (*mode == 'w') { long_perms += mask; }
553       else if (*mode != '-') { error = 1; break; }
554       mask >>= 1; mode++;
555       if (*mode == 'x') { long_perms += mask; }
556       else if (*mode != '-') { error = 1; break; }
557       mask >>= 1; mode++;
558     }
559 
560     if (error) {
561       ret = send_message_with_args(501,context,"Invalid permission");
562       str_deallocate(str_mode);
563       return 0;
564     }
565   }
566   str_deallocate(str_mode);
567 
568   buffer = malloc(WZD_MAX_PATH+1);
569 
570   while ( (filename = str_tok(command_line," \t\r\n")) )
571   {
572     /* convert file to absolute path, remember _setPerm wants ABSOLUTE paths ! */
573     if (!checkpath_new(str_tochar(filename),buffer,context)) {
574       _setPerm(buffer,0,0,0,0,long_perms,context);
575     }
576     str_deallocate(filename);
577   }
578 
579   snprintf(buffer,WZD_MAX_PATH,"mode changed to '%lo'",long_perms);
580   ret = send_message_with_args(200,context,buffer);
581 
582   free(buffer);
583   return 0;
584 }
585 
586 /********************* do_site_chown ***********************/
587 /** chown: user file1 [file2 ...]
588  */
589 
do_site_chown(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)590 int do_site_chown(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
591 {
592   char * buffer;
593   wzd_string_t * username, *filename;
594   int ret;
595   wzd_user_t  *user;
596 
597   username = str_tok(command_line," \t\r\n");
598   if (!username) {
599     do_site_help("chown",context);
600     str_deallocate(username);
601     return 1;
602   }
603   /* check that username exists */
604   user = GetUserByName(str_tochar(username));
605   if ( !user ) {
606     ret = send_message_with_args(501,context,"User does not exist");
607     str_deallocate(username);
608     return 1;
609   }
610 
611   buffer = malloc(WZD_MAX_PATH+1);
612 
613   while ( (filename = str_tok(command_line," \t\r\n")) )
614   {
615     /* convert file to absolute path, remember _setPerm wants ABSOLUTE paths ! */
616     if (!checkpath_new(str_tochar(filename),buffer,context))
617     {
618       _setPerm(buffer,0,str_tochar(username),0,0,(unsigned long)-1,context);
619     }
620     str_deallocate(filename);
621   }
622 
623   snprintf(buffer,WZD_MAX_PATH,"owner changed to '%s'",str_tochar(username));
624   ret = send_message_with_args(200,context,buffer);
625 
626   free(buffer);
627   str_deallocate(username);
628   return 0;
629 }
630 
631 /********************* do_site_chpass **********************/
632 /** chpass: [user] new_pass
633  *   siteops can change everyones password
634  *   gadmins can change the groups password
635  *   everyone can change their own password
636  *   noone can change a siteops password except himself
637  */
do_site_chpass(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)638 int do_site_chpass(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
639 {
640   wzd_string_t * username, *new_pass;
641   int ret;
642   wzd_user_t *user, *me;
643   short is_gadmin;
644   unsigned long mod_type;
645 
646   me = GetUserByID(context->userid);
647   is_gadmin = (me->flags && strchr(me->flags,FLAG_GADMIN)) ? 1 : 0;
648 
649   username = str_tok(command_line," \t\r\n");
650   if (!username) {
651     do_site_help("chpass",context);
652     return 1;
653   }
654   new_pass = str_tok(command_line," \t\r\n");
655   if (!new_pass) { /* assume changing own password */
656     new_pass = username;
657     username = NULL;
658     user = me;
659   }
660   else {
661     /* check that username exists */
662     user = GetUserByName(str_tochar(username));
663     str_deallocate(username);
664     username = NULL;
665     if ( !user ) {
666       ret = send_message_with_args(501,context,"User does not exist");
667       str_deallocate(username); str_deallocate(new_pass);
668       return 1;
669     }
670   }
671 
672   /* GAdmin ? */
673   if (is_gadmin)
674   {
675     if (me->group_num==0 || user->group_num==0 || me->groups[0]!=user->groups[0]) {
676       ret = send_message_with_args(501,context,"You can't change this user");
677       str_deallocate(username); str_deallocate(new_pass);
678       return 1;
679     }
680   }
681   else {
682     if ( !(me->flags && strchr(me->flags,FLAG_SITEOP))
683         && me->uid != user->uid )
684     {
685       ret = send_message_with_args(501,context,"You can't change password for other users");
686       str_deallocate(username); str_deallocate(new_pass);
687       return 1;
688     }
689   }
690   if ( (user->flags && strchr(user->flags,FLAG_SITEOP))
691       && me->uid != user->uid )
692   {
693     ret = send_message_with_args(501,context,"You can't change password for a siteop");
694     str_deallocate(username); str_deallocate(new_pass);
695     return 1;
696   }
697 
698   mod_type = _USER_USERPASS;
699   strncpy(user->userpass,str_tochar(new_pass),sizeof(user->userpass));
700   str_deallocate(new_pass);
701 
702   /* commit to backend */
703   ret = backend_mod_user(mainConfig->backends->filename,user->uid,user,mod_type);
704 
705   if (ret)
706     ret = send_message_with_args(501,context,"An error occurred during password change");
707   else
708     ret = send_message_with_args(200,context,"Password changed");
709   return 0;
710 }
711 
712 /********************* do_site_checkperm *******************/
do_site_checkperm(UNUSED wzd_string_t * ignored,wzd_string_t * commandline,wzd_context_t * context)713 int do_site_checkperm(UNUSED wzd_string_t *ignored, wzd_string_t * commandline, wzd_context_t * context)
714 {
715   unsigned long word;
716   char * buffer;
717   wzd_string_t *username, *filename, *perms;
718   wzd_user_t *user;
719 
720   username = str_tok(commandline," \t\r\n");
721   if (!username) { do_site_help("checkperm",context); return 1; }
722   filename = str_tok(commandline," \t\r\n");
723   if (!filename) {
724     str_deallocate(username);
725     do_site_help("checkperm",context);
726     return 1;
727   }
728   perms = str_tok(commandline,"\r\n");
729   if (!perms) {
730     str_deallocate(username); str_deallocate(filename);
731     do_site_help("checkperm",context);
732     return 1;
733   }
734 
735   word = right_text2word(str_tochar(perms));
736   str_deallocate(perms);
737   if (word == 0) {
738     str_deallocate(username); str_deallocate(filename);
739     send_message_with_args(501,context,"Invalid permission");
740     return 1;
741   }
742 
743   user = GetUserByName(str_tochar(username));
744   str_deallocate(username);
745   if ( !user ) {
746     str_deallocate(filename);
747     send_message_with_args(501,context,"User does not exist");
748     return 1;
749   }
750 
751   buffer = malloc(WZD_MAX_PATH+1);
752 
753   /* convert file to absolute path, remember _setPerm wants ABSOLUTE paths ! */
754   if (checkpath(str_tochar(filename),buffer,context)) {
755     send_message_with_args(501,context,"File does not exist");
756     str_deallocate(filename);
757     free(buffer);
758     return 1;
759   }
760   str_deallocate(filename);
761 
762 /*  buffer[strlen(buffer)-1] = '\0';*/ /* remove '/', appended by checkpath */
763 
764   if (_checkPerm(buffer,word,user)==0) {
765     wzd_strncpy(buffer,"Right okay",WZD_MAX_PATH);
766   } else {
767     wzd_strncpy(buffer,"Refused",WZD_MAX_PATH);
768   }
769 
770   send_message_with_args(200,context,buffer);
771   free(buffer);
772   return 0;
773 }
774 
775 /********************* do_site_free ************************/
776 /** free sectionname
777  */
778 
do_site_free(UNUSED wzd_string_t * ignored,UNUSED wzd_string_t * param,wzd_context_t * context)779 int do_site_free(UNUSED wzd_string_t *ignored, UNUSED wzd_string_t *param, wzd_context_t * context)
780 {
781   char * buffer;
782   int ret;
783 /*  char * ptr;
784   char * sectionname;
785   wzd_user_t user;
786   int uid;
787   wzd_context_t user_context;*/
788   long f_type, f_bsize, f_blocks, f_free;
789   float freeb,totalb;
790   char unit;
791 
792 /*  ptr = command_line;
793   username = strtok_r(command_line," \t\r\n",&ptr);
794   if (!username) {
795     do_site_help("user",context);
796     return;
797   }*/
798 
799   buffer = malloc(WZD_MAX_PATH+1);
800 
801   if (checkpath_new(".",buffer,context)) {
802     send_message_with_args(501,context,". does not exist?!");
803     free(buffer);
804     return -1;
805   }
806 
807   ret = get_device_info(buffer,&f_type, &f_bsize, &f_blocks, &f_free);
808 
809   unit='k';
810   freeb = f_free*(f_bsize/1024.f);
811   totalb = f_blocks*(f_bsize/1024.f);
812 
813   if (totalb > 1000.f) {
814     unit='M';
815     freeb /= 1024.f;
816     totalb /= 1024.f;
817   }
818   if (totalb > 1000.f) {
819     unit='G';
820     freeb /= 1024.f;
821     totalb /= 1024.f;
822   }
823 
824   snprintf(buffer,WZD_MAX_PATH,"[FREE] + [current dir: %.2f / %.2f %c] -",freeb,totalb,unit);
825 
826   ret = send_message_with_args(200,context,buffer);
827 
828   free(buffer);
829   return 0;
830 }
831 
832 /********************* do_site_invite **********************/
833 /** invite: ircnick
834  */
do_site_invite(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)835 int do_site_invite(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
836 {
837   wzd_string_t * ircnick;
838   int ret;
839   wzd_user_t *user;
840   wzd_group_t *group;
841   char buffer[2048], path[2048];
842 
843   ircnick = str_tok(command_line," \t\r\n");
844   if (!ircnick) {
845     do_site_help("invite",context);
846     return 1;
847   }
848   /* TODO check that user is allowed to be invited ? */
849   user = GetUserByID(context->userid);
850   group = GetGroupByID(user->groups[0]);
851 
852   strncpy(buffer,context->currentpath,sizeof(buffer));
853   stripdir(buffer,path,2047);
854 
855   log_message("INVITE","\"%s\" \"%s\" \"%s\" \"%s\"",
856       path, /* ftp-absolute path */
857       user->username,
858       (group->groupname)?group->groupname:"No Group",
859       str_tochar(ircnick));
860 
861   ret = send_message_with_args(200,context,"SITE INVITE command okay");
862   str_deallocate(ircnick);
863   return 0;
864 }
865 
866 /********************* do_site_link ************************/
867 /** link: create dir linkname
868   * link: remove linkname
869   */
870 
do_site_link(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)871 int do_site_link(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
872 {
873   char buffer_dir[BUFFER_LEN], buffer_link[BUFFER_LEN];
874   wzd_string_t * dirname, *linkname;
875   wzd_string_t * command;
876   int ret;
877 
878   command = str_read_token(command_line);
879   if (!command) {
880     do_site_help("link",context);
881     return 1;
882   }
883   dirname = str_read_token(command_line);
884   if (!dirname) {
885     do_site_help("link",context);
886     str_deallocate(command);
887     return 1;
888   }
889 
890   /* convert file to absolute path, remember _setPerm wants ABSOLUTE paths ! */
891   if (checkpath_new(str_tochar(dirname),buffer_dir,context)) {
892     ret = send_message_with_args(501,context,"Dirname is invalid");
893     str_deallocate(command); str_deallocate(dirname);
894     return 0;
895   }
896   str_deallocate(dirname);
897   /* remove the last slash */
898   if(buffer_dir[strlen(buffer_dir)-1]=='/') buffer_dir[strlen(buffer_dir)-1]=0;
899 
900   if (strcasecmp(str_tochar(command),"CREATE")==0)
901   {
902     linkname = str_read_token(command_line);
903     if (!linkname) {
904       do_site_help("link",context);
905       str_deallocate(command);
906       str_deallocate(linkname);
907       return 1;
908     }
909     if ( (ret = checkpath_new(str_tochar(linkname),buffer_link,context) ) && ret != E_FILE_NOEXIST) { /* of course it returns no_exist */
910       ret = send_message_with_args(501,context,"Linkname is invalid");
911       str_deallocate(command);
912       str_deallocate(linkname);
913       return 0;
914     }
915     str_deallocate(linkname);
916     if(buffer_link[strlen(buffer_link)-1]=='/') buffer_link[strlen(buffer_link)-1]=0;
917 
918     ret = symlink_create(buffer_dir, buffer_link);
919   }
920   else if (strcasecmp(str_tochar(command),"REMOVE")==0)
921   {
922     ret = symlink_remove(buffer_dir);
923   }
924   else {
925     do_site_help("link",context);
926     str_deallocate(command);
927     return 1;
928   }
929 
930   ret ? send_message_with_args(501,context,"Command failed") : send_message_with_args(200,context,"Command okay");
931   str_deallocate(command);
932 
933   return 0;
934 }
935 
936 
937 /********************* do_site_msg *************************/
938 /** msg: show
939  *       new msg_line
940  *       append msg_line
941  *       convert filename
942  *       delete
943  */
do_site_msg(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)944 int do_site_msg(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
945 {
946 /*  int ret;*/
947   wzd_string_t * command, * filename;
948   char msg_file[2048];
949   char other_file[2048];
950   unsigned int length;
951   fs_filestat_t s;
952 
953   if (!mainConfig->dir_message) {
954     send_message_with_args(501,context,"No dir_message defined in config");
955     return 1;
956   }
957 
958   if (checkpath_new(".",msg_file,context)) {
959     send_message_with_args(501,context,". does not exist?!");
960     return 1;
961   } else {
962     length = strlen(msg_file);
963     if (msg_file[length-1] != '/') msg_file[length++] = '/'; /** \bug now we are _sure_ that checkpath_new appends a / so we can remove check ? */
964     strncpy(other_file,msg_file,2048);
965     strncpy(msg_file+length,mainConfig->dir_message,2048-length-1);
966   }
967 
968   command = str_tok(command_line," \t\r\n");
969   if (!command) {
970     do_site_help("msg",context);
971     return 1;
972   }
973 
974   if (strcasecmp(str_tochar(command),"show")==0)
975   {
976     str_deallocate(command);
977     do_site_print_file_raw(msg_file,context);
978     return 0;
979   }
980   else if (strcasecmp(str_tochar(command),"convert")==0)
981   {
982     str_deallocate(command);
983     filename = str_tok(command_line,"\r\n");
984     if (!filename) {
985       do_site_help("msg",context);
986       return 1;
987     }
988     strncpy(other_file+length,str_tochar(filename),2048-length-1);
989     str_deallocate(filename);
990     if (fs_file_stat(other_file,&s) || !S_ISREG(s.mode))
991     {
992       send_message_with_args(501,context,"Inexistant file, or not a regular file");
993       return -1;
994     }
995     unlink(msg_file);
996     if (!safe_rename(other_file,msg_file))
997     {
998       send_message_with_args(200,context,"Message file loaded");
999       return 0;
1000     }
1001     send_message_with_args(501,context,"Error while renaming file");
1002     return -1;
1003   }
1004   else if (strcasecmp(str_tochar(command),"delete")==0)
1005   {
1006     str_deallocate(command);
1007     unlink(msg_file);
1008     send_message_with_args(200,context,"Message file deleted");
1009     return 0;
1010   }
1011   else if (strcasecmp(str_tochar(command),"new")==0)
1012   {
1013     FILE * fp;
1014     wzd_string_t * buf;
1015     unsigned int length;
1016 
1017     str_deallocate(command);
1018     fp = fopen(msg_file,"w");
1019     if (!fp) {
1020       send_message_with_args(501,context,"Unable to open message file for writing");
1021       return 1;
1022     }
1023     buf = str_tok(command_line,"\r\n");
1024     if (!buf) {
1025       fclose(fp);
1026       do_site_help("msg",context);
1027       return 1;
1028     }
1029     length = strlen(str_tochar(buf));
1030     if (length != fwrite(str_tochar(buf),1,length,fp)) {
1031       fclose(fp);
1032       send_message_with_args(501,context,"Unable to write message");
1033       str_deallocate(buf);
1034       return 1;
1035     }
1036     fclose(fp);
1037     send_message_with_args(200,context,"Message file written");
1038     str_deallocate(buf);
1039     return 0;
1040   }
1041   else if (strcasecmp(str_tochar(command),"append")==0)
1042   {
1043     FILE * fp;
1044     wzd_string_t * buf;
1045     unsigned int length;
1046 
1047     str_deallocate(command);
1048     fp = fopen(msg_file,"a");
1049     if (!fp) {
1050       send_message_with_args(501,context,"Unable to open message file for writing");
1051       return 1;
1052     }
1053     buf = str_tok(command_line,"\r\n");
1054     if (!buf) {
1055       fclose(fp);
1056       do_site_help("msg",context);
1057       return 1;
1058     }
1059     length = strlen(str_tochar(buf));
1060     if (length != fwrite(str_tochar(buf),1,length,fp)) {
1061       fclose(fp);
1062       send_message_with_args(501,context,"Unable to write message");
1063       str_deallocate(buf);
1064       return 1;
1065     }
1066     fclose(fp);
1067     send_message_with_args(200,context,"Message file written");
1068     str_deallocate(buf);
1069     return 0;
1070   }
1071 
1072   do_site_help("msg",context);
1073   str_deallocate(command);
1074   return 0;
1075 }
1076 
subcmp(const char * string,const char * substring)1077 static int subcmp(const char * string, const char * substring)
1078 {
1079   return strncasecmp(string,substring,strlen(substring));
1080 }
1081 
1082 
1083 
1084 /********************* do_site_perm ************************/
1085 /** perm: show  (show all permissions)
1086  *        show perm_name  (show permissions for all commands starting with perm_name)
1087  *        add perm_name perms
1088  *        change perm_name perms
1089  *        remove perm_name
1090  * XXX FIXME sort perms before sending !
1091  */
do_site_perm(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)1092 int do_site_perm(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
1093 {
1094 /*  int ret;*/
1095   wzd_string_t * command_name, * perm_name, * ptr;
1096   char perm_buffer[256];
1097   char buffer[2048];
1098   wzd_command_t * command;
1099   int ret;
1100 
1101   command_name = str_tok(command_line," \t\r\n");
1102   if (!command_name) {
1103     do_site_help("perm",context);
1104     return 1;
1105   }
1106   perm_name = str_tok(command_line," \t\r\n");
1107 
1108   if (strcasecmp(str_tochar(command_name),"show")==0)
1109   {
1110     str_deallocate(command_name);
1111     send_message_raw("200-\r\n",context);
1112     if ( !perm_name ) {
1113       /* no argument: print all perms */
1114       List * list;
1115       ListElmt * elmnt;
1116 
1117       list = chtbl_extract(mainConfig->commands_list, NULL, NULL, (cmp_function)strcmp);
1118 
1119       if (list) {
1120         for (elmnt=list_head(list); elmnt; elmnt=list_next(elmnt)) {
1121           command = list_data(elmnt);
1122           if (command && !perm2str(command->perms,perm_buffer,sizeof(perm_buffer)) ) {
1123             snprintf( buffer, sizeof(buffer), " %s%s\r\n", command->name, perm_buffer);
1124             send_message_raw(buffer,context);
1125           }
1126         }
1127         list_destroy(list);
1128         free(list);
1129       }
1130     } else {
1131       /* search on perms name */
1132       int found=0;
1133       List * list;
1134       ListElmt * elmnt;
1135 
1136       list = chtbl_extract(mainConfig->commands_list, (cmp_function)subcmp, str_tochar(perm_name), (cmp_function)strcmp);
1137 
1138       if (list) {
1139         if (list_size(list)>0) found=1;
1140         for (elmnt=list_head(list); elmnt; elmnt=list_next(elmnt)) {
1141           command = list_data(elmnt);
1142           if (command && !perm2str(command->perms,perm_buffer,sizeof(perm_buffer)) ) {
1143             snprintf( buffer, sizeof(buffer), " %s%s\r\n", command->name, perm_buffer);
1144             send_message_raw(buffer,context);
1145           }
1146         }
1147         list_destroy(list);
1148         free(list);
1149       }
1150       if (!found)
1151         send_message_raw(" permission not found\r\n",context);
1152       str_deallocate(perm_name);
1153     }
1154     send_message_raw("200 \r\n",context);
1155     return 0;
1156   }
1157   else if (strcasecmp(str_tochar(command_name),"change")==0)
1158   {
1159     str_deallocate(command_name);
1160     ptr = str_tok(command_line,"\r\n");
1161     if (!perm_name || !ptr) {
1162       do_site_help("perm",context);
1163       str_deallocate(perm_name);
1164       return 1;
1165     }
1166 
1167     ret = commands_set_permission(mainConfig->commands_list,str_tochar(perm_name),str_tochar(ptr));
1168 
1169     str_deallocate(perm_name);
1170     str_deallocate(ptr);
1171     if (ret) {send_message_with_args(501,context,"Error changing permission"); return 1; }
1172     send_message_with_args(200,context,"Command okay, permission changed");
1173     return -1;
1174   }
1175   else if (strcasecmp(str_tochar(command_name),"remove")==0)
1176   {
1177     str_deallocate(command_name);
1178     if (!perm_name) {
1179       do_site_help("perm",context);
1180       return 1;
1181     }
1182     if ( commands_delete_permission(mainConfig->commands_list,perm_name) )
1183       send_message_with_args(501,context,"Error, permission NOT deleted");
1184     else
1185       send_message_with_args(200,context,"Command okay, permission deleted");
1186     str_deallocate(perm_name);
1187     return 0;
1188   }
1189   else if (strcasecmp(str_tochar(command_name),"add")==0)
1190   {
1191     str_deallocate(command_name);
1192     ptr = str_tok(command_line,"\r\n");
1193     if (!perm_name || !ptr) {
1194       do_site_help("perm",context);
1195       str_deallocate(perm_name); str_deallocate(ptr);
1196       return 1;
1197     }
1198 
1199     ret = commands_add_permission(mainConfig->commands_list,str_tochar(perm_name),str_tochar(ptr));
1200 
1201     str_deallocate(perm_name);
1202     str_deallocate(ptr);
1203     if (ret) {send_message_with_args(501,context,"Error adding permission"); return 1; }
1204     send_message_with_args(200,context,"Command okay, permission changed");
1205     return 0;
1206   }
1207 
1208   do_site_help("perm",context);
1209   str_deallocate(command_name);
1210   str_deallocate(perm_name);
1211   return 0;
1212 }
1213 
1214 
1215 /********************* do_site_print_file ******************/
1216 /** Print filename to control connection. Cookies are replaced as usual.
1217  */
do_site_print_file(const char * filename,wzd_user_t * user,wzd_group_t * group,wzd_context_t * context)1218 void do_site_print_file(const char *filename, wzd_user_t *user, wzd_group_t *group, wzd_context_t *context)
1219 {
1220   wzd_cache_t * fp;
1221   char * file_buffer;
1222   unsigned int size, filesize;
1223   u64_t sz64;
1224   fp = wzd_cache_open(filename,O_RDONLY,0644);
1225   if (!fp) {
1226     send_message_with_args(501,context,"Inexistant file");
1227     return;
1228   }
1229   sz64 = wzd_cache_getsize(fp);
1230   if (sz64 > INT_MAX) {
1231     out_log(LEVEL_HIGH,"%s:%d couldn't allocate %" PRIu64 "bytes for file %s\n",__FILE__,__LINE__,sz64,filename);
1232     wzd_cache_close(fp);
1233     send_message_with_args(501,context,"Internal error (see log)");
1234     return;
1235   }
1236   filesize = (unsigned int)sz64;
1237   file_buffer = malloc(filesize+1);
1238   if ( (size=wzd_cache_read(fp,file_buffer,filesize))!=filesize )
1239   {
1240     out_err(LEVEL_HIGH,"Could not read file %s read %u instead of %u (%s:%d)\n",filename,size,filesize,__FILE__,__LINE__);
1241     free(file_buffer);
1242     wzd_cache_close(fp);
1243     send_message_with_args(501,context,"Internal error (see log)");
1244     return;
1245   }
1246   file_buffer[filesize]='\0';
1247 
1248   /* send header */
1249   send_message_raw("200-\r\n",context);
1250 
1251   cookie_parse_buffer(file_buffer,user,group,context,NULL,0);
1252 
1253   wzd_cache_close(fp);
1254 
1255   send_message_raw("200 \r\n",context);
1256 
1257   free(file_buffer);
1258 }
1259 
1260 /********************* do_site_print_file_raw **************/
1261 /** Print filename to control connection, without replacing cookies.
1262  */
do_site_print_file_raw(const char * filename,wzd_context_t * context)1263 void do_site_print_file_raw(const char *filename, wzd_context_t *context)
1264 {
1265   wzd_cache_t * fp;
1266   char buffer[1024];
1267   unsigned int length;
1268 
1269   fp = wzd_cache_open(filename,O_RDONLY,0644);
1270   if (!fp) {
1271     send_message_with_args(501,context,"Inexistant file");
1272     return;
1273   }
1274 
1275   /* send header */
1276   send_message_raw("200--\r\n",context);
1277 
1278   strncpy(buffer,"200-",5);
1279   while (wzd_cache_gets(fp, buffer+4, sizeof(buffer)-8))
1280   {
1281     chop(buffer);
1282     length = strlen(buffer);
1283     buffer[length  ] = '\r';
1284     buffer[length+1] = '\n';
1285     buffer[length+2] = '\0';
1286     send_message_raw(buffer,context);
1287   }
1288 
1289   wzd_cache_close(fp);
1290 
1291   send_message_raw("200 -\r\n",context);
1292 }
1293 
1294 /********************* do_site_reload **********************/
1295 
do_site_reload(UNUSED wzd_string_t * ignored,UNUSED wzd_string_t * param,wzd_context_t * context)1296 int do_site_reload(UNUSED wzd_string_t * ignored, UNUSED wzd_string_t *param, wzd_context_t * context)
1297 {
1298   int ret;
1299   pid_t pid;
1300 #ifndef WIN32
1301   char buffer[256];
1302 #endif
1303 
1304 #ifdef WZD_MULTIPROCESS
1305   pid = getppid();
1306 #else
1307   pid = getpid();
1308 #endif
1309   if (pid <2) {
1310     ret = send_message_with_args(501,context,"ARG! Getting invalid pid?!");
1311     return 1;
1312   }
1313   out_log(LEVEL_CRITICAL,"Target pid: %d\n",pid);
1314 
1315 #ifndef WIN32
1316   ret = send_message_raw("200-Sending SIGHUP to main server, waiting for result\r\n",context);
1317   ret = kill(pid,SIGHUP);
1318   if (ret)
1319     snprintf(buffer,255,"200 ERROR kill returned %d (%s)\r\n",ret,strerror(errno));
1320   else
1321     snprintf(buffer,255,"200 kill returned ok\r\n");
1322   ret = send_message_raw(buffer,context);
1323 #else
1324   /* FIXME VISUAL : call server_restart explicitely ? */
1325   /*ret = send_message_with_args(501,context,"kill(getpid(),SIGHUP) not supported on visual ...");*/
1326   ret = send_message_with_args(501, context, "Restarting server, cross your fingers...");
1327   server_restart(SIGHUP);
1328   return 1;
1329 #endif
1330   return 0;
1331 }
1332 
1333 /********************* do_site_rusage **********************/
1334 
do_site_rusage(UNUSED wzd_string_t * ignored,UNUSED wzd_string_t * param,wzd_context_t * context)1335 int do_site_rusage(UNUSED wzd_string_t * ignored, UNUSED wzd_string_t *param, wzd_context_t * context)
1336 {
1337 #ifndef WIN32
1338   int ret=0;
1339   char buffer[256];
1340   struct rusage ru;
1341   struct rlimit rlim;
1342 
1343   send_message_raw("200-\r\n",context);
1344 
1345   if (getrusage(RUSAGE_SELF,&ru)<0)
1346   {
1347     ret=errno; /* save errno value */
1348     send_message_raw("200- getrusage() failed !\r\n",context);
1349     snprintf(buffer,255,"200-errno: %d (%s)\r\n",ret,strerror(ret));
1350     send_message_raw(buffer,context);
1351     send_message_raw("200 \r\n",context);
1352     return 0;
1353   }
1354   send_message_raw("200- Ressources used for wzdftpd:\r\n",context);
1355   sprintf(buffer,"200-  user time used: %ld s %ld ms\r\n",ru.ru_utime.tv_sec,ru.ru_utime.tv_usec/1000);
1356   send_message_raw(buffer,context);
1357   sprintf(buffer,"200-  system time used: %ld s %ld ms\r\n",ru.ru_stime.tv_sec,ru.ru_stime.tv_usec/1000);
1358   send_message_raw(buffer,context);
1359   /* system time used */
1360   /* maximum resident set size */
1361   /* integral shared memory size */
1362   /* integral unshared data size */
1363   /* integral unshared stack size */
1364   /* page reclaims */
1365   /* page faults */
1366   /* swaps */
1367   /* block input operations */
1368   /* block output operations */
1369   /* messages sent */
1370   /* messages received */
1371   /* signals received */
1372   /* voluntary context switches */
1373   /* involuntary context switches */
1374 
1375   /* Number of opened files:
1376    *  LINUX: read directory  /proc/`getpid()`/fd/
1377    *    contains symlinks, destination is file
1378    */
1379 #ifdef __linux__
1380   {
1381     char dirname[256], buflink[256];
1382     unsigned int childs[256];
1383     unsigned int child;
1384     pid_t mother;
1385     DIR * d;
1386     struct dirent *dent;
1387     int count, rdi, fdcount;
1388 
1389     send_message_raw("200- LINUX specific:\r\n",context);
1390     mother = getpid();
1391     sprintf(buffer,"200-  mother pid: %ld\r\n",(long)mother);
1392     send_message_raw(buffer,context);
1393 
1394     /* searching for child threads */
1395     count = 0;
1396     snprintf(dirname,sizeof(dirname),"/proc/%ld/task",(long)mother); /** \todo XXX 2.6 specific ? */
1397     d = opendir(dirname);
1398     if (d) {
1399       while ( (dent = readdir(d)) ) {
1400         if (dent->d_name[0] == '.') continue;
1401         child = atoi(dent->d_name);
1402         childs[count] = child;
1403         count ++;
1404         sprintf(buffer,"200-   |-> child pid: %s\r\n",dent->d_name);
1405         send_message_raw(buffer,context);
1406       }
1407       closedir(d);
1408     }
1409 
1410     sprintf(buffer,"200-  resources for: %d\r\n",mother);
1411     send_message_raw(buffer,context);
1412     snprintf(dirname,sizeof(dirname),"/proc/%d/task/%d/fd",mother,mother); /** \todo XXX 2.6 specific ? */
1413     d = opendir(dirname);
1414     if (d) {
1415       fdcount = 0;
1416       while ( (dent = readdir(d)) ) {
1417         if (dent->d_name[0] == '.') continue;
1418         fdcount ++;
1419         snprintf(dirname,sizeof(dirname),"/proc/%d/task/%d/fd/%s",mother,mother,dent->d_name); /** \todo XXX 2.6 specific ? */
1420         rdi = readlink(dirname,buflink,sizeof(buflink));
1421         if (rdi > 0) {
1422           buflink[rdi] = '\0';
1423           sprintf(buffer,"200-   |-> %s -> %s\r\n",dent->d_name,buflink);
1424           send_message_raw(buffer,context);
1425         }
1426       }
1427       closedir(d);
1428 
1429       sprintf(buffer,"200-  number of open files: %d\r\n",fdcount);
1430       send_message_raw(buffer,context);
1431     }
1432   }
1433 #endif /* __linux__ */
1434 
1435   if (getrlimit(RLIMIT_NOFILE,&rlim)<0) {
1436     send_message_raw("200- getrlimit(RLIMIT_NOFILE) failed !\r\n",context);
1437     snprintf(buffer,255,"200-errno: %d (%s)\r\n",ret,strerror(ret));
1438     send_message_raw(buffer,context);
1439     send_message_raw("200 \r\n",context);
1440     return 0;
1441   }
1442 
1443   send_message_raw("200- LIMITS:\r\n",context);
1444   sprintf(buffer,"200-  number of open files: %ld ; max: %ld\r\n",(long)rlim.rlim_cur,(long)rlim.rlim_max);
1445   send_message_raw(buffer,context);
1446 
1447   send_message_raw("200 \r\n",context);
1448 #else /* _MSC_VER */
1449   send_message_with_args(501,context,"Can't be implemented on win32!");
1450 #endif /* _MSC_VER */
1451   return 0;
1452 }
1453 
1454 /********************* do_site_savecfg *********************/
do_site_savecfg(UNUSED wzd_string_t * ignored,UNUSED wzd_string_t * command_line,wzd_context_t * context)1455 int do_site_savecfg(UNUSED wzd_string_t *ignored, UNUSED wzd_string_t *command_line, wzd_context_t * context)
1456 {
1457   wzd_string_t * data = NULL;
1458   size_t length = 0, written;
1459   int fd;
1460   int do_backup;
1461   int err = 0;
1462 
1463   do_backup = config_get_boolean(mainConfig->cfg_file, "GLOBAL", "backup config", &err);
1464   if (err == CF_ERROR_NOT_FOUND) do_backup = 0; /* default: no backup */
1465   else if (err != CF_OK) {
1466     out_log(LEVEL_HIGH,"ERROR Could not save config (error while getting option 'backup config')\n");
1467     send_message_with_args(501,context,"Cannot save server config");
1468     return -1;
1469   }
1470 
1471   if (do_backup)
1472   {
1473     int backup_fd;
1474     char buffer[1024];
1475     ssize_t ret;
1476     char * filename = NULL;
1477     size_t name_length;
1478 
1479     name_length = strlen(mainConfig->config_filename);
1480     filename = malloc(name_length + 6);
1481     snprintf(filename,name_length+5,"%s.old",mainConfig->config_filename);
1482     out_log(LEVEL_FLOOD,"DEBUG Config file saved to [%s]\n",filename);
1483 
1484     fd = open(mainConfig->config_filename, O_RDONLY, 0644);
1485     backup_fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0644);
1486     if (fd < 0 || backup_fd < 0) {
1487       out_log(LEVEL_HIGH,"ERROR Could not save config (error when opening files)\n");
1488       send_message_with_args(501,context,"Cannot save server config");
1489       close(fd);
1490       close(backup_fd);
1491       return -1;
1492     }
1493     while ((ret = read(fd,buffer,sizeof(buffer))) > 0) {
1494       write(backup_fd,buffer,ret);
1495     }
1496     close(fd);
1497     close(backup_fd);
1498   }
1499 
1500   out_log(LEVEL_NORMAL,"INFO saving config to %s\n",mainConfig->config_filename);
1501 
1502   fd = open(mainConfig->config_filename, O_WRONLY | O_TRUNC, 0644); /** XXX file mode is hardcoded */
1503   if (fd < 0) {
1504     out_log(LEVEL_HIGH,"ERROR Could not save config (error while creating a temporary file: %d: %s)\n",errno,strerror(errno));
1505     send_message_with_args(501,context,"Cannot save server config");
1506     return -1;
1507   }
1508 
1509   data = config_to_data(mainConfig->cfg_file,&length);
1510 
1511   if (data == NULL) {
1512     out_log(LEVEL_HIGH,"ERROR Could not save config (error in config_to_data)\n");
1513     send_message_with_args(501,context,"Cannot save server config");
1514     close(fd);
1515     return -1;
1516   }
1517 
1518   written = write(fd, str_tochar(data), length);
1519   if (written != length) {
1520     out_log(LEVEL_HIGH,"ERROR Could not save config (written %ld bytes instead of %ld\n",(long)written,(long)length);
1521   }
1522   close(fd);
1523 
1524   send_message_with_args(200,context,"Server config saved");
1525 
1526   str_deallocate(data);
1527   return 0;
1528 }
1529 
1530 /********************* do_site_sections ******************/
1531 /** Print all sections
1532  */
do_site_sections(UNUSED wzd_string_t * ignored,UNUSED wzd_string_t * param,wzd_context_t * context)1533 int do_site_sections(UNUSED wzd_string_t *ignored, UNUSED wzd_string_t *param, wzd_context_t * context)
1534 {
1535   wzd_section_t * section;
1536   wzd_string_t * buffer = str_allocate();
1537 
1538   /* send header */
1539   send_message_raw("200-\r\n",context);
1540   send_message_raw(" NAME  MASK  REGEX\r\n",context);
1541 
1542   for (section = mainConfig->section_list; section; section = section->next_section) {
1543     str_sprintf(buffer, " %s  %s  %s\r\n", section->sectionname, section->sectionmask, section->sectionre);
1544     send_message_raw(str_tochar(buffer), context);
1545   }
1546 
1547   send_message_raw("200 \r\n",context);
1548   str_deallocate(buffer);
1549 
1550   return 0;
1551 }
1552 
1553 /** \brief Show last log messages (10 by default)
1554  */
do_site_showlog(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)1555 int do_site_showlog(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
1556 {
1557   int i;
1558   struct memory_log_t * log = get_log_buffer();
1559   wzd_string_t * buffer = str_allocate();
1560   int lines_to_show = 10;
1561   int offset = 0;
1562   char *ptr;
1563   unsigned long ul;
1564 
1565   /* check if we have an argument (the number of lines to display) */
1566   if (str_length(command_line) > 0) {
1567     ul = strtoul(str_tochar(command_line),&ptr,10);
1568     if (ptr != NULL && *ptr == '\0' && ul < (long)log->size) {
1569       lines_to_show = (int)ul;
1570 
1571       /* change offset to match the last line to display */
1572       for (i=log->size-1; i>=0; i--) {
1573         if (log->data[i] != NULL) {
1574           offset = i;
1575           break;
1576         }
1577       }
1578       /* then go back to print the correct number of lines */
1579       offset -= lines_to_show;
1580       if (offset < 0) offset = 0;
1581 
1582     } else {
1583       lines_to_show = log->size;
1584     }
1585   }
1586 
1587   /* send header */
1588   send_message_raw("200-\r\n",context);
1589 
1590   for (i=offset; i<offset+lines_to_show; i++) {
1591     if (log->data[i] != NULL) {
1592       str_sprintf(buffer, " %s", log->data[i]);
1593       send_message_raw(str_tochar(buffer),context);
1594     }
1595   }
1596 
1597   send_message_raw("200 \r\n",context);
1598   str_deallocate(buffer);
1599 
1600   return 0;
1601 }
1602 
1603 /********************* do_site_unlock **********************/
1604 /** unlock: file1 [file2 ...]
1605  */
1606 
do_site_unlock(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)1607 int do_site_unlock(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
1608 {
1609   char buffer[BUFFER_LEN];
1610   wzd_string_t * filename;
1611   int ret=0;
1612 
1613   filename = str_tok(command_line," \t\r\n");
1614   if (!filename) {
1615     do_site_help("unlock",context);
1616     return 1;
1617   }
1618 
1619   do
1620   {
1621     /* convert file to absolute path, remember file_unlock wants ABSOLUTE paths ! */
1622     ret = checkpath(str_tochar(filename),buffer,context);
1623     str_deallocate(filename);
1624     if (ret) continue; /* path is NOT ok ! */
1625 /*    buffer[strlen(buffer)-1] = '\0';*/ /* remove '/', appended by checkpath */
1626 
1627     /* we need to use open() directly because file_open uses file_islocked ... */
1628     ret = file_force_unlock(buffer);
1629     if (ret < 0) {
1630       break;
1631     }
1632   }
1633   while ( (filename = str_tok(command_line," \t\r\n")) );
1634 
1635   if (ret == 0) {
1636     ret = send_message_with_args(200,context,"File(s) unlocked");
1637   } else {
1638     ret = send_message_with_args(501,context,"UNLOCK FAILED");
1639   }
1640 
1641   return 0;
1642 }
1643 /********************* do_site_user ************************/
1644 /** user username
1645  */
1646 
do_site_user(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)1647 int do_site_user(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
1648 {
1649   const char * username;
1650   int ret;
1651   wzd_user_t user;
1652   int uid;
1653   wzd_string_t * str;
1654   wzd_user_t * me = NULL;
1655 
1656   if (context) me = GetUserByID(context->userid); /*get self*/
1657 
1658   username = str_tochar(command_line);
1659   if (!username) {
1660     do_site_help("user",context);
1661     return 0;
1662   }
1663   /* check that username exists */
1664   if ( backend_find_user(username,&user,&uid) ) {
1665     ret = send_message_with_args(501,context,"User does not exist");
1666     return 0;
1667   }
1668   if ( strchr(user.flags,FLAG_ULTRAHIDDEN )&&
1669        (me) &&
1670        (strcmp(username,me->username)!=0)/* do not hide to self ! */
1671      ) {
1672     /* for siteops we could send a different message, like 'user is hidden' */
1673     ret = send_message_with_args(501,context,"User does not exist");
1674     return 0;
1675   }
1676 
1677   str = config_get_string(mainConfig->cfg_file,"GLOBAL","sitefile_user",NULL);
1678   if (!str) {
1679     ret = send_message_with_args(501,context,"File [GLOBAL] / sitefile_user does not exist");
1680     return 0;
1681   }
1682 
1683   do_site_print_file(str_tochar(str),&user,NULL,context);
1684 
1685   str_deallocate(str);
1686   return 0;
1687 }
1688 
1689 /********************* do_site_utime ***********************/
1690 /** utime filename YYYYMMDDhhmmss YYYYMMDDhhmmss YYYYMMDDhhmmss UTC
1691  * change access time, modification time, modification of status of a file
1692  */
1693 
do_site_utime(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)1694 int do_site_utime(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
1695 {
1696 #ifdef HAVE_STRPTIME
1697   extern char *strptime (__const char *__restrict __s,
1698     __const char *__restrict __fmt, struct tm *__tp);
1699 #endif
1700   char buffer[BUFFER_LEN];
1701   char * ptr;
1702   wzd_string_t * filename;
1703   wzd_string_t * new_atime, * new_mtime, * new_ctime;
1704   wzd_string_t * timezone;
1705   struct tm tm_atime, tm_mtime, tm_ctime;
1706   struct utimbuf utime_buf;
1707   int ret;
1708   wzd_user_t * user;
1709 
1710   user = GetUserByID(context->userid);
1711 
1712   filename = str_tok(command_line," \t\r\n");
1713   if (!filename) {
1714     do_site_help("utime",context);
1715     return 1;
1716   }
1717   new_atime = str_tok(command_line," \t\r\n");
1718   if (!new_atime) {
1719     do_site_help("utime",context);
1720     str_deallocate(filename);
1721     return 1;
1722   }
1723   new_mtime = str_tok(command_line," \t\r\n");
1724   if (!new_mtime) {
1725     do_site_help("utime",context);
1726     str_deallocate(filename); str_deallocate(new_atime);
1727     return 1;
1728   }
1729   new_ctime = str_tok(command_line," \t\r\n");
1730   if (!new_ctime) {
1731     do_site_help("utime",context);
1732     str_deallocate(filename); str_deallocate(new_atime); str_deallocate(new_mtime);
1733     return 1;
1734   }
1735   timezone = str_tok(command_line," \t\r\n");
1736   if (!timezone) {
1737     do_site_help("utime",context);
1738     str_deallocate(filename); str_deallocate(new_atime); str_deallocate(new_mtime);
1739     str_deallocate(new_ctime);
1740     return 1;
1741   }
1742   /* TODO check that timezone is UTC */
1743   memset(&tm_atime,0,sizeof(struct tm));
1744   ptr=strptime((char*)str_tochar(new_atime),"%Y%m%d%H%M%S",&tm_atime);
1745   if (ptr == NULL || *ptr != '\0') {
1746     do_site_help("utime",context);
1747     str_deallocate(filename); str_deallocate(new_atime); str_deallocate(new_mtime);
1748     str_deallocate(new_ctime); str_deallocate(timezone);
1749     return 1;
1750   }
1751   str_deallocate(new_atime);
1752   memset(&tm_mtime,0,sizeof(struct tm));
1753   ptr=strptime((char*)str_tochar(new_mtime),"%Y%m%d%H%M%S",&tm_mtime);
1754   if (ptr == NULL || *ptr != '\0') {
1755     do_site_help("utime",context);
1756     str_deallocate(filename); str_deallocate(new_mtime);
1757     str_deallocate(new_ctime); str_deallocate(timezone);
1758     return 1;
1759   }
1760   str_deallocate(new_mtime);
1761   /* TODO ctime is useless in *nix systems ... */
1762   memset(&tm_ctime,0,sizeof(struct tm));
1763   ptr=strptime((char*)str_tochar(new_ctime),"%Y%m%d%H%M%S",&tm_ctime);
1764   if (ptr == NULL || *ptr != '\0') {
1765     do_site_help("utime",context);
1766     str_deallocate(filename); str_deallocate(new_ctime); str_deallocate(timezone);
1767     return 1;
1768   }
1769   str_deallocate(new_ctime);
1770   str_deallocate(timezone);
1771 
1772   utime_buf.actime = mktime(&tm_atime);
1773   utime_buf.modtime = mktime(&tm_mtime);
1774   /* convert file to absolute path, remember _setPerm wants ABSOLUTE paths ! */
1775   if (checkpath(str_tochar(filename),buffer,context)) { /* path is NOT ok ! */
1776     ret = send_message_with_args(501,context,"File does not exist");
1777     str_deallocate(filename);
1778     return 1;
1779   }
1780   str_deallocate(filename);
1781 /*  buffer[strlen(buffer)-1] = '\0';*/ /* remove '/', appended by checkpath */
1782   ret = _checkPerm(buffer,RIGHT_RNFR,user);
1783   if (ret) {
1784     ret = send_message_with_args(501,context,"Access denied");
1785     return 1;
1786   }
1787 
1788   ret = utime(buffer,&utime_buf);
1789 
1790   ret = send_message_with_args(200,context,"UTIME command okay");
1791   return 0;
1792 }
1793 
1794 /********************* do_site_vars *********************/
1795 
do_site_vars(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)1796 int do_site_vars(UNUSED wzd_string_t *ignored, wzd_string_t * command_line, wzd_context_t * context)
1797 {
1798   wzd_string_t *command, *varname, *value;
1799   char * buffer;
1800   int ret;
1801 
1802   command = str_tok(command_line," \t\r\n");
1803   if (!command) {
1804     do_site_help("vars",context);
1805     return 1;
1806   }
1807   str_tolower(command);
1808 
1809   varname = str_tok(command_line," \t\r\n");
1810   if (!varname) {
1811     do_site_help("vars",context);
1812     str_deallocate(command);
1813     return 1;
1814   }
1815 
1816   if (strcmp(str_tochar(command),"get")==0) {
1817     str_deallocate(command);
1818     buffer = malloc(1024); /** \todo XXX harcoded value ! */
1819     ret = vars_get(str_tochar(varname),buffer,1024,mainConfig);
1820 
1821     if (ret)
1822       send_message_with_args(200,context,"An error occurred inside vars_get");
1823     else
1824       send_message_with_args(200,context,buffer);
1825 
1826     free(buffer);
1827     str_deallocate(varname);
1828     return 0;
1829   }
1830   else if (strcmp(str_tochar(command),"set")==0) {
1831     str_deallocate(command);
1832     value = str_tok(command_line," \t\r\n");
1833     if (!value) {
1834       do_site_help("vars",context);
1835       str_deallocate(varname);
1836       return 1;
1837     }
1838 
1839     ret = vars_set(str_tochar(varname),str_tochar(value),strlen(str_tochar(value)),mainConfig);
1840 
1841     if (ret)
1842       send_message_with_args(200,context,"An error occurred inside vars_set");
1843     else
1844       send_message_with_args(200,context,"Command okay");
1845 
1846     str_deallocate(varname);
1847     str_deallocate(value);
1848     return 0;
1849   }
1850 
1851   send_message_with_args(200,context,"Command okay");
1852   str_deallocate(command);
1853   str_deallocate(varname);
1854   return 0;
1855 }
1856 
1857 /****************** do_site_vars_group *******************/
1858 
do_site_vars_group(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)1859 int do_site_vars_group(UNUSED wzd_string_t *ignored, wzd_string_t * command_line, wzd_context_t * context)
1860 {
1861   wzd_string_t *groupname, *command, *varname, *value;
1862   char * buffer;
1863   int ret;
1864   wzd_group_t * group;
1865 
1866   command = str_tok(command_line," \t\r\n");
1867   if (!command) {
1868     do_site_help("vars_group",context);
1869     return 1;
1870   }
1871   str_tolower(command);
1872 
1873   groupname = str_tok(command_line," \t\r\n");
1874   if (!groupname) {
1875     do_site_help("vars_group",context);
1876     str_deallocate(command);
1877     return 1;
1878   }
1879   group = GetGroupByName(str_tochar(groupname));
1880   str_deallocate(groupname);
1881   if ( !group ) {
1882     send_message_with_args(501,context,"Group does not exist");
1883     str_deallocate(command);
1884     return 1;
1885   }
1886 
1887   varname = str_tok(command_line," \t\r\n");
1888   if (!varname) {
1889     do_site_help("vars_group",context);
1890     str_deallocate(command);
1891     return 1;
1892   }
1893 
1894   if (strcmp(str_tochar(command),"get")==0) {
1895     str_deallocate(command);
1896     buffer = malloc(1024); /** \todo XXX harcoded value ! */
1897     ret = vars_group_get(group->groupname,str_tochar(varname),buffer,1024,mainConfig);
1898 
1899     if (ret)
1900       send_message_with_args(200,context,"An error occurred inside vars_group_get");
1901     else
1902       send_message_with_args(200,context,buffer);
1903 
1904     free(buffer);
1905     str_deallocate(varname);
1906     return 0;
1907   }
1908   else if (strcmp(str_tochar(command),"set")==0) {
1909     str_deallocate(command);
1910     value = str_tok(command_line," \t\r\n");
1911     if (!value) {
1912       do_site_help("vars_group",context);
1913       str_deallocate(varname);
1914       return 1;
1915     }
1916 
1917     ret = vars_group_set(group->groupname,str_tochar(varname),str_tochar(value),strlen(str_tochar(value)),mainConfig);
1918 
1919     if (ret)
1920       send_message_with_args(200,context,"An error occurred inside vars_group_set");
1921     else
1922       send_message_with_args(200,context,"Command okay");
1923     str_deallocate(value);
1924     str_deallocate(varname);
1925     return 0;
1926   }
1927 
1928   send_message_with_args(200,context,"Command okay");
1929   str_deallocate(command);
1930   str_deallocate(varname);
1931   return 0;
1932 }
1933 
1934 /****************** do_site_vars_user *******************/
1935 
do_site_vars_user(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)1936 int do_site_vars_user(UNUSED wzd_string_t *ignored, wzd_string_t * command_line, wzd_context_t * context)
1937 {
1938   wzd_string_t *username, *command, *varname, *value;
1939   char * buffer;
1940   int ret;
1941   wzd_user_t * user;
1942 
1943   command = str_tok(command_line," \t\r\n");
1944   if (!command) {
1945     do_site_help("vars_user",context);
1946     return 1;
1947   }
1948   str_tolower(command);
1949 
1950   username = str_tok(command_line," \t\r\n");
1951   if (!username) {
1952     do_site_help("vars_user",context);
1953     str_deallocate(command);
1954     return 1;
1955   }
1956   user = GetUserByName(str_tochar(username));
1957   str_deallocate(username);
1958   if ( !user ) {
1959     send_message_with_args(501,context,"User does not exist");
1960     str_deallocate(command);
1961     return 1;
1962   }
1963 
1964   varname = str_tok(command_line," \t\r\n");
1965   if (!varname) {
1966     do_site_help("vars_user",context);
1967     str_deallocate(command);
1968     return 1;
1969   }
1970 
1971   if (strcmp(str_tochar(command),"get")==0) {
1972     str_deallocate(command);
1973     buffer = malloc(1024); /** \todo XXX harcoded value ! */
1974     ret = vars_user_get(user->username,str_tochar(varname),buffer,1024,mainConfig);
1975 
1976     if (ret)
1977       send_message_with_args(200,context,"An error occurred inside vars_user_get");
1978     else
1979       send_message_with_args(200,context,buffer);
1980 
1981     free(buffer);
1982     str_deallocate(varname);
1983     return 0;
1984   }
1985   else if (strcmp(str_tochar(command),"set")==0) {
1986     str_deallocate(command);
1987     value = str_tok(command_line," \t\r\n");
1988     if (!value) {
1989       do_site_help("vars_user",context);
1990       str_deallocate(varname);
1991       str_deallocate(value);
1992       return 1;
1993     }
1994 
1995     ret = vars_user_set(user->username,str_tochar(varname),str_tochar(value),strlen(str_tochar(value)),mainConfig);
1996 
1997     if (ret)
1998       send_message_with_args(200,context,"An error occurred inside vars_user_set");
1999     else
2000       send_message_with_args(200,context,"Command okay");
2001     str_deallocate(varname);
2002     str_deallocate(value);
2003     return 0;
2004   }
2005 
2006   send_message_with_args(200,context,"Command okay");
2007   str_deallocate(varname);
2008   return 0;
2009 }
2010 
2011 /********************* do_site_version *********************/
2012 
do_site_version(UNUSED wzd_string_t * ignored,UNUSED wzd_string_t * param,wzd_context_t * context)2013 int do_site_version(UNUSED wzd_string_t * ignored, UNUSED wzd_string_t * param, wzd_context_t * context)
2014 {
2015   char str[256];
2016   snprintf(str,256,"%s build %s (%s)",
2017       WZD_VERSION_LONG,WZD_BUILD_NUM,WZD_BUILD_OPTS);
2018   send_message_with_args(200,context,str);
2019   return 0;
2020 }
2021 
2022 /********************* do_site_vfsadd **********************/
2023 /** vfsadd |/home/vfsroot|/physical/path| +O =user
2024  */
2025 
do_site_vfsadd(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)2026 int do_site_vfsadd(UNUSED wzd_string_t * ignored, wzd_string_t * command_line, wzd_context_t * context)
2027 {
2028   char *vpath, *ppath, *target;
2029 /*  int i;*/
2030   int ret;
2031   char sep;
2032   const char *ptr;
2033   char * dstptr;
2034   unsigned int dstlen, length;
2035   char buffer[1024];
2036 
2037   strncpy(buffer,str_tochar(command_line),1024);
2038 
2039   /* allocate enough memory */
2040   length = strlen(buffer);
2041   vpath = malloc(length);
2042   ppath = malloc(length);
2043 
2044   /* parse command line */
2045   ptr = buffer;
2046   sep = *ptr++;
2047 
2048   dstptr = vpath;
2049   dstlen = 0;
2050 
2051   while (*ptr) {
2052     if (*ptr == sep) break; /* end */
2053     if (dstlen++ == length-1) break; /* too long */
2054     *dstptr++ = *ptr++;
2055   }
2056   if (!*ptr || *ptr != sep) {
2057     free(vpath); free(ppath);
2058     send_message_with_args(501,context,"site vfsadd |/home/vfsroot|/physical/path| [PERM]");
2059     return 1;
2060   }
2061   *dstptr = '\0';
2062 
2063   dstptr = ppath;
2064   dstlen = 0;
2065   ptr++;
2066 
2067   while (*ptr) {
2068     if (*ptr == sep) break; /* end */
2069     if (dstlen++ == length-1) break; /* too long */
2070     *dstptr++ = *ptr++;
2071   }
2072   if (!*ptr || *ptr != sep) {
2073     free(vpath); free(ppath);
2074     send_message_with_args(501,context,"site vfsadd |/home/vfsroot|/physical/path| [PERM]");
2075     return 1;
2076   }
2077   *dstptr = '\0';
2078 
2079   target = NULL;
2080   ptr++;
2081 
2082   if (*ptr) {
2083     while( *ptr && (*ptr==' ' || *ptr=='\t')) ptr++;
2084     if (*ptr)
2085       target = (char*)ptr;
2086   }
2087 
2088   if( target )
2089     ret = vfs_add_restricted( &mainConfig->vfs, vpath, ppath, target );
2090   else
2091     ret = vfs_add( &mainConfig->vfs, vpath, ppath );
2092 
2093   if (ret==1)
2094     send_message_with_args(501,context,"site vfsadd |/home/vfsroot|/physical/path| [PERM]");
2095   else if (ret==2)
2096   {
2097     char tmp[80];
2098     snprintf( tmp, 80, "vfs %s already set", vpath );
2099     send_message_with_args(501,context,tmp);
2100   }  else
2101     send_message_with_args(200,context,"VFSADD command okay");
2102 
2103   free(vpath); free(ppath);
2104 
2105   return 0;
2106 }
2107 
2108 /********************* do_site_vfsdel **********************/
2109 /** vfsdel /home/vfsroot
2110  */
2111 
do_site_vfsdel(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)2112 int do_site_vfsdel(UNUSED wzd_string_t * ignored, wzd_string_t * command_line, wzd_context_t * context)
2113 {
2114   int ret;
2115 
2116   if (command_line && strlen(str_tochar(command_line))>0)
2117     ret = vfs_remove( &mainConfig->vfs, str_tochar(command_line) );
2118   else ret = 1;
2119 
2120   if (ret==1)
2121     send_message_with_args(501,context,"site vfsdel /home/vfsroot");
2122   else if (ret==2)
2123   {
2124     char tmp[80];
2125     snprintf( tmp, 80, "vfs %s does not exist", str_tochar(command_line) );
2126     send_message_with_args(501,context,tmp);
2127   } else
2128     send_message_with_args(200,context,"VFSDEL command okay");
2129 
2130   return 0;
2131 }
2132 
2133 /* TODO XXX FIXME tests missing
2134  */
do_internal_wipe(const char * filename,wzd_context_t * context)2135 static int do_internal_wipe(const char *filename, wzd_context_t * context)
2136 {
2137   fs_filestat_t s;
2138   int ret;
2139   const char *dir_filename;
2140   char buffer[1024];
2141   char path[1024];
2142   char * ptr;
2143   fs_dir_t * dir;
2144   fs_fileinfo_t * finfo;
2145 
2146   split_filename(filename,path,NULL,1024,0);
2147 
2148   if (fs_file_lstat(filename,&s)) return -1;
2149 
2150   if (S_ISREG(s.mode) || S_ISLNK(s.mode)) {
2151     ret = file_remove(filename,context);
2152     if (ret) return 1;
2153   }
2154   if (S_ISDIR(s.mode))
2155   {
2156     strncpy(buffer,filename,sizeof(buffer));
2157     ptr = buffer + strlen(buffer);
2158     *ptr++ = '/';
2159 
2160     if ( fs_dir_open(filename,&dir) ) return -1;
2161 
2162     while ( !fs_dir_read(dir,&finfo) ) {
2163       dir_filename = fs_fileinfo_getname(finfo);
2164 
2165       if (strcmp(dir_filename,".")==0 || strcmp(dir_filename,"..")==0)
2166         continue;
2167       if (strlen(buffer)+strlen(dir_filename)>=1024) { fs_dir_close(dir); return 1; }
2168       strncpy(ptr,dir_filename,256);
2169 
2170 /*      if (fs_file_stat(buffer,&s)) { fs_dir_close(dir); return -1; }*/
2171       if (fs_file_lstat(buffer,&s)==0) {
2172         if (S_ISREG(s.mode) || S_ISLNK(s.mode)) {
2173 /*          ret = file_remove(buffer,context);*/
2174           ret = unlink(buffer);
2175           if (ret) { fs_dir_close(dir); return 1; }
2176         }
2177         if (S_ISDIR(s.mode)) {
2178           ret = do_internal_wipe(buffer,context);
2179           if (ret) { fs_dir_close(dir); return 1; }
2180         }
2181       }
2182     }
2183 
2184     fs_dir_close(dir);
2185     ret = rmdir(filename);
2186     if (ret) return 1;
2187   }
2188 
2189   return 0;
2190 }
2191 
2192 /********************* do_site_wipe ************************/
2193 /** wipe: [-r] file1 [file2 ...]
2194  */
2195 
do_site_wipe(UNUSED wzd_string_t * ignored,wzd_string_t * command_line,wzd_context_t * context)2196 int do_site_wipe(UNUSED wzd_string_t *ignored, wzd_string_t *command_line, wzd_context_t * context)
2197 {
2198   char buffer[WZD_MAX_PATH+1];
2199   wzd_string_t * firstarg, *filename;
2200   int is_recursive;
2201   int ret;
2202 /*  wzd_user_t user;*/
2203 /*  int uid;*/
2204 
2205   firstarg = str_read_token(command_line);
2206   if (!firstarg) {
2207     do_site_help("wipe",context);
2208     return E_PARAM_NULL;
2209   }
2210   /* check if wiping is recursive */
2211   if ( strcasecmp(str_tochar(firstarg),"-r")==0 ) {
2212     str_deallocate(firstarg);
2213     is_recursive=1;
2214     filename = str_read_token(command_line);
2215     if( !filename) {
2216       do_site_help("wipe",context);
2217       return E_PARAM_INVALID;
2218     }
2219   }
2220   else
2221     filename = firstarg;
2222 
2223   do
2224   {
2225     /* convert file to absolute path, remember _setPerm wants ABSOLUTE paths ! */
2226     if (checkpath_new(str_tochar(filename),buffer,context) != 0)
2227     {
2228       ret = send_message_with_args(501,context,"File does not exist");
2229       str_deallocate(filename);
2230       return E_FILE_NOEXIST;
2231     }
2232 
2233     {
2234       wzd_string_t * event_args = str_allocate();
2235       wzd_user_t * user = GetUserByID(context->userid);
2236       str_sprintf(event_args,"%s %s",user->username,str_tochar(filename));
2237       ret = event_send(mainConfig->event_mgr, EVENT_PREWIPE, 0, event_args, context);
2238       str_deallocate(event_args);
2239     }
2240     if (ret != EVENT_OK && ret != EVENT_BREAK) {
2241       out_log(LEVEL_NORMAL, "Wipe denied by hook (returned %d)\n", ret);
2242       ret = send_message_with_args(501,context,"WIPE denied");
2243       str_deallocate(filename);
2244       return E_COMMAND_FAILED;
2245     }
2246 
2247     /* wipe file | if_recursive dir/file */
2248     ret = do_internal_wipe(buffer,context);
2249     if (ret) {
2250       ret = send_message_with_args(501,context,"WIPE failed");
2251       str_deallocate(filename);
2252       return E_COMMAND_FAILED;
2253     }
2254 
2255     {
2256       wzd_string_t * event_args = str_allocate();
2257       wzd_user_t * user = GetUserByID(context->userid);
2258       str_sprintf(event_args,"%s %s",user->username,str_tochar(filename));
2259       event_send(mainConfig->event_mgr, EVENT_WIPE, 200, event_args, context);
2260       str_deallocate(event_args);
2261     }
2262 
2263     str_deallocate(filename);
2264   }
2265   while ( (filename = str_read_token(command_line)) );
2266 
2267   ret = send_message_with_args(200,context,"File(s) wiped");
2268 
2269   return 0;
2270 }
2271 
2272 /********************* do_site *****************************/
2273 
do_site(wzd_string_t * command,wzd_string_t * command_line,wzd_context_t * context)2274 int do_site(wzd_string_t *command, wzd_string_t *command_line, wzd_context_t * context)
2275 {
2276   char buffer[4096];
2277   int ret=0;
2278   wzd_hook_reply_t hook_reply;
2279   int first_reply;
2280   const char *s_token;
2281 
2282   if (!command || !command_line) {
2283     ret = send_message_with_args(501,context,"SITE command failed");
2284     return 1;
2285   }
2286 
2287   /* check general site permission */
2288 #ifdef DEBUG
2289   if (strlen(str_tochar(command))>255) {
2290     out_err(LEVEL_HIGH,"*** WARNING *** permissions name too long > 255 - truncated : '%s'\n",str_tochar(command));
2291   }
2292 #endif
2293 
2294   {
2295     wzd_command_t * command_real;
2296 
2297     command_real = commands_find(mainConfig->commands_list,command);
2298     /* disabled because this breaks custom site commands */
2299 #if 0
2300     if (!command_real) {
2301       ret = send_message_with_args(501,context,"Permission not found for site command");
2302       return 1;
2303     }
2304 #endif
2305     if (command_real && commands_check_permission(command_real,context)) {
2306       ret = send_message_with_args(501,context,"Permission Denied");
2307       return 1;
2308     }
2309   }
2310 
2311   s_token = str_tochar(command);
2312 
2313 #if 0
2314   fct = site_find(s_token);
2315 
2316   if (fct)
2317   {
2318     tok_command = str_tok(command_line,"\r\n");
2319     ret = (*fct)((char*)str_tochar(tok_command),context);
2320     str_deallocate(tok_command);
2321     return ret;
2322   }
2323 #endif
2324 
2325 /******************** CLOSE *********************/
2326   if (strcmp(s_token,"site_close")==0) {
2327     mainConfig->site_closed = 1;
2328     ret = send_message_with_args(250,context,"SITE: ","server is now closed");
2329     return 0;
2330   } else
2331 /******************** REOPEN ********************/
2332   if (strcmp(s_token,"site_reopen")==0) {
2333     mainConfig->site_closed = 0;
2334     ret = send_message_with_args(250,context,"SITE: ","server is now opened");
2335     return 0;
2336   } else
2337 /******************* UPTIME *********************/
2338   if (strcmp(s_token,"site_uptime")==0) {
2339     time_t t;
2340     time(&t);
2341     t = t - mainConfig->server_start;
2342     snprintf(buffer,sizeof(buffer),"Uptime: %s",time_to_str(t));
2343     ret = send_message_with_args(200,context,buffer);
2344     return 0;
2345   }
2346 /******************* SHUTDOWN *******************/
2347 #ifndef WZD_MULTITHREAD
2348   else if (strcmp(s_token,"site_shutdown")==0) {
2349     mainConfig->serverstop = 1;
2350     ret = send_message_with_args(250,context,"SITE: ","server will shutdown after you logout");
2351     context->exitclient = 1;
2352     return 0;
2353   }
2354 #endif /* WZD_MULTIPROCESS */
2355 #ifdef WZD_MULTITHREAD
2356   else if (strcmp(s_token,"site_shutdown")==0) {
2357     ret = send_message_with_args(250,context,"SITE: ","server will shutdown NOW");
2358     mainConfig->serverstop = 1;
2359     return 0;
2360   }
2361 #endif /* WZD_MULTITHREAD */
2362 
2363   hook_reply = EVENT_IGNORED;
2364   first_reply = 1;
2365 
2366 #if 0
2367   FORALL_HOOKS(EVENT_SITE)
2368     typedef wzd_hook_reply_t (*site_hook)(unsigned long, wzd_context_t *, const char*,const char *);
2369     if (hook->hook) {
2370       hook_reply = (*(site_hook)hook->hook)(EVENT_SITE,context,s_token,str_tochar(command_line));
2371       /** \todo implement and use constants: HANDLED, NEXT, ERROR or something like .. */
2372       if (hook_reply != EVENT_IGNORED && hook_reply != EVENT_NEXT) break;
2373     }
2374     /* custom site commands */
2375     if (hook->opt && hook->external_command && strcasecmp(hook->opt,s_token)==0) {
2376       if (first_reply) { send_message_raw("200-\r\n",context); first_reply=0; }
2377       ret = hook_call_custom(context, hook, 200, (char*)str_tochar(command_line));
2378       if (!ret) {
2379         ret = send_message_with_args(200,context,"SITE command ok");
2380       } else {
2381         ret = send_message_with_args(200,context,"SITE command failed");
2382       }
2383       return 0; /* there can be only one site command ! */
2384     }
2385   END_FORALL_HOOKS
2386 #endif /* 0 */
2387 
2388   switch (hook_reply) {
2389   case EVENT_ERR:
2390     /* we do not know how to reply .. trying 200 */
2391     out_log(LEVEL_INFO, "Someone reported errors for site command %s\n", s_token);
2392     ret = send_message_with_args(200,context,"SITE command failed");
2393     break;
2394   case EVENT_NEXT:
2395     /* we do not know how to reply .. trying 200 */
2396     out_log(LEVEL_INFO, "Received only EVENT_NEXT for site command %s\n", s_token);
2397     out_log(LEVEL_INFO, "The last handler should send EVENT_CATCHED\n");
2398     ret = send_message_with_args(200,context,"SITE command executed (with warnings)");
2399     break;
2400   case EVENT_IGNORED:
2401     ret = send_message_with_args(250,context,"SITE ","command unknown");
2402     break;
2403   case EVENT_HANDLED:
2404     break;
2405   }
2406 
2407   return 0;
2408 }
2409 
2410 
2411