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 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 
31 #include "wzd_all.h"
32 
33 #ifndef WZD_USE_PCH
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h> /* isspace */
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <errno.h>
42 
43 #ifdef WIN32
44 #include <winsock2.h>
45 #include <direct.h>
46 #include <io.h>
47 #else
48 #include <unistd.h>
49 #include <sys/ioctl.h>
50 
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
55 
56 #include <net/if.h>
57 #include <netdb.h>
58 
59 #include <dirent.h>
60 
61 #include <pthread.h>
62 #endif
63 
64 #include <fcntl.h>
65 #include <time.h>
66 /* for intel compiler */
67 #ifdef __INTEL_COMPILER
68 # define __SWORD_TYPE   int
69 #endif /* __INTEL_COMPILER */
70 
71 #ifndef _MSC_VER
72 #include <sys/param.h>
73 
74 #if HAVE_SYS_STATFS_H
75 #include <sys/statfs.h> /* statfs */
76 #endif
77 
78 #if HAVE_SYS_STATVFS_H
79 #include <sys/statvfs.h> /* statfs */
80 #endif
81 
82 #if HAVE_SYS_VFS_H
83 #include <sys/vfs.h> /* statfs */
84 #endif
85 
86 #if HAVE_SYS_MOUNT_H
87 #include <sys/mount.h> /* statfs */
88 #endif
89 
90 #endif /* _MSC_VER */
91 
92 #ifndef HAVE_STRTOK_R
93 # include "libwzd-base/wzd_strtok_r.h"
94 #endif
95 
96 
97 #include "wzd_structs.h"
98 
99 #include "wzd_ClientThread.h"
100 #include "wzd_fs.h"
101 #include "wzd_libmain.h"
102 #include "wzd_log.h"
103 #include "wzd_misc.h"
104 #include "wzd_messages.h"
105 #include "wzd_mutex.h"
106 #include "wzd_user.h"
107 
108 #endif /* WZD_USE_PCH */
109 
110 
111 #if defined(WIN32) || defined(BSD) || defined(__sun__)
112 #define LONGBITS  0x20
113 #else
114 /* needed  for LONGBITS */
115 #include <values.h>
116 #endif
117 
118 
119 #include "wzd_debug.h"
120 
121 
122 /** Compute the hash value for the given string.  The algorithm
123  * is taken from [Aho,Sethi,Ullman], modified to reduce the number of
124  * collisions for short strings with very varied bit patterns.
125  * See http://www.clisp.org/haible/hashfunc.html
126  */
compute_hashval(const void * key,size_t keylen)127 unsigned long compute_hashval (const void *key, size_t keylen)
128 {
129   size_t cnt;
130   unsigned long int hval;
131 
132   cnt = 0;
133   hval = keylen;
134   while (cnt < keylen)
135   {
136     hval = (hval << 9) | (hval >> (LONGBITS - 9));
137     hval += (unsigned long int) *(((char *) key) + cnt++);
138   }
139   return hval != 0 ? hval : ~((unsigned long) 0);
140 }
141 
142 #define PRIME 211
hash_pjw(const char * s)143 int hash_pjw(const char *s)
144 {
145   const char *p;
146   unsigned h=0, g;
147 
148   for (p=s; *p!='\0'; p++)
149   {
150     h = (h << 4) + (*p);
151     if ( (g= h & 0xF0000000) != 0) {
152       h = h ^ (g >> 24);
153       h = h ^ g;
154     }
155   }
156   return h % PRIME;
157 }
158 
time_to_str(time_t time)159 char *time_to_str(time_t time)
160 {
161 
162   static char workstr[100];
163   unsigned short int days=(time/86400),hours,mins,secs;
164   hours=((time-(days*86400))/3600);
165   mins=((time-(days*86400)-(hours*3600))/60);
166   secs = time % 60;
167 
168   if (days) {
169     snprintf(workstr,sizeof(workstr),"%dd %dh %dm %ds",days,hours,mins,secs);
170   }
171   else {
172     if (hours) {
173       snprintf(workstr,sizeof(workstr),"%dh %dm %ds",hours,mins,secs);
174     }
175     else {
176       if (mins) {
177         snprintf(workstr,sizeof(workstr),"%dm %ds",mins,secs);
178       }
179       else {
180         if (secs) {
181           snprintf(workstr,sizeof(workstr),"%ds",secs);
182         }
183         else {
184           snprintf(workstr,sizeof(workstr),"0 seconds");
185         }
186       }
187     }
188   }
189 
190 
191   return(workstr);
192 }
193 
194 /** \todo replace this with: char units[]="bkMGT";
195  * char * ptr = units; -> while (*value > 1024.f) { ... *unit = *ptr++; }
196  */
bytes_to_unit(float * value,char * unit)197 int bytes_to_unit(float *value, char *unit)
198 {
199   *unit='b';
200   if (*value>1024.f) {
201     *value /= 1024.f;
202     *unit = 'k';
203   }
204   if (*value>1024.f) {
205     *value /= 1024.f;
206     *unit = 'M';
207   }
208   if (*value>1024.f) {
209     *value /= 1024.f;
210     *unit = 'G';
211   }
212   if (*value>1024.f) {
213     *value /= 1024.f;
214     *unit = 'T';
215   }
216   return 0;
217 }
218 
chop(char * s)219 void chop(char *s)
220 {
221   char *r;
222 
223   if ((r=(char*) strchr(s,'\r')))
224     *r = '\0';
225   if ((r=(char*) strchr(s,'\n')))
226     *r = '\0';
227 }
228 
split_filename(const char * filename,char * path,char * stripped_filename,int pathlen,unsigned int filelen)229 int split_filename(const char *filename, char *path, char *stripped_filename,
230     int pathlen, unsigned int filelen)
231 {
232   char *ptr;
233 
234   ptr = strrchr(filename,'/');
235   if (!ptr) { /* no dir */
236     if (path && pathlen>0) path[0] = '\0';
237     if (stripped_filename && filelen>strlen(filename)) strncpy(stripped_filename,filename,filelen);
238   } else {
239     if (path && pathlen>(ptr-filename))
240       { memcpy(path,filename,ptr-filename); path[ptr-filename]='\0'; }
241     if (stripped_filename && filelen>(strlen(filename)-(ptr-filename)))
242       { strncpy(stripped_filename,ptr+1,filelen); }
243   }
244 
245   return 0;
246 }
247 
248 /** returns system ip on specifed interface (e.g eth0) */
get_system_ip(const char * itface,struct in_addr * ina)249 int get_system_ip(const char * itface, struct in_addr * ina)
250 {
251 #if defined(_MSC_VER)
252   char buffer_name[256];
253   struct hostent * host;
254 
255   if (gethostname(buffer_name,sizeof(buffer_name))) return -1;
256 
257   if ( !(host = gethostbyname(buffer_name)) ) return -1;
258 
259   memcpy(ina,host->h_addr,4);
260   out_log(LEVEL_FLOOD,"IP: %s\n",inet_ntoa(*ina));
261 
262   return 0;
263 #endif
264 #if defined(WIN32) || defined(__sun__)
265   return -1;
266 #else
267 /*  struct in_addr *ina = void_in;*/
268   struct ifreq ifr;
269   int s;
270 
271   if ( (s = socket(PF_INET,SOCK_STREAM,0))<0 ) {
272     out_log(LEVEL_CRITICAL,"Can't create new socket (%s:%d)\n",__FILE__,__LINE__);
273     ina->s_addr = 0;
274     return -1;
275   }
276   memset(&ifr,0,sizeof(ifr));
277   strncpy(ifr.ifr_name,itface,sizeof(ifr.ifr_name));
278 
279   if (ioctl(s,SIOCGIFADDR,&ifr)<0) {
280     out_log(LEVEL_CRITICAL,"Can't get my ip (ioctl %s:%d)\n",__FILE__,__LINE__);
281     ina->s_addr = 0;
282     return -1;
283   }
284 
285   memcpy(ina,ifr.ifr_addr.sa_data+2,4);
286   out_log(LEVEL_FLOOD,"IP: %s\n",inet_ntoa(*ina));
287 
288   close(s);
289   return 0;
290 #endif /* BSD */
291 }
292 
293 /** returns info on device containing dir/file */
get_device_info(const char * file,long * f_type,long * f_bsize,long * f_blocks,long * f_free)294 int get_device_info(const char *file, long * f_type, long * f_bsize, long * f_blocks, long *f_free)
295 {
296 #ifdef HAVE_STATVFS
297   struct statvfs fs;
298 
299   if (statvfs(file,&fs)==0) {
300     if (f_bsize) *f_bsize = fs.f_bsize;
301     if (f_type) *f_type = -1; /* not filled in statvfs */
302     if (f_blocks) *f_blocks = fs.f_blocks;
303     if (f_free) *f_free = fs.f_bavail; /* f_bavail: free blocks avail to non-superuser */
304 
305     return 0;
306   }
307 
308 #else /* HAVE_STATVFS */
309 
310 #ifndef WIN32
311   struct statfs fs;
312 
313   if (statfs(file,&fs)==0) {
314     if (f_bsize) *f_bsize = fs.f_bsize;
315 #ifndef BSD
316     if (f_type) *f_type = fs.f_type;
317 #endif /* BSD */
318     if (f_blocks) *f_blocks = fs.f_blocks;
319     if (f_free) *f_free = fs.f_bavail; /* f_bavail: free blocks avail to non-superuser */
320     return 0;
321   }
322 #else
323   struct _diskfree_t df;
324   unsigned int err;
325   unsigned int drive;
326 
327   if (!file || strlen(file)<2) return -1;
328 
329   if (file[1] == ':') {
330     if (file[0] >= 'A' && file[0] <= 'Z')
331       drive = file[0] - 'A' + 1;
332     else
333       drive = file[0] - 'a' + 1;
334   } else {
335     drive = _getdrive();
336   }
337   if (drive > 26) return -1;
338   err = _getdiskfree(drive, &df);
339   if (!err) {
340     if (f_free) *f_free = df.avail_clusters * df.sectors_per_cluster;
341     if (f_bsize) *f_bsize = df.bytes_per_sector;
342     if (f_blocks) *f_blocks = df.total_clusters * df.sectors_per_cluster;
343     return 0;
344   }
345 #endif
346 
347 #endif /* HAVE_STATVFS */
348   return -1;
349 }
350 
351 /** internal fct, rename files by copying data */
_int_rename(const char * src,const char * dst)352 static int _int_rename(const char * src, const char *dst)
353 {
354   fs_filestat_t s;
355   int ret;
356 
357   if (fs_file_lstat(src,&s)) return -1;
358 
359   if (S_ISDIR(s.mode)) {
360     char buf_src[2048];
361     char buf_dst[2048];
362     unsigned int length_src=2048;
363     unsigned int length_dst=2048;
364     char * ptr_src, * ptr_dst;
365     const char *filename;
366     fs_dir_t * dir;
367     fs_fileinfo_t * finfo;
368 
369     ret = mkdir(dst,s.mode & 0xffff);
370     ret = chmod(dst,s.mode & 0xffff);
371     memset(buf_src,0,2048);
372     memset(buf_dst,0,2048);
373     strncpy(buf_src,src,length_src-1); /* FIXME check ret */
374     strncpy(buf_dst,dst,length_dst-1); /* FIXME check ret */
375     length_src -= strlen(buf_src);
376     length_dst -= strlen(buf_dst);
377     ptr_src = buf_src + strlen(buf_src);
378     ptr_dst = buf_dst + strlen(buf_dst);
379     *ptr_src++ = '/'; /* no need to add '\0', the memset had already filled buffer with 0 */
380     *ptr_dst++ = '/';
381     /* TODO read dir and recurse function for all entries */
382     if ( fs_dir_open(src,&dir) ) return -1;
383 
384     while ( !fs_dir_read(dir,&finfo) ) {
385       filename = fs_fileinfo_getname(finfo);
386 
387       if (filename[0]=='.') {
388         if (strcmp(filename,".")==0 ||
389             strcmp(filename,"..")==0)
390           continue;
391       }
392 
393       strncpy(ptr_src,filename,length_src-1); /* FIXME check ret */
394       strncpy(ptr_dst,filename,length_dst-1); /* FIXME check ret */
395       ret = _int_rename(buf_src,buf_dst); /* FIXME check ret */
396       *ptr_src = '\0';
397       *ptr_dst = '\0';
398     }
399 
400     fs_dir_close(dir);
401     rmdir(src);
402   } else
403   if (S_ISLNK(s.mode)) {
404     char buf[WZD_MAX_PATH+1];
405     memset(buf,0,sizeof(buf));
406     ret = readlink(src,buf,WZD_MAX_PATH);
407     /* FIXME this will work iff the symlink is _relative_ to src
408      * otherwise we need to re-build a path from buf
409      */
410     ret = symlink(buf,dst);
411     ret = chmod(dst,s.mode & 0xffff);
412     unlink(src);
413   } else
414   if (S_ISREG(s.mode)) {
415     char buffer[32768];
416     int fd_from, fd_to;
417     size_t sz;
418     int mode;
419 
420 #ifdef WIN32
421     /* since we can't move directories on windows, we can get here while
422      * trying to move files on the same device. So we just try to rename them
423      * before copy/remove, since renaming is much faster !
424      */
425     ret = rename(src,dst);
426     if (ret == 0) return 0;
427     mode = _O_BINARY;
428 #endif
429 
430     /* FIXME XXX would it be wise to test functions return values ? :-P */
431     fd_from = open(src,O_RDONLY | mode);
432     if (fd_from < 0) return -1;
433     fd_to = open(dst,O_CREAT | O_WRONLY | mode,0666); /** \fixme this will overwite existing files */
434     if (fd_to < 0) { close(fd_from); return -1; }
435     do {
436       sz = read(fd_from,buffer,sizeof(buffer));
437       if (sz > 0) {
438         ret = write(fd_to,buffer,sz);
439       }
440     } while (sz > 0);
441     close(fd_from);
442     close(fd_to);
443     unlink(src);
444   }
445 
446   return 0;
447 }
448 
449 /** renames file/dir, if on different fs then moves recursively */
safe_rename(const char * src,const char * dst)450 int safe_rename(const char *src, const char *dst)
451 {
452   int ret;
453 
454 #ifdef WIN32
455   /* Quote from MSDN documentation:
456    * However, you cannot use rename to move a directory. Directories can be renamed, but not moved.
457    * I'm just sick of all this crap
458    */
459   {
460     fs_filestat_t s;
461 
462     if (fs_file_lstat(src,&s)) return -1;
463 
464     /** \todo kill all users in this path, windows does not allow moving an opened file */
465     if (S_ISDIR(s.mode)) {
466       ret = _int_rename(src,dst);
467       return ret;
468     }
469   }
470 #endif
471 
472   ret = rename(src,dst);
473   if (ret == -1 && errno == EXDEV)
474   {
475     out_err(LEVEL_INFO,"Cross device move\n");
476     ret = _int_rename(src,dst);
477   }
478 
479   return ret;
480 }
481 
482 /** returns 1 if file is hidden: perm,hidden,race_info file, etc */
is_hidden_file(const char * filename)483 int is_hidden_file(const char *filename)
484 {
485   const char *ptr;
486 
487   ptr = strrchr(filename,'/');
488   if (ptr) {
489     if (strcasecmp(ptr+1,HARD_PERMFILE)==0) return 1;
490     if (*(ptr+1)=='.' && CFG_GET_OPTION(mainConfig,CFG_OPT_HIDE_DOTTED_FILES)) return 1;
491   } else {
492     if (strcasecmp(filename,HARD_PERMFILE)==0) return 1;
493     if (filename[0]=='.' && CFG_GET_OPTION(mainConfig,CFG_OPT_HIDE_DOTTED_FILES)) return 1;
494   }
495   return 0;
496 }
497 
498 /** \return 1 if file is perm file
499  * \deprecated Use \ref is_hidden_file
500  */
is_perm_file(const char * filename)501 int is_perm_file(const char *filename)
502 {
503   const char *endfile;
504 
505   if (filename) {
506     endfile = filename + (strlen(filename) - strlen(HARD_PERMFILE));
507     if (strlen(filename)>strlen(HARD_PERMFILE)) {
508       if (strcasecmp(HARD_PERMFILE,endfile)==0)
509         return 1;
510     }
511   }
512   return 0;
513 }
514 
515 /** get file last change time */
get_file_ctime(const char * file)516 time_t get_file_ctime(const char *file)
517 {
518   fs_filestat_t s;
519   if ( fs_file_stat(file,&s) < 0 ) return (time_t)-1;
520   return s.ctime;
521 }
522 
lget_file_ctime(int fd)523 time_t lget_file_ctime(int fd)
524 {
525   fs_filestat_t s;
526   if ( fs_file_fstat(fd,&s) < 0 ) return (time_t)-1;
527   return s.ctime;
528 }
529 
530 /** \brief Checks server status
531  */
server_diagnose(void)532 int server_diagnose(void)
533 {
534   if (!mainConfig) return -1;
535   if (mainConfig->serverstop != 0) return -1;
536 
537   /** \todo implement more checks */
538 
539   return 0;
540 }
541 
542 
543 #define WORK_BUF_LEN	8192
544 
545 /** \brief allocate buffer big enough to format arguments with printf
546  *
547  * Returned string must be freed with \ref wzd_free
548  */
safe_vsnprintf(const char * format,va_list ap)549 char * safe_vsnprintf(const char *format, va_list ap)
550 {
551   int size = WORK_BUF_LEN;
552   char * buffer = wzd_malloc(size);
553   int result;
554   va_list ap2;
555 
556   /** vsnprintf modifies its last argument on some archs, we have to
557    * work on a copy of the va_list
558    */
559 #ifdef va_copy
560   va_copy(ap2,ap);
561 #else
562   memcpy (&ap2,&ap, sizeof(va_list));
563 #endif
564 
565   result = vsnprintf(buffer, size, format, ap2);
566   if (result >= size)
567   {
568     buffer = wzd_realloc(buffer, result+1);
569     va_end(ap2);
570 #ifdef va_copy
571     va_copy(ap2,ap);
572 #else
573     memcpy (&ap2,&ap, sizeof(va_list));
574 #endif
575     result = vsnprintf(buffer, result+1, format, ap2);
576   }
577   va_end(ap2);
578 
579   return buffer;
580 }
581 
582 /** \brief Formats the message if multiline, e.g 220-hello\\r\\n220 End
583  *
584  * if code is negative, the last line will NOT be formatted as the end
585  * of a normal ftp reply
586  */
v_format_message(wzd_context_t * context,int code,va_list argptr)587 wzd_string_t * v_format_message(wzd_context_t * context, int code, va_list argptr)
588 {
589   wzd_string_t * str = NULL;
590   wzd_user_t * user;
591   wzd_group_t * group;
592   char * cookies_buf, * work_buf;
593   char * token, * ptr;
594   const char * msg;
595   int must_free;
596   u16_t is_terminated=1;
597   int ret;
598 
599   if (!context) return NULL;
600 
601   if (code < 0) {
602     is_terminated = 0;
603     code = (-code);
604   }
605 
606   user = GetUserByID(context->userid);
607   group = user ? GetGroupByID(user->groups[0]) : NULL;
608 
609   msg = getMessage(code,&must_free);
610 
611   /* first, replace cookies */
612   cookies_buf = wzd_malloc(WORK_BUF_LEN+1);
613   ret = cookie_parse_buffer(msg, user, group, context, cookies_buf, WORK_BUF_LEN); /** \todo use wzd_string_t here */
614 
615   /* then format message */
616   work_buf = safe_vsnprintf(cookies_buf,argptr);
617   wzd_free(cookies_buf);
618 
619   /* we don't need msg anymore */
620   if (must_free) wzd_free( (char*) msg );
621 
622   str = str_allocate();
623 
624   ptr = work_buf;
625   token = strtok_r(work_buf, "\r\n", &ptr);
626 
627   if (!token || strcmp(ptr,"\n")==0) { /* easy, one line */
628     if (is_terminated)
629       ret = str_sprintf(str,"%d %s\r\n",code,work_buf);
630     else
631       ret = str_sprintf(str,"%d-%s\r\n",code,work_buf);
632     if (ret < 0) goto lbl_v_format_message;
633   }
634   else { /* funnier, multiline */
635 
636     /* first line begins with 123- */
637     str_sprintf(str,"%d-%s\r\n",code,token);
638 
639     while ( (token=strtok_r(NULL, "\r\n", &ptr)) ) {
640       if (strcmp(ptr,"\n")==0) { /* last line */
641         wzd_string_t * end;
642         end = str_allocate();
643         if (is_terminated)
644           ret = str_sprintf(end,"%d %s\r\n",code,token);
645         else
646           ret = str_sprintf(end,"%d-%s\r\n",code,token);
647         str_append(str,str_tochar(end));
648         str_deallocate(end);
649         break;
650       }
651       str_append(str,token);
652       str_append(str,"\r\n");
653     }
654 
655   }
656 
657   wzd_free(work_buf);
658   return str;
659 
660 lbl_v_format_message:
661   wzd_free(work_buf);
662   str_deallocate(str);
663   return NULL;
664 }
665 
format_message(wzd_context_t * context,int code,...)666 wzd_string_t * format_message(wzd_context_t * context, int code, ...)
667 {
668   va_list argptr;
669   wzd_string_t * str;
670 
671   va_start(argptr,code); /* note: ansi compatible version of va_start */
672 
673   str = v_format_message(context, code, argptr);
674   va_end (argptr);
675 
676   return str;
677 }
678 
679 
680 
681 /************* BANDWIDTH LIMITATION *********/
682 
get_bandwidth(unsigned long * dl,unsigned long * ul)683 unsigned long get_bandwidth(unsigned long *dl, unsigned long *ul)
684 {
685   unsigned long ul_bandwidth=0, dl_bandwidth=0;
686   unsigned int id;
687   ListElmt * elmnt;
688   wzd_user_t * user;
689   wzd_context_t * context;
690 
691   for (elmnt=list_head(context_list); elmnt!=NULL; elmnt=list_next(elmnt)) {
692     context = list_data(elmnt);
693     if (context && context->magic == CONTEXT_MAGIC) {
694       id = context->userid;
695       user = GetUserByID(id);
696       if (context->current_action.token==TOK_RETR) {
697         dl_bandwidth += (unsigned long)context->current_dl_limiter.current_speed;
698       }
699       if (context->current_action.token==TOK_STOR) {
700         ul_bandwidth += (unsigned long)context->current_ul_limiter.current_speed;
701       }
702     } /* if CONTEXT_MAGIC */
703   } /* forall contexts */
704 
705   if (dl) *dl = dl_bandwidth;
706   if (ul) *ul = ul_bandwidth;
707 
708   return (dl_bandwidth+ul_bandwidth);
709 }
710 
711 
limiter_new(int maxspeed)712 wzd_bw_limiter * limiter_new(int maxspeed)
713 {
714   wzd_bw_limiter *l_new;
715 #ifndef WIN32
716   struct timezone tz;
717 #endif
718 
719   l_new = malloc(sizeof(wzd_bw_limiter));
720   l_new->maxspeed = maxspeed;
721   l_new->bytes_transfered = 0;
722 #ifndef WIN32
723   gettimeofday(&(l_new->current_time),&tz);
724 #else
725   _ftime(&(l_new->current_time));
726 #endif
727 
728   return l_new;
729 }
730 
limiter_add_bytes(wzd_bw_limiter * l,wzd_mutex_t * mutex,int byte_count,UNUSED int force_check)731 void limiter_add_bytes(wzd_bw_limiter *l, wzd_mutex_t * mutex, int byte_count, UNUSED int force_check)
732 {
733 #ifndef WIN32 /* FIXME VISUAL */
734   struct timeval tv;
735   struct timezone tz;
736 #else
737   struct _timeb tb;
738 #endif
739   unsigned long elapsed;
740   unsigned long pause_time;
741 /*  double rate_ratio;*/
742   unsigned int bw_rate;
743 
744   if (!l) return;
745 /*  if (l->maxspeed == 0) return;*/
746 
747 wzd_mutex_lock(mutex);
748   l->bytes_transfered += byte_count;
749 wzd_mutex_unlock(mutex);
750 
751   /* if at least 1 second of data is downloaded, assess the situation
752    * and determine how much time to wait */
753 /*  if ( (l->bytes_transfered >= l->maxspeed) || force_check )
754   {*/
755 #ifndef WIN32
756     gettimeofday( &tv, &tz );
757     elapsed = (tv.tv_sec - l->current_time.tv_sec) * 1000000;
758     elapsed += (tv.tv_usec - l->current_time.tv_usec);
759 #else
760     _ftime(&tb);
761     elapsed = (tb.time - l->current_time.time) * 1000000;
762     elapsed += (tb.millitm - l->current_time.millitm) * 1000;
763 #endif
764     if (elapsed==0) elapsed=1;
765 /*    bw_rate = (unsigned int)((double)l->bytes_transfered / elapsed);*/
766     l->current_speed = (float)( (l->bytes_transfered * 1000000.f) / elapsed);
767     bw_rate = (unsigned int)l->current_speed;
768 /*  }*/
769   if (l->maxspeed == 0 || bw_rate <= l->maxspeed) {
770     return;
771   }
772 /*out_err(LEVEL_INFO,"received: %d\n",l->bytes_transfered);*/
773 /*out_err(LEVEL_INFO,"speed: %d max:%d\n",bw_rate,l->maxspeed);*/
774 /*  rate_ratio = (double)bw_rate / (double)l->maxspeed;*/
775 /*  pause_time = (rate_ratio - (double)1)*elapsed;*/
776   pause_time = (bw_rate-l->maxspeed) * (elapsed / l->maxspeed);
777 #ifndef WIN32
778   usleep (pause_time);
779 #else
780   Sleep((unsigned long)(pause_time / (double)1000));
781 #endif
782 /*  gettimeofday( &tv, &tz );
783   l->current_time.tv_sec = tv.tv_sec;
784   l->current_time.tv_usec = tv.tv_usec;
785   l->bytes_transfered = 0;*/
786 }
787 
limiter_free(wzd_bw_limiter * l)788 void limiter_free(wzd_bw_limiter *l)
789 {
790   free(l);
791 }
792 
793 /** \brief Read file, replace cookies and send it to client
794  *
795  * header (200-) MUST have been sent, and end (200 ) is NOT sent)
796  */
print_file(const char * filename,int code,void * void_context)797 int print_file(const char *filename, int code, void * void_context)
798 {
799   wzd_context_t * context = void_context;
800   void * param;
801   char complete_buffer[1024];
802   char * buffer = complete_buffer + 4;
803   int ret;
804   FILE *fp;
805 
806   if (strlen(filename)==0) {
807     out_log(LEVEL_HIGH,"Trying to print file (null) with code %d\n",code);
808     return 1;
809   }
810   fp = fopen(filename,"r");
811   if (!fp) {
812     out_log(LEVEL_HIGH,"Problem opening file %s (code %d)\n",filename,code);
813     return 1;
814   }
815 
816   snprintf(complete_buffer,5,"%3d-",code);
817   if ( (fgets(buffer,1018,fp))==NULL ) {
818     out_log(LEVEL_HIGH,"File %s is empty (code %d)\n",filename,code);
819     return 1;
820   }
821 
822   param = NULL;
823   do {
824     ret = cookie_parse_buffer(buffer,NULL,NULL,context,NULL,0); /* TODO test ret */
825   /* XXX FIXME TODO */
826 /*    out_log(LEVEL_HIGH,"READ: %s\n",complete_buffer);*/
827     send_message_raw(complete_buffer,context);
828   } while ( (fgets(buffer,1018,fp)) != NULL);
829 
830   return 0;
831 }
832 
833 /** used to translate text to binary word for rights */
right_text2word(const char * text)834 unsigned long right_text2word(const char * text)
835 {
836   unsigned long word=0;
837   const char * ptr = text;
838 
839   do {
840     while ( (*ptr)==' ' || (*ptr)=='\t' || (*ptr)=='+' || (*ptr)=='|' ) {
841       ptr++;
842     }
843     if (*ptr == '\0' || *ptr == '\r' || *ptr=='\n') break;
844 
845     if (strncasecmp(ptr,"RIGHT_LIST",strlen("RIGHT_LIST"))==0) {
846      word += RIGHT_LIST;
847      ptr += strlen("RIGHT_LIST");
848     } else
849     if (strncasecmp(ptr,"RIGHT_RETR",strlen("RIGHT_RETR"))==0) {
850      word += RIGHT_RETR;
851      ptr += strlen("RIGHT_RETR");
852     } else
853     if (strncasecmp(ptr,"RIGHT_STOR",strlen("RIGHT_STOR"))==0) {
854      word += RIGHT_STOR;
855      ptr += strlen("RIGHT_STOR");
856     } else
857     if (strncasecmp(ptr,"RIGHT_CWD",strlen("RIGHT_CWD"))==0) {
858      word += RIGHT_CWD;
859      ptr += strlen("RIGHT_CWD");
860     } else
861     if (strncasecmp(ptr,"RIGHT_RNFR",strlen("RIGHT_RNFR"))==0) {
862      word += RIGHT_RNFR;
863      ptr += strlen("RIGHT_RNFR");
864     } else
865     {
866       return 0;
867     }
868   } while (*ptr);
869 
870   return word;
871 }
872 
873 
874 /** \brief Compare strings \a src and \a dst
875  *
876  * dst can be composed of wildcards
877  *
878  * \return 1 if string matches, otherwise 0
879  */
my_str_compare(const char * src,const char * dst)880 int my_str_compare(const char * src, const char *dst)
881 {
882   const char * ptr_src;
883   const char * ptr_dst;
884   char c;
885 
886   ptr_src = src;
887   ptr_dst = dst;
888 
889   while ((c = *ptr_src)) {
890     if (*ptr_dst=='*') { /* wildcard * */
891       if (*(ptr_dst+1)=='\0') return 1; /* terminated with a *, ok */
892       ptr_dst++;
893       c = *ptr_dst;
894       while (*ptr_src && c!=*ptr_src)
895         ptr_src++;
896       if (!*ptr_src) break; /* try next ip */
897       continue;
898     }
899     if (*ptr_dst=='?') { /* wildcard ?, match one char and continue */
900       ptr_src++;
901       ptr_dst++;
902       continue;
903     }
904     if (*ptr_dst!=c) break; /* try next ip */
905     ptr_dst++;
906     ptr_src++;
907   }
908 
909   /* test if checking was complete */
910   if ( (*ptr_dst == '\0' && (*ptr_src == '\0')) ||
911        (*ptr_dst=='*' && *(ptr_dst+1)=='\0') ) return 1;
912 
913   return 0;
914 }
915 
916 /* lower only characters in A-Z ! */
ascii_lower(char * s,unsigned int length)917 void ascii_lower(char * s, unsigned int length)
918 {
919   register unsigned int i=0;
920   while (i<length) {
921     if (s[i] >= 'A' && s[i] <= 'Z') {
922       s[i] |= 0x20;
923     }
924     i++;
925   }
926 }
927 
928 /** \brief Read next token from input string.
929  * \return a pointer to the next token, or NULL if not found, or if there is
930  * only whitespaces, or if quotes are unbalanced
931  *
932  * Read next token separated by a whitespace, except if string begins
933  * with a ' or ", in this case it searches the matching character.
934  *
935  * \note input string is modified as a \\0 is written.
936  */
read_token(char * s,char ** endptr)937 char * read_token(char *s, char **endptr)
938 {
939   char *tok, c;
940   char sep[2];
941 
942   if (s == NULL && (s = *endptr) == NULL)
943   {
944     return NULL;
945   }
946 
947   /* skip leading spaces */
948   while ( (c = *s) && isspace(c) ) s++;
949   if (*s == '\0') /* only whitespaces */
950   { *endptr = NULL; return NULL; }
951 
952   /* search for any whitespace or quote */
953   tok = strpbrk(s, " \t\r\n\"'");
954 
955   if (!tok) {
956     /* nothing, we return string */
957     *endptr = NULL;
958     return s;
959   }
960 
961   /* the first char is a quote ? */
962   if (*tok == '"' || *tok == '\'') {
963     sep[0] = *tok;
964     sep[1] = '\0';
965     if (!strchr(tok+1,*tok)) { /* unbalanced quotes */
966       *endptr = NULL;
967       return NULL;
968     }
969     /** \bug we can't have escaped characters */
970     return strtok_r(tok, sep, endptr);
971   }
972 
973   /* normal case, we search a whitespace */
974   return strtok_r(s, " \t\r\n", endptr);
975 }
976 
977 /* replace all \ with / and lower string */
win_normalize(char * s,unsigned int length,unsigned int lower)978 void win_normalize(char * s, unsigned int length, unsigned int lower)
979 {
980   register unsigned int i=0;
981   while (i<length) {
982     if (lower && (s[i] >= 'A' && s[i] <= 'Z')) {
983       s[i] |= 0x20;
984     }
985     if (s[i] == '\\') s[i] = '/';
986     i++;
987   }
988 }
989 
990 
991 
992 /* \return 0 if ok, -1 if error, 1 if trying to kill myself */
kill_child_signal(unsigned long pid,wzd_context_t * context)993 int kill_child_signal(unsigned long pid, wzd_context_t * context)
994 {
995   ListElmt * elmnt;
996   wzd_context_t * loop_context;
997   int found=0;
998 #ifndef WIN32
999   int ret;
1000 #endif
1001 
1002   /* preliminary check: i can't kill myself */
1003   if (context != NULL && pid==context->pid_child) return 1;
1004 
1005   /* checks that pid is really one of the users */
1006   for (elmnt=list_head(context_list); elmnt!=NULL; elmnt=list_next(elmnt))
1007   {
1008     loop_context = list_data(elmnt);
1009     if (loop_context && loop_context->magic == CONTEXT_MAGIC && loop_context->pid_child == pid) { found = 1; break; }
1010   }
1011   if (!found) return -1;
1012 
1013 #ifdef WIN32
1014   /* \todo XXX FIXME remove/fix test !! */
1015   loop_context->exitclient = 1;
1016 /*  ret = TerminateThread((HANDLE)pid,0);*/
1017 #else
1018   ret = pthread_cancel(pid);
1019 #endif
1020 
1021   return 0;
1022 }
1023 
1024 /* \return 0 if ok, -1 if error, 1 if trying to kill myself */
kill_child_new(unsigned long pid,wzd_context_t * context)1025 int kill_child_new(unsigned long pid, wzd_context_t * context)
1026 {
1027   ListElmt * elmnt;
1028   int found=0;
1029 
1030   /* preliminary check: i can't kill myself */
1031   if (context != NULL && pid==context->pid_child) return 1;
1032 
1033   /* checks that pid is really one of the users */
1034   for (elmnt=list_head(context_list); elmnt!=NULL; elmnt=list_next(elmnt))
1035   {
1036     context = list_data(elmnt);
1037     if (context && context->magic == CONTEXT_MAGIC && context->pid_child == pid) { found = 1; break; }
1038   }
1039   if (!found) return -1;
1040 
1041   /* \todo XXX FIXME remove/fix test !! */
1042   context->exitclient = 1;
1043 /*  ret = TerminateThread((HANDLE)pid,0);*/
1044 /*  ret = pthread_cancel(pid);*/
1045 
1046   return 0;
1047 }
1048 
1049 
1050 
1051 
1052 
is_user_in_group(wzd_user_t * user,unsigned int gid)1053 short is_user_in_group(wzd_user_t * user, unsigned int gid)
1054 {
1055   unsigned int i;
1056 
1057   if (!user || user->group_num<=0) return -1;
1058   for (i=0; i<user->group_num; i++)
1059     if (gid==user->groups[i]) return 1;
1060   return 0;
1061 }
1062 
1063 
group_remove_user(wzd_user_t * user,unsigned int gid)1064 int group_remove_user(wzd_user_t * user, unsigned int gid)
1065 {
1066   unsigned int i;
1067   unsigned int idx=(unsigned int)-1;
1068 
1069   if (!user || user->group_num<=0) return -1;
1070   for (i=0; i<user->group_num; i++)
1071   {
1072     if (user->groups[i]==gid) {
1073       idx = i;
1074     }
1075   }
1076   if (idx==(unsigned int)-1) return -1;
1077 
1078   for (i=idx; i<user->group_num; i++)
1079   {
1080     user->groups[i] = user->groups[i+1];
1081   }
1082   user->group_num--;
1083 
1084   return 0;
1085 }
1086 
1087 
1088 /** wrappers to context list */
GetMyContext(void)1089 void * GetMyContext(void)
1090 {
1091   ListElmt * elmnt;
1092   wzd_context_t * context=NULL;
1093 
1094   /* first, try to get value from TLS */
1095   context = _tls_get_context();
1096   if (context != NULL) {
1097     WZD_ASSERT_RETURN(context->magic == CONTEXT_MAGIC, NULL);
1098     return context;
1099   }
1100 
1101   /* if not found, try iterating through context list */
1102 #ifdef WIN32
1103   {
1104     unsigned long thread_id;
1105 
1106     thread_id = (unsigned long)GetCurrentThreadId();
1107     /* TODO search context list and cleanup context */
1108     for (elmnt=list_head(context_list); elmnt!=NULL; elmnt=list_next(elmnt))
1109     {
1110       context = list_data(elmnt);
1111       if (context && context->magic == CONTEXT_MAGIC && context->thread_id == thread_id) {
1112         return context;
1113       }
1114     }
1115   }
1116 #else /* WIN32 */
1117   {
1118     pthread_t thread_id;
1119 
1120     if (!context_list) return NULL;
1121 
1122     thread_id = pthread_self();
1123     /* TODO search context list and cleanup context */
1124     for (elmnt=list_head(context_list); elmnt!=NULL; elmnt=list_next(elmnt))
1125     {
1126       context = list_data(elmnt);
1127       if (context && context->magic == CONTEXT_MAGIC &&
1128         pthread_equal((pthread_t)context->thread_id,thread_id)) {
1129           return context;
1130       }
1131     }
1132   }
1133 #endif /* WIN32 */
1134 
1135   return NULL;
1136 }
1137 
1138 #ifdef WIN32
win32_gettimeofday(struct timeval * tv,struct timezone * tz)1139 int win32_gettimeofday(struct timeval *tv, struct timezone *tz)
1140 {
1141     FILETIME        ft;
1142     LARGE_INTEGER   li;
1143     __int64         t;
1144     static int      tzflag;
1145 
1146     if (tv)
1147     {
1148         GetSystemTimeAsFileTime(&ft);
1149         li.LowPart  = ft.dwLowDateTime;
1150         li.HighPart = ft.dwHighDateTime;
1151         t  = li.QuadPart;       /* In 100-nanosecond intervals */
1152         t -= EPOCHFILETIME;     /* Offset to the Epoch time */
1153         t /= 10;                /* In microseconds */
1154         tv->tv_sec  = (long)(t / 1000000);
1155         tv->tv_usec = (long)(t % 1000000);
1156     }
1157 
1158     if (tz)
1159     {
1160         if (!tzflag)
1161         {
1162             _tzset();
1163             tzflag++;
1164         }
1165         tz->tz_minuteswest = _timezone / 60;
1166         tz->tz_dsttime = _daylight;
1167     }
1168 
1169     return 0;
1170 }
1171 
1172 
1173 /** We can't use _chsize() since it is limited to 2Gb files, nor _chsize_s()
1174  * which does not exist on all versions of the platform SDK.
1175  */
win32_ftruncate(int fd,__int64 length)1176 int win32_ftruncate(int fd, __int64 length)
1177 {
1178   HANDLE handle = (HANDLE)_get_osfhandle(fd);
1179   __int64 offset, offset_set;
1180   __int32 offset_hi, length_hi;
1181   int res;
1182 
1183   /* save file pointer */
1184   offset_hi = 0;
1185   offset = SetFilePointer(handle, 0, &offset_hi, FILE_CURRENT);
1186   if (offset == INVALID_SET_FILE_POINTER)
1187     return -1;
1188 
1189   /* extend or truncate */
1190   length_hi = (__int32)(length >> 32);
1191   offset_set = SetFilePointer(handle, (__int32)(length & 0xFFFFFFFF), &length_hi, FILE_BEGIN);
1192   if (offset_set == INVALID_SET_FILE_POINTER)
1193     return -1;
1194 
1195   res = SetEndOfFile(handle);
1196   if (res == FALSE)
1197     return -1;
1198 
1199   /* restore file pointer */
1200   offset_set = SetFilePointer(handle, (__int32)(offset & 0xFFFFFFFF), &offset_hi, FILE_BEGIN);
1201   if (offset_set == INVALID_SET_FILE_POINTER)
1202     return -1;
1203 
1204   return res;
1205 }
1206 
1207 #endif /* WIN32 */
1208 
1209 #ifndef HAVE_STRERROR
strerror(int errnum)1210 const char *strerror(int errnum)
1211 {
1212   static char buf[1024];
1213 
1214   snprintf(buf,1023,"Unknown error %d\n",errnum);
1215   buf[1023] = '\0';
1216 
1217   return buf;
1218 }
1219 #endif /* HAVE_STRERROR */
1220 
1221