1     /*********************************************************************\
2     *  Copyright (c) 2003-5 by Radim Kolar (hsn@cybermail.net)            *
3     *  Copyright (c) 1991 by Wen-King Su (wen-king@vlsi.cs.caltech.edu)   *
4     *                                                                     *
5     *  You may copy or modify this file in any manner you wish, provided  *
6     *  that this notice is always included, and that you hold the author  *
7     *  harmless for any loss or damage resulting from the installation or *
8     *  use of this software.                                              *
9     \*********************************************************************/
10 
11 #include "tweak.h"
12 #include "server_def.h"
13 #include "s_extern.h"
14 #include <stdlib.h>
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #ifdef HAVE_UTIME_H
19 #include <utime.h>
20 #endif
21 #include "my-string.h"
22 #include "fifocache.h"
23 
24 /****************************************************************************
25 * This is a modified server_file that uses cache for directory listings and
26 * directory status information.                                             *
27 *****************************************************************************/
28 
29 static struct FifoCache *dirlistcache;
30 static struct FifoCache *dirstatcache;
31 /* open file handles cache */
32 static struct FifoCache *fpcache;
33 
search_fpcache(unsigned long inet_num,unsigned short port_num,const char * fname)34 static FPCACHE *search_fpcache (unsigned long inet_num,
35 	                        unsigned short port_num,
36 				const char * fname)
37 {
38   unsigned int i;
39   FPCACHE *entry;
40   const char **key;
41 
42   for (i=0;i<fp_cache_limit;i++)
43   {
44       /* search for file in cache */
45     entry=(FPCACHE *)(fpcache->e_head+i*sizeof(FPCACHE));
46     if (port_num==entry->port_num && inet_num==entry->inet_num)
47     {
48 	key=f_cache_get_key(fpcache,entry);
49 	if(!strcmp(fname,*key))
50 	{
51 		/* file found in cache, return cache-pointer */
52 	        fpcache->hit++;
53                 return entry;
54 	}
55     }
56   }
57   fpcache->miss++;
58   return((FPCACHE *)0);
59 }
60 
fpcache_free_entry(void * entry)61 static void fpcache_free_entry (void *entry)
62 {
63     FPCACHE *f=entry;
64 
65     if(f->fp)
66 	fclose(f->fp);
67 }
68 
fpcache_entry_profiler(const void * entry)69 static unsigned int fpcache_entry_profiler (const void *entry)
70 {
71     const FPCACHE *f=entry;
72 
73     if(f->fp)
74 #ifdef __DragonFly__
75 	return sizeof(struct __FILE_public);
76 #else
77 	return sizeof(FILE);
78 #endif
79     else
80 	return 0;
81 }
82 
83 
dirlistcache_free_entry(void * entry)84 static void dirlistcache_free_entry (void * entry)
85 {
86     DIRLISTING *d=entry;
87 
88     if(d->listing)
89 	free(d->listing);
90 }
91 
dirlistcache_entry_profiler(const void * entry)92 static unsigned int dirlistcache_entry_profiler (const void *entry)
93 {
94     const DIRLISTING *d=entry;
95 
96     return d->listing_size;
97 }
98 
dirstatcache_free_entry(void * entry)99 static void dirstatcache_free_entry (void * entry)
100 {
101     DIRINFO *d=entry;
102 
103     if(d->realname)
104 	free(d->realname);
105     if(d->owner_password)
106 	free(d->owner_password);
107     if(d->public_password)
108 	free(d->public_password);
109     if(d->owner)
110 	free_ip_table(d->owner);
111     if(d->readme)
112 	free(d->readme);
113 }
114 
dirstatcache_entry_profiler(const void * entry)115 static unsigned int dirstatcache_entry_profiler  (const void *entry)
116 {
117    /* TODO profiling for owner ip_table */
118    const DIRINFO *d=entry;
119    unsigned int res=0;
120    if(d->realname)
121    {
122        res=strlen(d->realname);
123        res++;
124    }
125     if(d->owner_password)
126     {
127 	res+=strlen(d->owner_password);
128 	res++;
129     }
130     if(d->public_password)
131     {
132 	res+=strlen(d->public_password);
133 	res++;
134     }
135     if(d->readme)
136     {
137 	res+=strlen(d->readme);
138 	res++;
139     }
140 
141    return res;
142 }
143 
string_free(void * entry)144 static void string_free (void * entry)
145 {
146     char **s=entry;
147     if(*s!=NULL)
148 	free(*s);
149 }
150 
string_profiler(const void * entry)151 static unsigned int string_profiler (const void *entry)
152 {
153     char * const *s=entry;
154 
155     if(*s!=NULL)
156 	return(strlen(*s));
157     else
158 	return 0;
159 }
160 
string_compare(const void * e1,const void * e2)161 static int string_compare (const void *e1,const void *e2)
162 {
163 
164     char *const *s1=e1;
165     char *const *s2=e2;
166 
167     /* strcmp do not likes NULLs */
168     if(*s1 && *s2)
169     {
170        return strcmp(*s1,*s2);
171     }else
172 	return 1;
173 }
174 
175 /* should init all types of caches in future */
init_caches(void)176 int init_caches (void)
177 {
178    dirlistcache=f_cache_new(dir_cache_limit,sizeof(DIRLISTING),dirlistcache_free_entry,sizeof(char *),string_free,string_compare);
179    dirstatcache=f_cache_new(stat_cache_limit,sizeof(DIRINFO),dirstatcache_free_entry,sizeof(char *),string_free,string_compare);
180    fpcache=f_cache_new(fp_cache_limit,sizeof(FPCACHE),fpcache_free_entry,sizeof(char *),string_free,string_compare);
181 
182    if(dirlistcache && dirstatcache && fpcache)
183    {
184        f_cache_set_memory_profilers(dirlistcache,string_profiler,dirlistcache_entry_profiler);
185        f_cache_set_memory_profilers(dirstatcache,string_profiler,dirstatcache_entry_profiler);
186        f_cache_set_memory_profilers(fpcache,string_profiler,fpcache_entry_profiler);
187        return 0;
188    }
189    else
190    {
191        f_cache_destroy(dirlistcache);
192        f_cache_destroy(dirstatcache);
193        f_cache_destroy(fpcache);
194        return -1;
195    }
196 }
197 
stat_caches(FILE * fp)198 void stat_caches (FILE *fp)
199 {
200           if(fp==NULL) fp=stderr;
201  	  fprintf(fp,"DIRLISTCACHE ");
202  	  f_cache_stats(dirlistcache,fp);
203  	  fprintf(fp,"DIRSTATCACHE ");
204  	  f_cache_stats(dirstatcache,fp);
205  	  fprintf(fp,"FPCACHE ");
206 	  f_cache_stats(fpcache,fp);
207 }
208 
209 /* should init all types of caches in future */
shutdown_caches(void)210 void shutdown_caches (void)
211 {
212 #ifdef LAMERPACK
213     fclose(stderr);
214 #endif
215     fprintf(stderr,"DIRLISTCACHE ");
216     f_cache_stats(dirlistcache,stderr);
217     f_cache_clear(dirlistcache);
218     f_cache_destroy(dirlistcache);
219     fprintf(stderr,"DIRSTATCACHE ");
220     f_cache_stats(dirstatcache,stderr);
221     f_cache_clear(dirstatcache);
222     f_cache_destroy(dirstatcache);
223     fprintf(stderr,"FPCACHE ");
224     f_cache_stats(fpcache,stderr);
225     f_cache_clear(fpcache);
226     f_cache_destroy(fpcache);
227 }
228 
229 
230 
231 /*****************************************************************************
232  * Validate path - check that path does not fall outside the bounds of the
233  * FSP root directory, weed out references to / and ../.. type links.
234  * Input:     fullp - pointer to full filename
235  *         lenfullp - length of full filename (including \0)
236  *         *di      - where to return DIRINFO information about directory
237  *         want_directory - want to operate on directory, not a file
238  *****************************************************************************/
validate_path(char * fullp,unsigned lenfullp,PPATH * pp,DIRINFO ** di,int want_directory)239 const char *validate_path (char * fullp, unsigned lenfullp, PPATH * pp,DIRINFO **di, int want_directory)
240 {
241     char work [NBSIZE];
242     const char *err;
243     char *s;
244     struct stat sd;
245     DIRINFO newdir;
246 
247     if(dbug)
248     {
249  	  fprintf(stderr,"Validating path = %s len=%u\n",lenfullp>0? fullp:NULL,lenfullp);
250     }
251     /* split fullpath into parts */
252     if( (err=parse_path(fullp,lenfullp,pp)) ) return err;
253     if(want_directory)
254     {
255 	/* everything in path is a directory */
256 	pp->d_ptr=pp->fullp;
257 	pp->d_len=pp->d_len+pp->f_len+1;
258     }
259     /* extract dir from path to work */
260     memcpy(work,pp->d_ptr,pp->d_len);
261     work[pp->d_len]='\0';
262     if(dbug)
263     {
264         fprintf(stderr,"looking for directory %s in statcache.\n",work);
265     }
266     err=work;
267     *di=f_cache_find(dirstatcache,&err);
268     if(*di)
269     {
270 	if(dbug) fprintf(stderr,"hit!\n");
271 	/* need to recheck a cached directory? */
272 	if((*di)->lastcheck+stat_cache_timeout>cur_time)
273 	{
274 	    if(dbug) fprintf(stderr,"...and fresh\n");
275 	    return NULL; /* no, we have fresh cache entry */
276 	}
277 	else
278 	    if(dbug)
279 		fprintf(stderr,"...but stale\n");
280     }
281     /* try to stat directory */
282     if(FSP_STAT(work,&sd))
283 	return("No such directory");
284     if(!S_ISDIR(sd.st_mode))
285 	return("Not a directory");
286     /* recheck directory */
287     if(*di)
288     {
289 	if(use_directory_mtime && sd.st_mtime==(*di)->mtime)
290 	{
291 	    /* rechecked ok */
292 	    (*di)->lastcheck=cur_time;
293 	    return NULL;
294 	} else
295 	{
296 	    /* throw out old directory */
297 	    f_cache_delete_entry(dirstatcache,*di);
298 	    *di=NULL;
299 	}
300     }
301 
302    /* build a new directory info */
303   if (chdir(work))
304       return("Can't change directory");
305   /* save fixed input directory to err */
306   s=strdup(work);
307   if(!getcwd(work,NBSIZE))
308   {
309       chdir(home_dir);
310       free(s);
311       return("Can't get current directory");
312   }
313   /* check namespace */
314   if(homedir_restricted)
315       if (strncmp(work,home_dir,strlen(home_dir))) {
316 	  chdir(home_dir);
317 	  free(s);
318 	  return("Not following link out of restricted area");
319       }
320   /*  init. dir structure */
321   newdir.realname=strdup(work);
322   if(!newdir.realname)
323   {
324       free(s);
325       chdir(home_dir);
326       return("Memory exhausted");
327   }
328   if(use_directory_mtime)
329       newdir.mtime=sd.st_mtime;
330   else
331       newdir.mtime=cur_time;
332   newdir.lastcheck=cur_time;
333   /* load access perms */
334   load_access_rights(&newdir);
335   chdir(home_dir);
336   /* put new record into cache */
337   if(dbug) fprintf(stderr,"putting into statcache: %s\n",s);
338   *di=f_cache_put(dirstatcache,&s,&newdir);
339   return NULLP;
340 }
341 
342 /* copy file : from -> to */
copy_file(const char * n1,const char * n2)343 static const char *copy_file (const char * n1, const char * n2)
344 {
345     FILE *ft,*fp;
346     size_t bytes;
347     char buf[1024];
348 
349   if(!(ft = fopen(n1,"rb"))) {
350     return("Can't open temporary file");
351   }
352 
353   if(!(fp = fopen(n2,"wb"))) {
354     fclose(ft);
355     return("Can't open file for output");
356 
357   }
358   /* copy temporary file to actual fput file */
359   while( (bytes = fread(buf,1,sizeof(buf),ft)))
360   {
361       if ( bytes != fwrite(buf,1,bytes,fp)) {
362 	  break;
363       }
364   }
365 
366   if ( ferror(ft) || ferror(fp) )
367   {
368       fclose(ft);
369       fclose(fp);
370       unlink(n2);
371       return ("Write error");
372   }
373 
374   fclose(ft);
375   fclose(fp);
376   return NULL;
377 }
378 
379 /* appends new packet to directory listing, return 0 on success */
append_dir_listing(DIRLISTING * dl,const char * buf,unsigned int size)380 static int append_dir_listing (DIRLISTING * dl,const char * buf,unsigned int size)
381 {
382       int8_t *newbuf;
383 
384       /* append this buffer */
385       newbuf=realloc(dl->listing,dl->listing_size+size);
386       if(newbuf==NULL)
387       {
388 	  if(dl->listing) free(dl->listing);
389 	  dl->listing=NULL;
390 	  return -1;
391       }
392       memcpy(newbuf+dl->listing_size,buf,size);
393       dl->listing_size+=size;
394       dl->listing=newbuf;
395 
396       return 0;
397 }
398 
399 /* builds directory listing into DIRLISTING structure, in case of any
400  * error. nulls dl->listing
401  */
build_dir_listing(DIRLISTING * dl,const char * directory)402 static void build_dir_listing (DIRLISTING * dl,const char *directory)
403 {
404   int skip;            /* how many bytes skip to next 4byte boundary */
405   unsigned int rem;    /* remaining free space in UDP data space */
406   DIR *dir_f;          /* opened directory for listing */
407   struct dirent *dp;   /* record in directory */
408   struct stat    sb;   /* stat data of record */
409   register char  *s;   /* pointer to filename */
410   size_t nlen;         /* filename length including zero terminator */
411   char buffer[UBUF_SPACE]; /* buffer for building dirblock packet */
412   char name[NBSIZE];   /* buffer for stat name */
413   int namelen;         /* directory name length */
414   unsigned int bufpos; /* current write pos. in buffer */
415   unsigned int dirblocksize;
416 
417   /* init pointers */
418   dl->listing=NULL;
419   dl->listing_size=0;
420   bufpos=0;
421 
422   if(!(dir_f = opendir(directory)))  {
423     fprintf(stderr,"Can't open dir during listing initialization\n");
424     return;
425   }
426   /* do not build longer directory blocks than 1024 bytes */
427   if(packetsize > UBUF_SPACE)
428       dirblocksize=UBUF_SPACE;
429   else
430       dirblocksize = packetsize;
431   memset(buffer,0,dirblocksize); /* clear memory on the stack */
432   strcpy(name,directory);
433   namelen=strlen(directory);
434   name[namelen++]='/'; /* add directory separator to name */
435 
436   for(rem = dirblocksize; (dp = readdir(dir_f)); ) {
437     if (dp->d_ino == 0) continue;
438     s = dp->d_name;
439 
440     /* hide dot files, but allow . or .. */
441     if((s[0]=='.') && ((s[1]!=0) && (s[1] != '.' || s[2] != 0))) continue;
442 
443     strcpy(name+namelen,s);
444     if(FSP_STAT(name,&sb)) continue;
445     if(!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) continue;
446     if(sb.st_size>FOURGIGS) sb.st_size=FOURGIGS;
447 
448     nlen = strlen(s)+1;
449 
450     /* do we have space in buffer for entire entry?  */
451     if(rem < RDHSIZE + nlen) {
452       /* fill rest of buffer with '*' */
453       memset(buffer+bufpos,RDTYPE_SKIP,rem);
454       /* append this buffer */
455       if(append_dir_listing(dl,buffer,dirblocksize))
456       {
457 	  closedir(dir_f);
458 	  return;
459       }
460       rem = dirblocksize;
461       bufpos = 0;
462     }
463 
464     BB_WRITE4(buffer+bufpos,sb.st_mtime);
465     bufpos+=4;
466     BB_WRITE4(buffer+bufpos,sb.st_size );
467     bufpos+=4;
468     buffer[bufpos++]=S_ISDIR(sb.st_mode) ? RDTYPE_DIR : RDTYPE_FILE;
469     memcpy(buffer+bufpos,s,nlen);
470     bufpos+=nlen;
471     rem -= (nlen + RDHSIZE);
472 
473     /* pad to 4byte boundary */
474     if((skip = (nlen + RDHSIZE) & 0x3)) {
475       skip=4-skip;
476       bufpos+=skip;
477       rem   -=skip;
478     }
479   }
480   closedir(dir_f);
481 
482   /* do we have space for final END entry? */
483   if(rem <RDHSIZE )
484   {
485       /* no, make a new packet */
486       memset(buffer+bufpos,RDTYPE_SKIP,rem);
487       /* append this buffer */
488       if(append_dir_listing(dl,buffer,dirblocksize))
489 	  return;
490 
491       bufpos = 0;
492   }
493 
494   /* add final entry */
495   bufpos+=8;
496   buffer[bufpos++]=RDTYPE_END;
497   /* append last buffer */
498   append_dir_listing(dl,buffer,bufpos);
499   dl->mtime=cur_time;
500 }
501 
server_get_dir(DIRLISTING ** dl,const DIRINFO * di)502 const char *server_get_dir (DIRLISTING ** dl, const DIRINFO * di)
503 {
504   struct stat sf;
505   char   list_p[NBSIZE];
506 
507   /* get directory from memory cache */
508   if(dbug) fprintf(stderr,"finding %s in dirlistcache\n",di->realname);
509   *dl=f_cache_find(dirlistcache,&(di->realname));
510   if(*dl && (*dl)->mtime < di->mtime)
511   {
512       /* expired */
513       f_cache_delete_entry(dirlistcache,*dl);
514       *dl=NULL;
515       if(dbug) fprintf(stderr,"... directory was updated, list needs rebuild.\n");
516   }
517   if(*dl==NULL)
518   {
519       /* need to build a directory listing */
520       DIRLISTING dlnew;
521       char *key;
522       unsigned int ok;
523 
524       if(dbug) fprintf(stderr," miss.\n");
525       ok=0;
526       if(use_prebuild_dirlists)
527       {
528           sprintf(list_p,"%s/"FSP_DIRLISTING,di->realname);
529 	  /* we have up-to-date directory listing somewhere? */
530           if(!FSP_STAT(list_p,&sf))
531 	    if(sf.st_mtime>=di->mtime) {
532 	      /* try to load it */
533 	      FILE *f;
534 
535 	      dlnew.listing_size=sf.st_size;
536 	      dlnew.listing=malloc(dlnew.listing_size);
537 	      if(dlnew.listing)
538 	      {
539 		  if( (f=fopen(list_p,"rb")) )
540 		  {
541 	              if(dlnew.listing_size==fread(dlnew.listing,1,dlnew.listing_size,f))
542 		         ok=1;
543 		      fclose(f);
544 		  }
545 		  if(!ok)
546 		  {
547 		      free(dlnew.listing);
548 		      dlnew.listing=NULL;
549 		  }
550 	      }
551 	      if(ok)
552 	      {
553 		 if(dbug) fprintf(stderr,"cached content sucessfully used.\n");
554 	         dlnew.mtime=sf.st_mtime;
555 	      }
556 	  }
557       }
558       /* build list if we do not have it */
559       if(!ok)
560 	  build_dir_listing (&dlnew,di->realname);
561       if(!dlnew.listing)
562 	  return("Server can't list directory");
563       /* put new list into cache */
564       key=strdup(di->realname);
565       *dl=f_cache_put(dirlistcache,&key,&dlnew);
566       if(use_prebuild_dirlists && !ok)
567       {
568 	  /*  try to write a cache directory content */
569 	  FILE *f;
570 	  ok=0;
571 	  f=fopen(list_p,"wb");
572 	  if(f)
573 	  {
574 	      ok=fwrite(dlnew.listing,1,dlnew.listing_size,f);
575 	      if(ok==dlnew.listing_size)
576 		 ok=1;
577 	      fclose(f);
578 	  }
579 	  if(!ok)
580 	      unlink(list_p);
581 	  else
582 	     (*dl)->mtime=cur_time;
583       }
584   }
585   else
586   {
587     if(dbug) fprintf(stderr," hit!\n");
588   }
589   return(NULLP);
590 }
591 
592 /**********************************************************************/
593 /* assume path and ACL is validated */
server_del_file(PPATH * pp,DIRINFO * di)594 const char *server_del_file (PPATH * pp, DIRINFO * di)
595 {
596   struct stat sb;
597 
598   if(FSP_STAT(pp->fullp,&sb)) return("unlink: file not accessible");
599   if(!(S_ISREG(sb.st_mode))) return("unlink: not an ordinary file");
600 
601   if(unlink(pp->fullp) == -1) return("unlink: cannot unlink");
602   di->mtime=cur_time;
603   di->lastcheck=cur_time;
604 
605   return(NULLP);
606 }
607 
608 /**********************************************************************/
609 
server_del_dir(PPATH * pp,DIRINFO * di)610 const char *server_del_dir (PPATH * pp, DIRINFO *di)
611 {
612   struct stat sb;
613   DIRINFO null;
614 
615   if(FSP_STAT(pp->fullp,&sb)) return("rmdir: directory not accessible");
616   if(!(S_ISDIR(sb.st_mode))) return("rmdir: not an ordinary directory");
617 
618   memset(&null,0,sizeof(DIRINFO));
619   null.protection = DIR_LIST | DIR_GET;
620 
621   chdir(pp->fullp);
622   save_access_rights(&null);
623   chdir(home_dir);
624   if(rmdir(pp->fullp) != 0) {
625     chdir(pp->fullp);
626     save_access_rights(di);
627     chdir(home_dir);
628     return("rmdir: cannot unlink");
629   }
630   else
631   {
632       di->lastcheck=0;
633   }
634 
635   return(NULLP);
636 }
637 
638 /**********************************************************************/
639 
server_make_dir(PPATH * pp,unsigned long inet_num,DIRINFO ** di)640 const char *server_make_dir (PPATH * pp, unsigned long inet_num,DIRINFO **di)
641 {
642   DIRINFO newdir;
643   char temp_p[NBSIZE];
644   char *name;
645 
646   /* make directory and place ownerfile in it */
647   if(mkdir(pp->fullp,0777) != 0) return("Can't create directory");
648   memset(&newdir,0,sizeof(DIRINFO));
649   newdir.protection=(*di)->protection;
650   if((*di)->owner_password)
651       newdir.owner_password=strdup((*di)->owner_password);
652   if((*di)->public_password)
653       newdir.public_password=strdup((*di)->public_password);
654   newdir.lastcheck=cur_time;
655   newdir.mtime=cur_time;
656   /* make owner record */
657   sprintf(temp_p,"%d.%d.%d.%d O Creator\n",
658 	      ((unsigned char *)(&inet_num))[0],
659 	      ((unsigned char *)(&inet_num))[1],
660 	      ((unsigned char *)(&inet_num))[2],
661 	      ((unsigned char *)(&inet_num))[3]);
662   add_ipline(temp_p,&newdir.owner);
663   chdir(pp->fullp);
664   save_access_rights(&newdir);
665   getcwd(temp_p,NBSIZE);
666   chdir(home_dir);
667   newdir.realname=strdup(temp_p);
668   name=strdup(pp->fullp);
669   *di=f_cache_put(dirstatcache,&name,&newdir);
670   return(NULLP);
671 }
672 
673 /**********************************************************************/
674 
server_get_file(PPATH * pp,FILE ** fp,unsigned long inet_num,unsigned short port_num,DIRINFO * di)675 const char *server_get_file (PPATH * pp,
676 	                     FILE ** fp,
677 			     unsigned long  inet_num,
678 			     unsigned short port_num,
679 			     DIRINFO * di
680 			     )
681 {
682   struct stat sb;
683   char   realfn[NBSIZE];
684   FPCACHE *cache_f;
685 
686   sprintf(realfn,"%s/%s",di->realname,pp->f_ptr);
687   cache_f=search_fpcache(inet_num,port_num,realfn);
688 
689   if(!cache_f) {
690     FPCACHE newfile;
691     char *key;
692     /* file not found in cache? */
693 
694     if (FSP_STAT(realfn,&sb)) return("No such file");
695     if(!(S_ISREG(sb.st_mode)))
696     {
697 	if(S_ISDIR(sb.st_mode))
698 	    return ("Is a directory");
699 	else
700 	    return("Not a regular file");
701     }
702     /* open new file */
703     if(!(*fp = fopen(pp->fullp,"rb"))) return("Can't open file");
704 
705     /* add it to the file-cache */
706     newfile.inet_num=inet_num;
707     newfile.port_num=port_num;
708     newfile.fp=*fp;
709     key=strdup(realfn);
710     f_cache_put(fpcache,&key,&newfile);
711   }
712   /* get filepoint from cache */
713   else *fp = cache_f->fp;
714 
715   return(NULLP);
716 }
717 
718 /**********************************************************************/
719 /* returns number of readme bytes
720 */
server_get_pro(DIRINFO * di,char * result,const char * acc)721 int server_get_pro (DIRINFO * di, char * result, const char * acc)
722 {
723   int pos=0;
724 
725   if(di->readme)
726   {
727       strcpy(result,di->readme);
728       pos=strlen(result); /* add readme */
729       pos++;              /* add zero terminator char */
730   }
731   /* append xtra data space area */
732   result[pos] = di->protection^DIR_GET;
733   if(acc[0]=='O')
734             result[pos] |= DIR_OWNER;
735 
736   return pos;
737 }
738 
739 /**********************************************************************/
740 
server_set_pro(DIRINFO * di,const char * key)741 const char *server_set_pro (DIRINFO *di, const char * key)
742 {
743   unsigned char act;
744 
745   switch(key[1]) {
746     case 'c':
747       act=DIR_ADD;
748       break;
749     case 'd':
750       act=DIR_DEL;
751       break;
752     case 'g':
753       act=DIR_GET;
754       break;
755     case 'm':
756       act=DIR_MKDIR;
757       break;
758     case 'l':
759       act=DIR_LIST;
760       break;
761     case 'r':
762       act=DIR_RENAME;
763       break;
764     default:
765       return("Invalid syntax. <+|-> <c|d|g|m|l|r>");
766   }
767 
768   switch(key[0]) {
769     case '+':
770       di->protection|=act;
771       break;
772     case '-':
773       di->protection&=~act;
774       break;
775     default:
776       return("Invalid syntax. <+|-> <c|d|g|m|l|r>");
777     }
778 
779   di->mtime=cur_time;
780   di->lastcheck=cur_time;
781 
782   chdir(di->realname);
783   save_access_rights (di);
784   chdir(home_dir);
785 
786   return(NULLP);
787 }
788 
789 /**********************************************************************
790  *  These two are used for file uploading.
791  **********************************************************************/
792 
server_up_load(char * data,unsigned int len,unsigned long pos,unsigned long inet_num,unsigned short port_num)793 const char *server_up_load (char * data, unsigned int len, unsigned long pos,
794 			    unsigned long inet_num, unsigned short port_num)
795 {
796   FILE *fp;
797   char  tname[NBSIZE];
798   FPCACHE *cache_f;
799   char *tmp;
800   struct stat sf;
801 
802   sprintf(tname, "%s/.T%08lX%04X", tmp_dir,inet_num, port_num);
803 
804   tmp=tname;
805   cache_f=f_cache_find(fpcache,&tmp);
806   if(! cache_f ) {
807     /* file not found in cache */
808     FPCACHE newfile;
809     if (pos) {
810       fp = fopen(tname, "r+b");
811     } else {
812       unlink(tname);
813       fp = fopen(tname,"wb");
814     }
815 
816     if(!fp) return("Cannot open temporary file");
817 
818     /* check for symlinks or other junk */
819     if(lstat(tname,&sf) || !S_ISREG(sf.st_mode))
820     {
821 	fclose(fp);
822 	remove(tname);
823 	return("Temporary file is NOT a regular file");
824     }
825     /* test if we do not create hole in file which is caused that
826        client continues upload across server crash, which causes
827        some data loss due to libc stdio write caching */
828     /* server do not cleans temporary directory on startup - so
829        uploads across restart should work */
830     if(pos > sf.st_size)
831     {
832 	fclose(fp);
833 	unlink(tname);
834 	return("You are creating hole in file. Restart upload.");
835     }
836     if(pos + len < sf.st_size)
837     {
838 	fclose(fp);
839 	unlink(tname);
840         if ( pos == 0 )
841            return server_up_load(data, len, pos, inet_num, port_num);
842 	return("Non continuous upload detected. Restart upload please.");
843     }
844     /* seek to starting position */
845     if(fseeko(fp, pos, SEEK_SET))
846     {
847 	fclose(fp);
848 	unlink(tname);
849         return("Seeking in file failed");
850     }
851     /* protect temporary file */
852     chmod(tname,S_IRUSR|S_IWUSR);
853     /* add it to the file-cache */
854     newfile.inet_num=inet_num;
855     newfile.port_num=port_num;
856     newfile.fp=fp;
857     tmp=strdup(tname);
858     f_cache_put(fpcache,&tmp,&newfile);
859   } else {
860       /* get file pointer from cache */
861       fp=cache_f->fp;
862   }
863 
864   /* check for uploading on non-tail of file */
865   sf.st_size= ftello(fp);
866   if(pos > sf.st_size)
867   {
868         f_cache_delete_entry(fpcache,cache_f);
869 	unlink(tname);
870 	return("You are creating hole in file. Restart upload.");
871   }
872   if(pos + len < sf.st_size)
873   {
874         f_cache_delete_entry(fpcache,cache_f);
875 	unlink(tname);
876         if ( pos == 0 )
877            return server_up_load(data, len, pos, inet_num, port_num);
878 	return("Non continuous upload detected. Restart upload please.");
879   }
880   if ( pos != sf.st_size )
881   {
882      if(fseeko(fp, pos, SEEK_SET)) {
883          f_cache_delete_entry(fpcache,cache_f);
884          return("Seeking in file failed");
885      }
886   }
887   if(len!=fwrite(data, 1, len, fp))
888   {
889       f_cache_delete_entry(fpcache,cache_f);
890       return("Writing to file failed");
891   }
892   return(NULLP);
893 }
894 
server_install(PPATH * pp,unsigned long inet_num,unsigned short port_num,const char * acc,DIRINFO * di,unsigned int l2,const char * s2)895 const char *server_install (PPATH * pp, unsigned long inet_num,
896 			    unsigned short port_num, const char * acc, DIRINFO *di, unsigned int l2, const char *s2)
897 {
898   char tname[NBSIZE];
899   const char *tmp;
900   FPCACHE *cache_f;
901 #ifdef HAVE_UTIME_H
902   struct utimbuf ut;
903 #endif
904 
905   sprintf(tname, "%s/.T%08lX%04X", tmp_dir,inet_num, port_num);
906   /* if file still in cache, then close it & remove it from cache */
907   tmp=tname;
908   cache_f=f_cache_find(fpcache,&tmp);
909   f_cache_delete_entry(fpcache,cache_f);
910 
911   if (dbug)
912     fprintf(stderr,"server_install: tname: %s, pp->fullp: %s\n",tname, pp->fullp);
913   /* zero length filename */
914   if( strcmp(pp->fullp,".") == 0 )
915    {
916        if (dbug)
917 	   fprintf(stderr,"server_install: zero length name. aborting upload.\n");
918        unlink(tname);
919        return (NULLP);
920    }
921 
922   if(fexist(pp->fullp) &&
923 	( (di->protection & DIR_DEL)==0 && acc[0] != 'O')
924     )
925   {
926       unlink(tname);
927       if(dbug)
928 	  fprintf(stderr,"File %s already exists, but user is not directory owner and public can't delete files.\n",pp->fullp);
929       return("no permission for replacing that file. Not an owner.");
930   }
931 
932   di->lastcheck=cur_time;
933   di->mtime=cur_time;
934 
935   /* delete target file, if any */
936   unlink(pp->fullp);
937 
938   umask(upload_umask);
939   /* so just copy the temp file */
940   tmp=copy_file(tname,pp->fullp);
941   unlink(tname);
942   umask(system_umask);
943 #ifdef HAVE_UTIME_H
944   if(l2>=4)
945   {
946       ut.modtime=BB_READ4(s2);
947       ut.actime=cur_time;
948       utime(pp->fullp,&ut);
949   }
950 #endif
951 
952   return(tmp);
953 }
954 
955 /**********************************************************************/
956 /* assume path is validated */
957 /* start GRAB OPERATION! */
server_secure_file(PPATH * pp,unsigned long inet_num,unsigned short port_num,DIRINFO * di)958 const char *server_secure_file (PPATH * pp, unsigned long inet_num,
959 				unsigned short port_num,DIRINFO *di)
960 {
961   struct stat sb;
962   char temp_p[NBSIZE];
963   const char *tmp;
964 
965   if(FSP_STAT(pp->fullp,&sb)) return("grab: file not accessible");
966   if(!(S_ISREG(sb.st_mode))) return("grab: not an ordinary file");
967 
968   sprintf(temp_p,"%s/.G%08lX%04X", tmp_dir, inet_num,port_num);
969 
970   unlink(temp_p);
971   /* link emulated as a filecopy */
972   tmp=copy_file(pp->fullp,temp_p);
973   if(tmp) return tmp;
974 
975   if(unlink(pp->fullp) == -1) {
976     unlink(temp_p);
977     return("grab: cannot unlink original file");
978   }
979 
980   di->lastcheck=cur_time;
981   di->mtime=cur_time;
982 
983   return(NULLP);
984 }
985 
server_grab_file(FILE ** fp,unsigned long inet_num,unsigned short port_num)986 const char *server_grab_file (FILE ** fp,
987 			      unsigned long inet_num,
988 			      unsigned short port_num)
989 {
990   struct stat sb;
991   char temp_p[NBSIZE];
992   FPCACHE *cache_f;
993   char *key;
994 
995   sprintf(temp_p,"%s/.G%08lX%04X",tmp_dir,inet_num,port_num);
996   key=temp_p;
997   cache_f=f_cache_find(fpcache,&key);
998   if(!cache_f)
999   {
1000       FPCACHE newfile;
1001 
1002       if(FSP_STAT(temp_p,&sb)) return("grab: can't find file");
1003       if(!(S_ISREG(sb.st_mode))) return("grab: Not a file");
1004       if(!(*fp = fopen(temp_p,"rb"))) return("grab: can't open file");
1005       newfile.inet_num=inet_num;
1006       newfile.port_num=port_num;
1007       newfile.fp=*fp;
1008       key=strdup(temp_p);
1009       f_cache_put(fpcache,&key,&newfile);
1010   } else
1011       *fp=cache_f->fp;
1012 
1013   return(NULLP);
1014 }
1015 
server_grab_done(unsigned long inet_num,unsigned short port_num)1016 const char *server_grab_done (unsigned long inet_num,
1017 			      unsigned short port_num)
1018 {
1019   struct stat sb;
1020   char temp_p[NBSIZE];
1021   FPCACHE *cache_f;
1022   char *key;
1023 
1024   sprintf(temp_p,"%s/.G%08lX%04X",tmp_dir,inet_num,port_num);
1025   if(FSP_STAT(temp_p,&sb)) return("grabdone: can't find temporary file");
1026   key=temp_p;
1027   cache_f=f_cache_find(fpcache,&key);
1028   f_cache_delete_entry(fpcache,cache_f);
1029   if(unlink(temp_p) == -1) return("grabdone: can't delete temporary file");
1030   return(NULLP);
1031 }
1032 
1033 /* return STAT info about filesystem object */
server_stat(UBUF * ubuf)1034 const char *server_stat (UBUF * ubuf )
1035 {
1036   struct stat sb;
1037   int rc;
1038   PPATH pp;
1039   unsigned short len;
1040   DIRINFO *junk;
1041 
1042   rc=0;
1043   len=BB_READ2(ubuf->bb_len);
1044   if(len<2) {
1045       strcpy(ubuf->buf,"");
1046       len=1;
1047   }
1048   if(parse_path(ubuf->buf,len,&pp))
1049       rc=1;
1050   sb.st_mtime=0;
1051   sb.st_mtime=0;
1052   if(!rc)
1053   {
1054       rc=FSP_STAT(pp.fullp,&sb);
1055       if(!rc)
1056       {
1057 	  /* SECURITY CHECK: validate directory */
1058           if S_ISDIR(sb.st_mode)
1059 	  {
1060 	      if(validate_path(ubuf->buf,len,&pp,&junk,1))
1061 		  rc=1;
1062 	  } else
1063 	      if(S_ISREG(sb.st_mode))
1064 	      {
1065 	        if(validate_path(ubuf->buf,len,&pp,&junk,0))
1066 		    rc=1;
1067 	      }
1068       }
1069   }
1070 
1071   BB_WRITE4(ubuf->buf,sb.st_mtime);
1072   BB_WRITE4(ubuf->buf+4,sb.st_size );
1073 
1074   if(rc)
1075       rc=0;
1076   else
1077       if S_ISDIR(sb.st_mode) rc=RDTYPE_DIR;
1078       else
1079 	  if S_ISREG(sb.st_mode) rc=RDTYPE_FILE;
1080           else
1081 	      rc=0; /* not a file or directory */
1082 
1083   (ubuf->buf)[8]=rc;
1084   return(NULLP);
1085 }
1086 
1087 /* rename FILE/directory object */
server_rename(PPATH * src,PPATH * dest,DIRINFO * sdir,DIRINFO * tdir)1088 const char *server_rename (PPATH *src,PPATH *dest,DIRINFO *sdir, DIRINFO *tdir)
1089 {
1090   struct stat sb;
1091   int issrcdir, istargetdir;
1092 
1093   /* explore type of source object */
1094   if(FSP_STAT(src->fullp,&sb)) return("can't find source file or directory");
1095   if(S_ISDIR(sb.st_mode))
1096       issrcdir=1;
1097   else
1098       if(S_ISREG(sb.st_mode))
1099 	  issrcdir=0;
1100       else
1101 	  return ("Refusing to operate on special files");
1102 
1103   /* --- explore Target --- */
1104   if(FSP_STAT(dest->fullp,&sb))
1105       istargetdir=-1; /* non - existent! */
1106   else
1107      if(S_ISDIR(sb.st_mode))
1108          istargetdir=1;
1109      else
1110          if(S_ISREG(sb.st_mode))
1111 	     istargetdir=0;
1112          else
1113 	     return ("Refusing to operate on special files");
1114 
1115   /* --=== now check ACL and do it ===-- */
1116 
1117   /* Cross - directory rename? */
1118   if  (sdir == tdir)
1119   {
1120       /* no, do simple rename */
1121       /* not needed checked at upper level
1122       pe=require_access_rights( sdir,DIR_RENAME,inet,src.passwd);
1123       if(pe[0]!='N' && pe[0]!='O')
1124 	  return ("Permission denied");
1125       if(istargetdir==0)
1126          pe=require_access_rights( sdir,DIR_DEL,inet,src.passwd);
1127       if(pe[0]!='N' && pe[0]!='O')
1128 	  return ("No permission for overwriting files");
1129       */
1130       /* now go to the action */
1131       if (rename(src->fullp,dest->fullp))
1132 	  return ("Rename failed");
1133       /* update dir listing */
1134       sdir->lastcheck=cur_time;
1135       sdir->mtime=cur_time;
1136       return NULLP;
1137   }
1138   return ("Cross-directory renames are not implemented yet.");
1139   /*  return(NULLP); */
1140 }
1141 /*********************************************************************
1142  test and resolve home directory
1143  *********************************************************************/
1144 
init_home_dir(void)1145 void init_home_dir (void)
1146 {
1147   void *newhd;
1148 
1149   /* test and goto home dir */
1150   if(chdir(home_dir) == -1) {
1151     perror(home_dir);
1152     exit(6);
1153   }
1154 
1155   /* resolve home dir */
1156   newhd=realloc(home_dir,UBUF_SPACE);
1157   if(!newhd)
1158   {
1159       perror("realloc1 homedir");
1160       exit(5);
1161   }
1162 
1163   if(!getcwd(newhd,UBUF_SPACE))
1164   {
1165       perror("getcwd for homedir");
1166       exit(6);
1167   }
1168 
1169   home_dir=realloc(newhd,strlen(newhd)+1);
1170   if(!home_dir)
1171   {
1172       perror("realloc2 homedir");
1173       exit(5);
1174   }
1175 
1176   if(tmp_dir)
1177   {
1178       mkdir(tmp_dir,0700);
1179       chmod(tmp_dir,0700);
1180       if(chdir(tmp_dir)==-1)
1181       {
1182 	  free(tmp_dir);
1183 	  tmp_dir=NULL;
1184 	  read_only=1;
1185       }
1186       chdir(home_dir);
1187   }
1188 
1189 #ifndef LAMERPACK
1190   if(dbug) {
1191 #endif
1192     fprintf(stderr,"home on %s\n",home_dir);
1193     if(tmp_dir)
1194        fprintf(stderr,"tmpdir on %s\n",tmp_dir);
1195 #ifndef LAMERPACK
1196   }
1197 #endif
1198 }
1199