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