/* ** File I/O module for fhttpd ** fhttpd-file.cc Copyright (C) 1995, 96, 97 Alex Belits ** This code is a part of fhttpd distribution */ #include #include #include #include #include #include #include #include #ifndef NO_MMAP #include #endif #ifdef SOLARIS extern "C"{ void bzero(void *s, size_t n); int gethostname(char *name, int namelen); } #endif #ifdef IRIX #include "irix-fix.h" #endif #ifdef GETOPT_VARS_NOT_DEFINED extern char *optarg; extern int optind, opterr; #endif extern "C" { #include "servproc.h" void processimageform(struct request *req,struct httpresponse *response, const char *namevalue,double xvalue,double yvalue); } #include "fhttpd-decls.h" #include "configargs.h" #include "lists.h" #include "wildmat.h" #ifndef FILE_TRANSFER_BUFFER_SIZE #define FILE_TRANSFER_BUFFER_SIZE 65536 #endif #ifndef FHTTPD_CONFIG_FILE #define FHTTPD_CONFIG_FILE "/etc/fhttpd.conf" #endif #ifndef MMAPPAGESIZE #define MMAPPAGESIZE 4096 #endif #ifndef IDLE_TIMEOUT #define IDLE_TIMEOUT 120 #endif #ifndef TRANSFER_TIMEOUT #define TRANSFER_TIMEOUT 900 #endif int global_resolve_hostnames=1; char *default_header=NULL,*default_footer=NULL,*userhomedir=NULL; List types,dirtypes,executables,specialexecutables,pipeoutexec; char *configcommands[NCONFIGCOMMANDS]={ "NORESOLVE","HONORKEEPALIVE","HTTPPORT","FTPPORT","PROCESSPORT", "STORMODE","ABSSTORMODE","MKDMODE","ABSMKDMODE", "INBOUND","ABSINBOUND","PIPEINEXEC","ABSPIPEINEXEC", "PIPEOUTEXEC","ABSPIPEOUTEXEC", "DEFAULTHEADER","DEFAULTFOOTER","TYPE","ABSTYPE","DIRTYPE","ABSDIRTYPE", "GETRULE","ABSGETRULE","EXECMASK","SPECIALEXECMASK", "ACCESSREALM","ACCESSRIGHTS", "PRELOAD","HTTPROOT","ADDRESSROOT","APPLICATION","AUTHAPPLICATION", "LOGAPPLICATION", "UMASK","MAXCONTENTLENGTH","MAXMESSAGESIZE", "MAXDATAINQUEUE","MAXDATAINLOGQUEUE","USERDIR","MAPUSERDIR"}; char *strupr(char *s){ char *s1=s; while(*s1){ if(*s1>='a'&&*s1<='z') *s1&=0xdf; s1++; } return s; } ConfigArgs *appfindwildargs(List *list,char *string,char *realstring){ ConfigArgs *currargs,*nextargs; if(!string||!realstring) return NULL; if(list){ if(list->start){ currargs=(ConfigArgs*)(list->start); while(currargs){ nextargs=(ConfigArgs*)(currargs->next); if(currargs->params){ if(currargs->params[0]){ if(currargs->absolute){ if(wildmat(realstring,currargs->params[0])){ return currargs; } }else{ if(wildmat(string,currargs->params[0])){ //FIXME! return currargs; } } } } currargs=nextargs; } } } return NULL; } ConfigArgs *appfindwildargsreal(List *list,char *string){ if(!(*string)) return NULL; if(list){ return (ConfigArgs*)list->FindWildMatch(string); } return NULL; } int filenamecmp(char *s1,char *s2,int *noslashflag){ int noslashvar,*noslash=&noslashvar; char *s11=s1,*s22=s2; int n; if(noslashflag) noslash=noslashflag; while(*s11==*s22&&*s11){ s11++; s22++; } if(!*s11&&!*s22) return 0; if(*s11!='/'&&*s22!='/'){ if(s11==s1) return 1; s11--; s22--; } n=((*s11=='/')<<1)|(*s22=='/'); switch(n){ case 0: return 1; case 1: // *s22=='/' && *s11!='/' if(*s11) return 1; if(!s22[1]){ *noslash=1; return 0; } if(!strcmp(s22,"/file_index.html")){ *noslash=1; return 0; } if(!strcmp(s22,"/index.html")){ *noslash=1; return 0; } return 1; case 2: // *s11=='/' && *s22!='/' if(*s22) return 1; if(!s11[1]){ *noslash=1; return 0; } if(!strcmp(s11,"/file_index.html")){ *noslash=1; return 0; } if(!strcmp(s11,"/index.html")){ *noslash=1; return 0; } return 1; default: // *s11=='/' && *s22=='/' if(s22[1]) if(strcmp(s22,"/file_index.html")) if(strcmp(s22,"/index.html")) return 1; if(!s11[1]) return 0; if(!strcmp(s11,"/file_index.html")) return 0; if(!strcmp(s11,"/index.html")) return 0; return 1; } } void unconfig(void){ ConfigArgs *currargs; TypeArgs *currtypeargs; global_resolve_hostnames=1; if(default_header) free(default_header); default_header=NULL; if(default_footer) free(default_footer); default_footer=NULL; if(userhomedir) free(userhomedir); userhomedir=NULL; while((currtypeargs=(TypeArgs*)types.start)){ delete currtypeargs; } while((currtypeargs=(TypeArgs*)dirtypes.start)){ delete currtypeargs; } while((currargs=(ConfigArgs*)pipeoutexec.start)){ delete currargs; } while((currargs=(ConfigArgs*)executables.start)){ delete currargs; } while((currargs=(ConfigArgs*)specialexecutables.start)){ delete currargs; } } int readconfig(char *name){ char buffer[MESSAGE_BUFFER_SIZE],*p,*p1,*param[20]; int i,ii,j,l,nparam,nline=0; FILE *f; ConfigArgs* args; TypeArgs* currtype; f=fopen(name,"rt"); if(f){ while(fgets(buffer,MESSAGE_BUFFER_SIZE,f)){ nline++; do{ buffer[(MESSAGE_BUFFER_SIZE-1)]=0; p=strchr(buffer,'\n'); if(p) *p=0; p=strchr(buffer,'\r'); if(p) *p=0; p=strchr(buffer,'#'); if(p) *p=0; p=strrchr(buffer,'\\'); if(p){ p1=p+1; while(p&&*p1){ if(((unsigned char)*p1)>' '){ p=NULL; }else p1++; } if(p){ fgets(p,MESSAGE_BUFFER_SIZE-(p-buffer),f); nline++; } } }while(p&&p-buffer<(MESSAGE_BUFFER_SIZE-1)); i=0; nparam=0; l=strlen(buffer); while(buffer[i]&&((unsigned char)buffer[i]<=' ')) i++; if(buffer[i]){ do{ while(buffer[i]&&(unsigned char)buffer[i]<=' ') i++; param[nparam]=buffer+i; for(;(unsigned char)buffer[i]>' ';i++); if(buffer[i]&&(unsigned char)buffer[i]<=' '){ buffer[i]=0; i++; } if(!nparam){ strupr(param[0]); } nparam++; }while(i=3){ if(strlen(param[1])>MAXPATHLEN) param[1][MAXPATHLEN+1]=0; if(strlen(param[2])>MAXPATHLEN) param[2][MAXPATHLEN+1]=0; args=new ConfigArgs(nparam-1,param+1,i==ABSPIPEOUTEXEC); if(args){ pipeoutexec.Add(args); } }else{ fprintf(stderr,"fhttpd: config line %d - Not enough parameters.\n",nline); } break; case DEFAULTHEADER: if(nparam==2){ for(ii=0;param[1][ii];ii++){ if(param[1][ii]=='_') param[1][ii]=' '; if(param[1][ii]=='\\') if(param[1][ii+1]) for(j=ii;param[1][j];j++) param[1][j]=param[1][j+1]; } default_header=(char*)malloc(strlen(param[1])+1); if(default_header) strcpy(default_header,param[1]); }else{ fprintf(stderr,"fhttpd: config line %d - \"DefaultHeader\" should have one parameter.\n",nline); } break; case DEFAULTFOOTER: if(nparam==2){ for(ii=0;param[1][ii];ii++){ if(param[1][ii]=='_') param[1][ii]=' '; if(param[1][ii]=='\\') if(param[1][ii+1]) for(j=ii;param[1][j];j++) param[1][j]=param[1][j+1]; } default_footer=(char*)malloc(strlen(param[1])+1); if(default_footer) strcpy(default_footer,param[1]); }else{ fprintf(stderr,"fhttpd: config line %d - \"DefaultFooter\" should have one parameter.\n",nline); } break; case C_TYPE: case C_ABSTYPE: if(nparam==5){ if(strlen(param[1])>MAXPATHLEN) param[1][MAXPATHLEN+1]=0; if(strlen(param[2])>MAXPATHLEN) param[2][MAXPATHLEN+1]=0; if(strlen(param[3])>MAXPATHLEN) param[3][MAXPATHLEN+1]=0; if(strlen(param[4])>MAXPATHLEN) param[4][MAXPATHLEN+1]=0; for(ii=0;param[2][ii];ii++){ if(param[2][ii]=='_') param[2][ii]=' '; if(param[2][ii]=='\\') if(param[2][ii+1]) for(j=ii;param[2][j];j++) param[2][j]=param[2][j+1]; } for(ii=0;param[3][ii];ii++){ if(param[3][ii]=='_') param[3][ii]=' '; if(param[3][ii]=='\\') if(param[3][ii+1]) for(j=ii;param[3][j];j++) param[3][j]=param[3][j+1]; } for(ii=0;param[4][ii];ii++){ if(param[4][ii]=='_') param[4][ii]=' '; if(param[4][ii]=='\\') if(param[4][ii+1]) for(j=ii;param[4][j];j++) param[4][j]=param[4][j+1]; } currtype=new TypeArgs(param[1],param[2],param[3],param[4],i==C_ABSTYPE); if(currtype) types.Add(currtype); }else{ fprintf(stderr,"fhttpd: config line %d - \"[Abs]Type\" should have 4 parameters.\n",nline); } break; case C_DIRTYPE: case C_ABSDIRTYPE: if(nparam==4){ if(strlen(param[1])>MAXPATHLEN) param[1][MAXPATHLEN+1]=0; if(strlen(param[2])>MAXPATHLEN) param[2][MAXPATHLEN+1]=0; if(strlen(param[3])>MAXPATHLEN) param[3][MAXPATHLEN+1]=0; for(ii=0;param[2][ii];ii++){ if(param[2][ii]=='_') param[2][ii]=' '; if(param[2][ii]=='\\') if(param[2][ii+1]) for(j=ii;param[2][j];j++) param[2][j]=param[2][j+1]; } for(ii=0;param[3][ii];ii++){ if(param[3][ii]=='_') param[3][ii]=' '; if(param[3][ii]=='\\') if(param[3][ii+1]) for(j=ii;param[3][j];j++) param[3][j]=param[3][j+1]; } currtype=new TypeArgs(param[1],"",param[2],param[3],i==C_ABSDIRTYPE); if(currtype) dirtypes.Add(currtype); }else{ fprintf(stderr,"fhttpd: config line %d - \"[Abs]DirType\" should have 3 parameters.\n",nline); } break; case EXECMASK: if(nparam>=2){ if(strlen(param[1])>MAXPATHLEN) param[1][MAXPATHLEN+1]=0; args=new ConfigArgs(nparam,param); if(args){ if(args->params[0]&&args->params[1]){ free(args->params[0]); args->params[0]=(char*)malloc(strlen(args->params[1])+1); if(args->params[0]){ strcpy(args->params[0],args->params[1]); args->charvalue=args->params[0]; if(args->charvalue) args->charvaluelength=strlen(args->charvalue); } } executables.Add(args); } }else{ fprintf(stderr,"fhttpd: config line %d - Not enough parameters.\n",nline); } break; case SPECIALEXECMASK: if(nparam==2){ if(strlen(param[1])>MAXPATHLEN) param[1][MAXPATHLEN+1]=0; args=new ConfigArgs(nparam-1,param+1); if(args){ specialexecutables.Add(args); } }else{ fprintf(stderr,"fhttpd: config line %d - \"SpecialExecMask\" should have one parameter.\n",nline); } break; case USERDIR: if(nparam==2){ if(strlen(param[1])>MAXPATHLEN) param[1][MAXPATHLEN+1]=0; if(userhomedir) free(userhomedir); userhomedir=(char*)malloc(strlen(param[1])+1); if(userhomedir) strcpy(userhomedir,param[1]); }else{ fprintf(stderr,"fhttpd: config line %d - \"UserDir\" should have one parameter.\n",nline); } break; } } } } fclose(f); return 0; } return -1; } int global_alarmflag=0; int idle_timeout=IDLE_TIMEOUT; int transfer_timeout=TRANSFER_TIMEOUT; void alarmhandler(SIGACTARGS){ global_alarmflag=1; } void setalarm(int t){ global_alarmflag=0; struct sigaction tmpsigaction; bzero((char*)&tmpsigaction,sizeof(struct sigaction)); tmpsigaction.sa_handler=alarmhandler; sigaddset(&tmpsigaction.sa_mask,SIGALRM); tmpsigaction.sa_flags=0; sigaction(SIGALRM,&tmpsigaction,NULL); alarm(t); } int makefileresponse(struct httpresponse *response, const char *contenttype,int keepalive){ char tmpline[128]; char tmpline1[128]; int i; if(!response) return -1; i=putlinetoresponse(response,"HTTP/1.0 200 OK\r\nContent-Type: "); if(!contenttype){ i|=putlinetoresponse(response,"text/html"); }else{ i|=putlinetoresponse(response,contenttype); } i|=putlinetoresponse(response,"\r\nServer: " SERVERSOFTWARE); if(keepalive){ i|=putlinetoresponse(response, "\r\nConnection: Keep-Alive\r\nKeep-Alive: max=0, timeout=30"); } linefromtime(tmpline1,time(NULL)); sprintf(tmpline,"\r\nDate: %s\r\n", tmpline1); i|=putlinetoresponse(response,tmpline); if(i<0) i=-1; return i; } int displayfileentry(struct httpresponse *response,char *s, char *absbase,char *relbase){ if(!s) return -1; time_t tt,ft; char filetime[80]; char absstring[MAXPATHLEN+1],relstring[MAXPATHLEN+1]; char *prefix=NULL,*postfix=NULL; TypeArgs *currtype=NULL; *filetime=0; time(&tt); ft=((__s32*)s)[2]; strcpy(filetime,ctime(&ft)); filetime[16]=0; strncpy(absstring,absbase,MAXPATHLEN); absstring[MAXPATHLEN]=0; strncat(absstring,s+3*sizeof(__s32)+sizeof(char*), MAXPATHLEN-1-strlen(absstring)); absstring[MAXPATHLEN]=0; strncpy(relstring,relbase,MAXPATHLEN); relstring[MAXPATHLEN]=0; strncat(relstring,s+3*sizeof(__s32)+sizeof(char*), MAXPATHLEN-1-strlen(relstring)); relstring[MAXPATHLEN]=0; if((__s32)tt>((__s32*)s)[2]+15552000l){ filetime[11]=' '; memcpy(filetime+12,filetime+20,4); } if(S_ISDIR(((__s32*)s)[0])){ currtype=(TypeArgs*)dirtypes.start; }else{ currtype=(TypeArgs*)types.start; } while(currtype){ if(wildmat(currtype->absolute?absstring:relstring, (char*)(currtype->pattern))){ prefix=currtype->dirprefix; postfix=currtype->dirpostfix; currtype=NULL; } if(currtype) currtype=(TypeArgs*)currtype->next; } if(prefix) putlinetoresponse(response,prefix); else putlinetoresponse(response,"
"); if(S_ISDIR(((__s32*)s)[0])){ if(!prefix) putlinetoresponse(response,""); putlinetoresponse(response,""); putmlinetoresponse(response,s+3*sizeof(__s32)+sizeof(char*)); putlinetoresponse(response,"/ "); putlinetoresponse(response,filetime); if(postfix) putlinetoresponse(response,postfix); else putlinetoresponse(response,""); if(*((char**)&(((__s32*)s)[3]))) putlinetoresponse(response,*((char**)&(((__s32*)s)[3]))); }else{ putlinetoresponse(response,""); putmlinetoresponse(response,s+3*sizeof(__s32)+sizeof(char*)); putlinetoresponse(response," "); putlinetoresponse(response,filetime); sprintf(filetime," (%d bytes)",((__s32*)s)[1]); putlinetoresponse(response,filetime); if(postfix) putlinetoresponse(response,postfix); if(*((char**)&(((__s32*)s)[3]))) putlinetoresponse(response,*((char**)&(((__s32*)s)[3]))); } putlinetoresponse(response,"\r\n"); return 0; } int makedirheader(struct httpresponse *response,const char *host, const char *dir,const char *headerfmt){ const char *hbpointer,*hpointer; if(!host||!dir||!headerfmt) return -1; hbpointer=headerfmt; while(hbpointer){ hpointer=strchr(hbpointer,'$'); if(hpointer){ writetoresponse(response,hbpointer,hpointer-hbpointer); hpointer++; switch(*hpointer){ case 'D': case 'd': putmlinetoresponse(response,dir); hpointer++; break; case 'H': case 'h': putmlinetoresponse(response,host); hpointer++; break; case '$': writetoresponse(response,"$",1); hpointer++; break; } hbpointer=hpointer; }else{ putlinetoresponse(response,hbpointer); hbpointer=NULL; } } return 0; } int addfile(struct httpresponse *response,char *s){ #ifdef FCNTL_LOCK struct flock rdlck={F_RDLCK,SEEK_SET,0,0,0},unlck={F_UNLCK,SEEK_SET,0,0,0}; #endif struct stat statbuf; int rvalue=0; int tmph=open(s,O_RDONLY); if(tmph>=0){ if(!fstat(tmph,&statbuf)){ if(S_ISREG(statbuf.st_mode)){ #ifdef FLOCK if(!flock(tmph,LOCK_SH)) #else #ifdef FCNTL_LOCK if(!fcntl(tmph,F_SETLK,&rdlck)) #endif #endif { char buffer[FILE_TRANSFER_BUFFER_SIZE]; int l; while((l=read(tmph,buffer,FILE_TRANSFER_BUFFER_SIZE))>0){ writetoresponse(response,buffer,l); } #ifdef FLOCK flock(tmph,LOCK_UN); #else #ifdef FCNTL_LOCK fcntl(tmph,F_SETLK,&unlck); #endif #endif } }else rvalue=-1; }else rvalue=-1; close(tmph); }else rvalue=-1; return rvalue; } int dircompare(const void *a,const void *b){ if(S_ISDIR(*(__s32*)*(char**)a)&&!S_ISDIR(*(__s32*)*(char**)b)) return -1; if(S_ISDIR(*(__s32*)*(char**)b)&&!S_ISDIR(*(__s32*)*(char**)a)) return 1; return strcmp(((char*)*(char**)a)+3*sizeof(__s32)+sizeof(char*),((char*)*(char**)b)+3*sizeof(__s32)+sizeof(char*)); } int swrite(int h,char *s){ return write(h,s,strlen(s)); } int checkinput(int h){ fd_set readfd; FD_ZERO(&readfd); FD_SET(h,&readfd); return select(h+1,&readfd,NULL,NULL,NULL)>0; } int main(int argc,char **argv){ struct http_server *server; struct request *req; int argc1; char **argv1; int i,human=1,useuid=0,a=0,uid_error; char *configfile=FHTTPD_CONFIG_FILE; char tmpline[128]; int map_username=0; /* not global one in fhttpd itself */ char *startingslash; #ifdef FCNTL_LOCK struct flock rdlck={F_RDLCK,SEEK_SET,0,0,0},unlck={F_UNLCK,SEEK_SET,0,0,0}; #endif signal(SIGPIPE,SIG_IGN); signal(SIGCHLD,SIG_IGN); int c,i0=0,i1=0; while((c=getopt(argc,argv,"spdc:mu:it:T:"))!=-1){ switch(c){ case 'd': human=0; break; case 'c': configfile=optarg; break; case 's': i0=1; break; case 'p': i1=1; break; case 'm': map_username=1; break; case 'i': if(!getuid()) useuid=1; break; case 'u': if(*optarg=='0') umask(strtoul(optarg,NULL,8)); else umask(strtoul(optarg,NULL,10)); break; case 't': idle_timeout=atoi(optarg); break; case 'T': transfer_timeout=atoi(optarg); break; case ':': case '?': return -1; } } argc1=argc-optind; argv1=(char**)malloc(sizeof(char*)*(argc1+2)); if(!argv1) return -1; argv1+=2; for(i=optind;i Set umask\n" " -t