Back to index

wims  3.65+svn20090927
lines.c
Go to the documentation of this file.
00001 /*    Copyright (C) 1998-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis
00002  *
00003  *  This program is free software; you can redistribute it and/or modify
00004  *  it under the terms of the GNU General Public License as published by
00005  *  the Free Software Foundation; either version 2 of the License, or
00006  *  (at your option) any later version.
00007  *
00008  *  This program is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  *  GNU General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU General Public License
00014  *  along with this program; if not, write to the Free Software
00015  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00016  */
00017        /* line input / output / translation routines
00018         * and error routines */
00019 
00020 int is_class_module=0;
00021 
00022 int trusted_module(void);
00023 void phtml_put_base(char *fname,int cache);
00024 void module_error(char msg[]);
00025 void user_error(char msg[]);
00026 void accessfile(char *content, char *type, char *s,...);
00027 int remove_tree(char *p);
00028 void output(char *s,...);
00029 char *_getvar(char *vname);
00030 int checkhost(char *hlist);
00031 
00032 char *lastdata;
00033 char lastdatafile[MAX_FNAME+1];
00034 char *datacache[DATAFIELD_LIMIT];
00035 int  nextdatacache;
00036 struct stat ftst;
00037 char lastftest[MAX_FNAME+1];
00038 int lastftype;
00039 char outbuf[8192];
00040 char *outptr;
00041 
00042   /* These modules can execute private programs.
00043    * adm/ modules are always trusted, so need no definition here. */
00044 char *trusted_modules="";
00045               /* bit 0: module is not trusted.
00046                * bit 1: file in wimshome.
00047                * bit 2: readdef or file in writable directory.
00048                */
00049 int untrust=0;  /* non-zero if user detrusts the module. */
00050 
00051 int error_status=0;
00052 char pidbuf[32];
00053 #include <utime.h>
00054 
00055 void delete_pid(void);
00056 
00057        /* Internal use only */
00058 void _debug(char *s,...)
00059 {
00060     va_list vp;
00061     char buf[MAX_LINELEN+1];
00062 
00063     va_start(vp,s);
00064     vsnprintf(buf,sizeof(buf),s,vp);
00065     va_end(vp);
00066     setvar("debug",buf); module_error("debug");
00067     exit(1);
00068 }
00069 
00070 
00071        /* HTTP response header for non-processed CGI interface */
00072 void nph_header(int code)
00073 {
00074     char *cstr;
00075     switch(code) {
00076        case 200: cstr="OK"; break;
00077        case 301: cstr="Moved Permanently"; break;
00078        case 302: cstr="Moved Temporarily"; break;
00079        case 420: cstr="WIMS Nested Error"; break;
00080        case 450: cstr="WIMS User Error"; break;
00081        case 500: cstr="WIMS Internal Error"; break;
00082        case 550: cstr="WIMS Module Error"; break;       
00083        default: cstr="ERROR"; break;
00084     }
00085     if(httpd_type!=httpd_wims) printf("Status: %d %s\r\n",code,cstr);
00086     else {
00087        char *p, tbuf[256];
00088        mystrncpy(tbuf,ctime(&nowtime),sizeof(tbuf));
00089        for(p=tbuf+strlen(tbuf);p>=tbuf && (*(p-1)=='\n' || *(p-1)=='\r'); p--);
00090        *p=0;
00091        printf("HTTP/1.0 %d %s\r\n\
00092 Date: %s\r\n\
00093 Connection: close\r\n\
00094 ",code,cstr,tbuf);
00095     }
00096 }
00097 
00098 void flushoutput(void)
00099 {
00100     int l2;
00101     if(outptr<=outbuf) return;
00102     l2=outptr-outbuf;
00103     if(lastout_file!=-1) write(lastout_file,outbuf,l2);
00104     else fwrite(outbuf,l2,1,stdout);
00105     outptr=outbuf;
00106 }
00107 
00108 void internal_warn(char msg[])
00109 {
00110     char buf[1024];
00111     fprintf(stderr,"wims: %s\n%s\n",msg,strerror(errno));
00112     snprintf(buf,sizeof(buf),"%s: %s\n",nowstr,msg);
00113     accessfile(buf,"a","%s/internal_error.log",log_dir);
00114 }
00115 
00116   /* Internal error: panic and forget about requester. */
00117 void internal_error(char msg[])
00118 {
00119     if(error_status<2) {
00120        nph_header(500);
00121        printf("Cache-Control: no-cache\nPragma: no-cache\r\n\
00122 Content-type: text/plain\r\n\r\n\r\n\
00123 WIMS panick! %s\n%s\n",msg,strerror(errno));
00124        error_status=2; internal_warn(msg);
00125     }
00126     delete_pid(); exit(1);
00127 }
00128 
00129 void tex_nospace(char *p)
00130 {
00131     char *p1, *p2;
00132     char buf[MAX_LINELEN+1];
00133     for(p1=buf,p2=p;*p2 && p1-buf<MAX_LINELEN-4;p2++) {
00134        if(!isspace(*p2)) {*p1++=*p2;continue;}
00135        while(isspace(*p2)) p2++;
00136        if(*(p1-1)=='\\' || 
00137           (p1>buf && myisalnum(*(p1-1)) && myisalnum(*p2)))
00138          *p1++=' ';
00139        *p1++=*p2;
00140     }
00141     *p1++=0; memmove(p,buf,p1-buf);
00142 }
00143 
00144 void nametoolong(char *p)
00145 {
00146     char buf[MAX_FNAME+17];
00147     snprintf(buf,sizeof(buf),"%s...",p);
00148     force_setvar("wims_error_data",buf);
00149     module_error("file_name_too_long");
00150 }
00151 
00152 enum{is_file, is_dir, is_exec, is_fifo, is_socket, is_unknown};
00153 off_t ftest_size;
00154 
00155        /* A simple front-end of stat(). */
00156 int ftest(char *fname)
00157 {
00158     if(strcmp(fname,lastftest)==0) return lastftype;
00159 /* if(fname[0]=='/' || fname[0]=='.') fprintf(stderr,"ftest: %s\n",fname); */
00160     mystrncpy(lastftest,fname,sizeof(lastftest));
00161     if(stat(fname,&ftst)) return lastftype=-1;
00162     ftest_size=ftst.st_size;
00163     if(S_ISREG(ftst.st_mode)) {
00164        if((ftst.st_mode&(S_IXUSR|S_IXGRP|S_IXOTH))!=0) return lastftype=is_exec;
00165        else return lastftype=is_file;
00166     }
00167     if(S_ISDIR(ftst.st_mode)) return lastftype=is_dir;
00168     if(S_ISFIFO(ftst.st_mode)) return lastftype=is_fifo;
00169     if(S_ISSOCK(ftst.st_mode)) return lastftype=is_socket;
00170     return lastftype=is_unknown;
00171 }
00172 
00173 char fnbuf[MAX_FNAME+1];
00174 
00175        /* make a filename and check length */
00176 char *mkfname(char buf[], char *s,...)
00177 {
00178     va_list vp;
00179     char *p;
00180 
00181     if(buf==NULL) p=fnbuf; else p=buf;
00182     va_start(vp,s);
00183     vsnprintf(p,MAX_FNAME,s,vp);
00184     va_end(vp);
00185     if(strlen(p)>=MAX_FNAME-1) nametoolong(p);
00186     return p;
00187 }
00188 
00189 void sysmask_trigger(char *s)
00190 {
00191     char buf[MAX_FNAME+1];
00192     struct stat st;
00193     mkfname(buf,"%s/%s",sysmask_trigger_dir,s);
00194     stat(buf,&st);
00195 }
00196 
00197        /* read-in a file into buffer. Use open() and read().
00198         * Return buffer address which will be malloc'ed if buf=NULL. */
00199 char *readfile(char *fname, char buf[], long int buflen)
00200 {
00201     int fd, t, st;
00202     long int l, lc;
00203     char *bf;
00204     t=0; if(buf) buf[0]=0;
00205     st=ftest(fname); if(st!=is_file) {
00206        if(st==is_exec) { /* refuse to open executable file */
00207            setvar("executable",fname); module_error("executable");
00208        }
00209        return NULL;
00210     }
00211     l=ftst.st_size; if(l<0) return NULL;
00212     if(l>=buflen) {
00213        if(buflen<MAX_LINELEN) l=buflen-1;
00214        else {
00215            if(strncmp(fname,"modules/",strlen("modules/"))==0) {
00216               setvar(error_data_string,fname); module_error("file_too_long");
00217            }
00218            else user_error("cmd_output_too_long");
00219        }
00220     }
00221     fd=open(fname,O_RDONLY); if(fd==-1) return NULL;
00222     if(buf==NULL) bf=xmalloc(l+8); else {bf=buf;if(l==0) {t=1; l=buflen-1;}}
00223     lc=read(fd,bf,l); close(fd);
00224     if(lc<0 || lc>l || (lc!=l && t==0)) 
00225        {if(buf==NULL) free(bf); else buf[0]=0; return NULL;}
00226     bf[lc]=0; _tolinux(bf); return bf;
00227 }
00228 
00229        /* Get a line in a stored working file. 
00230         * Buffer length is always MAX_LINELEN. */
00231 int wgetline(char buf[], size_t buflen, WORKING_FILE *f)
00232 {
00233     int i,j; unsigned int n;
00234     i=f->linepointer; buf[0]=0;
00235     if(i>=f->linecnt || f->textbuf==NULL) return EOF;
00236     n=f->lines[i].llen;
00237     if(n>=buflen) n=buflen-1;
00238     if(n>0) memmove(buf,f->lines[i].address,n); buf[n]=0;
00239     for(j=i+1;j<f->linecnt && f->lines[j].isstart==0;j++);
00240     f->l=i; f->linepointer=j;
00241     if(j>=f->linecnt && n==0) return EOF; else return n;
00242 }
00243 
00244 int get_cached_file(char *name)
00245 {
00246     int i,l,flag;
00247     l=strlen(name); if(l>=127) return -1;
00248     if(strncmp(module_prefix,module_dir,strlen(module_dir))!=0) flag=8; else flag=0;
00249     for(i=0;i<mcachecnt;i++) {
00250        if(flag==mcache[i].nocache && strcmp(name,mcache[i].name)==0) {
00251            memmove(&m_file,mcache+i,sizeof(WORKING_FILE));
00252            m_file.nocache|=4;
00253            return i;
00254        }
00255     }
00256     return -1;
00257 }
00258 
00259        /* Open a work file. Returns 0 if OK. */
00260 int open_working_file(WORKING_FILE *f, char *fname)
00261 {
00262     char *p, *q;
00263     void *vp;
00264     int i,j,k,laststart,lc[LINE_LIMIT];
00265 
00266     f->linecnt=f->linepointer=0;
00267     f->for_idx=f->nocache=0;
00268     f->l=-1; f->lines=NULL;
00269     f->textbuf=readfile(fname,NULL,WORKFILE_LIMIT);
00270     if(f->textbuf==NULL) return -1;
00271     p=f->textbuf; if(*p) {
00272        for(i=0,p--; i<LINE_LIMIT-1 && p!=NULL;p=strchr(p,'\n')) {
00273            if(i>0) *p=0;
00274            lc[i++]=(++p-f->textbuf);
00275        }
00276     }
00277     else i=0;
00278     if(i>=LINE_LIMIT-1) module_error("file_too_long");
00279     lc[i]=lc[i-1]+strlen(f->textbuf+lc[i-1])+1;
00280     f->textbuf[lc[i]]=0;
00281     f->linecnt=i; laststart=0;
00282     f->for_stack=xmalloc(MAX_FOR_LEVEL*sizeof(FOR_STACK)+(i+1)*sizeof(LINE_STRUCT));
00283     vp=f->for_stack+MAX_FOR_LEVEL; f->lines=vp;
00284     for(j=0;j<i;j++) {
00285        f->lines[j].address=p=(f->textbuf)+lc[j];
00286        f->lines[j].llen=lc[j+1]-lc[j]-1;
00287        (f->lines[j]).execcode=(f->lines[j]).varcode=-1;
00288        if(j==0) goto normal;
00289        q=f->lines[laststart].address+f->lines[laststart].llen-1;
00290        if(q>=f->textbuf && *q=='\\') {
00291            f->lines[laststart].llen+=f->lines[j].llen; *q='\n';
00292            f->lines[j].isstart=f->lines[j].llen=0;
00293            memmove(q+1,p,lc[j+1]-lc[j]);
00294        }
00295        else {
00296            normal: for(q=p;myislspace(*q);q++);
00297            f->lines[j].isstart=1; laststart=j; k=0;
00298            switch(*q) {
00299                      /* isstart: bit 1 = start.
00300                       * bit 2 = exec
00301                       * bit 3 (4) = label
00302                       * bit 4 (8) = hard comment (!!)
00303                       * bit 5 (16) = soft comment (#) */
00304               case exec_prefix_char: {
00305                   if(myisalpha(q[1])) f->lines[j].isstart=3;
00306                   else f->lines[j].isstart=9;
00307                   k=1; break;
00308               }
00309               case label_prefix_char: {f->lines[j].isstart=5; k=1; break;}
00310               case comment_prefix_char: {f->lines[j].isstart=17; break;}
00311               default: {f->lines[j].isstart=1; break;}
00312            }
00313            if(k && q>p) {f->lines[j].address=q;f->lines[j].llen-=q-p;}
00314        }
00315     }
00316     f->lines[i].isstart=1; f->lines[i].llen=0;
00317     f->lines[i].address=(f->textbuf)+lc[i];
00318     mfilecnt++; return 0;
00319 }
00320 
00321        /* close an earlier opened working file */
00322 void close_working_file(WORKING_FILE *f, int cache)
00323 {
00324     f->linepointer=f->l=0;
00325     if(cache && untrust==0 && mcachecnt<MAX_MCACHE && (f->nocache&7)==0) {
00326        memmove(mcache+mcachecnt,f,sizeof(WORKING_FILE));
00327        mcachecnt++;
00328     }
00329     else if((f->nocache&4)==0) {
00330        if(f->for_stack!=NULL) free(f->for_stack);
00331        if(f->textbuf!=NULL) free(f->textbuf);
00332     }
00333     f->for_stack=NULL; f->textbuf=NULL; f->linecnt=0;
00334 }
00335 
00336 void free_mcache(void)
00337 {
00338     int i;
00339     untrust=0;
00340     for(i=mcachecnt-1;i>=0;i--) close_working_file(mcache+i,0);
00341     mcachecnt=0;
00342 }
00343 
00344 void cleantmpdir(void);
00345 void user_error_log(char msg[]);
00346 
00347 void nested_error(char msg[])
00348 {
00349     fprintf(stderr,"\nNested error! %s\n",msg);
00350     nph_header(420);
00351     printf("\r\n\r\nWIMS error processing aborted on nested error.\r\n\r\n%s\r\n",msg);
00352     delete_pid(); exit(1);
00353 }
00354 
00355        /* Send an error message to requester and exit.
00356         * This is for user errors, language-sensitive. */
00357 void user_error(char msg[])
00358 {
00359     char erfname[MAX_FNAME+1];
00360 
00361     if(error_status) nested_error(msg);
00362     error_status=1;
00363     mkfname(erfname,"%s.%s",user_error_msg_file,lang);
00364     if(ftest(erfname)!=is_file) internal_error("user_error(): error message file not found.\n\
00365 Bad installation.");
00366     force_setvar("wims_user_error",msg);
00367     if(strcmp(msg,"threshold")!=0) user_error_log(msg); 
00368     memmove(module_prefix,".",2); 
00369     if(lastout_file!=-1 && outputing) {
00370        flushoutput(); close(lastout_file); lastout_file=-1;
00371     }
00372     nph_header(450);
00373     phtml_put_base(erfname,0); 
00374     if(strcmp(msg,"double_click")!=0) delete_pid(); 
00375     else {
00376        cleantmpdir(); flushlog();
00377     }
00378     flushoutput(); exit(0);
00379 }
00380 
00381 void module_error_log(char msg[]);
00382 
00383        /* Messages for module errors. English only. */
00384        /* This is really rudimentary for the time being. */
00385 void module_error(char msg[])
00386 {
00387     int send=0;
00388     char *p;
00389     WORKING_FILE mf;
00390 
00391     if(error_status) nested_error(msg);
00392     error_status=1; untrust=0;
00393     module_error_log(msg);
00394     nph_header(550);
00395     printf("Server: %s %s (%s)\r\n", SHORTSWNAME,wims_version,LONGSWNAME);
00396     p=getvar("wims_main_font");
00397     if(p!=NULL && *p!=0) printf("Content-type: text/plain; charset=%s\r\n\r\n",p);
00398     else printf("Content-type: text/plain\r\n\r\n");
00399     p=getvar(ro_name[ro_module]); if(p==NULL) p="???";
00400     printf("ERROR.\n\nwims has detected an error in the module '%s'.",p);
00401     if(m_file.l>=0) printf("\n\nIn file '%s', line %d:",
00402                         m_file.name,m_file.l+1);
00403     printf(" %s.\n\n",msg);
00404     if(open_working_file(&mf,mkfname(NULL,"%s.%s",module_error_msg_file,lang))!=0)
00405       internal_error("module_error(): error message file not found.");
00406     while(wgetline(tmplbuf,MAX_LINELEN,&mf)!=EOF) {
00407        if(tmplbuf[0]!=tag_prefix_char) {
00408            if(send) {substit(tmplbuf); puts(tmplbuf);}
00409            continue;
00410        }
00411        strip_trailing_spaces(tmplbuf);
00412        if(tmplbuf[1]==0 || strcmp(msg,tmplbuf+1)==0) send=1;
00413        else send=0;
00414     }
00415     close_working_file(&mf,0);
00416     outptr=outbuf; delete_pid(); exit(1);
00417 }
00418 
00419        /* Output kernel routine */
00420 void _output_(char *s)
00421 {
00422     int l,l2;
00423     l=strlen(s); output_length+=l;
00424     if(output_length>=OUTPUT_LENGTH_LIMIT) {
00425        module_error("output_too_long"); return;
00426     }
00427     l2=sizeof(outbuf)-(outptr-outbuf);
00428     put: if(l<=l2) {
00429        memmove(outptr,s,l); outptr+=l; return;
00430     }
00431     memmove(outptr,s,l2); s+=l2; l-=l2;
00432     if(lastout_file!=-1) write(lastout_file,outbuf,sizeof(outbuf));
00433     else fwrite(outbuf,sizeof(outbuf),1,stdout);
00434     outptr=outbuf; l2=sizeof(outbuf); goto put;
00435 }
00436 
00437        /* Output with no var. */
00438 void output0(char buf[])
00439 {
00440     char *p1, *p2, *ps;
00441     int dynsave;
00442     if(backslash_insmath) {
00443        ps=buf; dynsave=instex_usedynamic;
00444        for(p1=strstr(buf,"\\("); p1; p1=strstr(p2,"\\(")) {
00445            p2=find_matching(p1+2,')');
00446            if(p2==NULL) break;
00447            if(p1>buf && *(p1-1)=='\\') continue;
00448            *p1=0; if(*(p2-1)=='\\') *(p2-1)=0; *p2++=0; _output_(ps); ps=p2;
00449            instex_usedynamic=1; insmath(p1+2);
00450        }
00451        if(*ps) _output_(ps); instex_usedynamic=dynsave;
00452     }
00453     else _output_(buf);
00454 }
00455 
00456        /* Output routine */
00457 void output(char *s,...)
00458 {
00459     va_list vp;
00460     char buf[4*MAX_LINELEN+1];
00461 
00462     va_start(vp,s);
00463     vsnprintf(buf,sizeof(buf),s,vp);
00464     va_end(vp);
00465     output0(buf);
00466 }
00467 
00468        /* read in tmpf in tmp directory, and places in p.
00469         * Maximal length: MAX_LINELEN. */
00470 void read_tmp_file(char *p, const char *fname)
00471 {
00472     char *name, *pp;
00473     name=mkfname(NULL,"%s/%s",tmp_dir,fname);
00474     if(!exec_is_module || !outputing || !direct_exec
00475        || strcmp(fname,"exec.out")!=0) {
00476        readfile(name,p,MAX_LINELEN);
00477        pp=getvar("wims_exec_dollar_double");
00478        if(pp && strcmp(pp,"yes")==0) {
00479            for(pp=strchr(p,'$'); pp; pp=strchr(pp+2,'$'))
00480              string_modify(p,pp,pp+1,"$$");
00481        }
00482     }
00483     else {
00484        char *s;
00485        s=readfile(name,NULL,OUTPUT_LENGTH_LIMIT);
00486        if(s==NULL) {*p=0; return;}
00487        if(memcmp(s,"Error: ", strlen("Error: "))==0) mystrncpy(p,s,MAX_LINELEN);
00488        output_length+=strlen(s);
00489        if(output_length>=OUTPUT_LENGTH_LIMIT) module_error("output_too_long");
00490        else _output_(s);
00491        free(s); *p=0;
00492        chmod(name,S_IRUSR|S_IWUSR);
00493     }
00494 }
00495 
00496        /* verify whether the module is trusted.
00497         * Returns 1 if yes, 0 if no. -1 for error. */
00498 int trusted_module(void)
00499 {
00500     char *modname, *w, buf[MAX_LINELEN+1];
00501     int i,n;
00502     static int _trusted=-1; /* avoid repeated computations */
00503     
00504     if(untrust&255) return 0;
00505     if(_trusted>=0) return _trusted;
00506     modname=getvar(ro_name[ro_module]);
00507     if(modname==NULL || *modname==0) return 0;
00508     if(memcmp(modname,"adm/",strlen("adm/"))==0 || 
00509        memcmp(modname,"classes/",strlen("classes/"))==0 || 
00510        strcmp(modname,home_module)==0 ||
00511        memcmp(modname,"help/",strlen("help/"))==0) {
00512        tr:
00513        if(memcmp(modname,"classes/",strlen("classes/"))==0)
00514          is_class_module=1;
00515        setenv("trusted_module","yes",1);
00516        return _trusted=1;
00517     }
00518     n=wordnum(trusted_modules); for(i=0;i<n;i++) {
00519        w=fnd_word(trusted_modules,i+1,buf);
00520        if(strcmp(w,modname)==0) goto tr;
00521     }
00522     return _trusted=0;
00523 }
00524 
00525        /* file should be in the module directory, but 
00526         * it may also be somewhere else.
00527         * buf[] requires MAX_FNAME+1 length.
00528         * Returns 0 if found. */
00529 int find_module_file(char *fname, char buf[], int mode)
00530 {
00531     char *p, dtest[32];
00532     
00533     fname=find_word_start(fname);
00534     if(*fname==0) return -1;
00535        /* Name checking: no directory backtracing. */
00536     if(strstr(fname,parent_dir_string)!=NULL) {
00537        setvar(error_data_string,fname); module_error("illegal_fname");
00538        return -1;
00539     }
00540     p=strchr(fname,'/'); if(p==NULL || p>fname+10) goto openit;
00541     memmove(dtest,fname,p-fname); dtest[p-fname]=0;
00542     if(strcmp(dtest,"datamodule")==0) {
00543        mkfname(buf,"modules/data%s",p); goto lastopen;
00544     }
00545     if(strcmp(dtest,"wimshome")==0 && trusted_module()) {
00546        mkfname(buf,"%s%s",getvar("wims_home"),p); goto lastopen;
00547     }
00548     if(strcmp(dtest,"writable")==0) {
00549        if(strncmp(p+1,"TEMP_",5)==0 && strchr(p+1,'/')==NULL) {
00550            mkfname(buf,"%s/%s",tmp_dir,p+1);
00551        }
00552        else {
00553            mkfname(buf,"w/%s/%s",module_prefix,p+1);
00554        }
00555        untrust|=4; 
00556     }
00557     else {
00558        openit: mkfname(buf,"%s/%s",module_prefix,fname);
00559     }
00560     if(mode) return 0;
00561     if(ftest(buf)!=is_file) {
00562        if(lastftype==is_exec) {
00563 isexec:
00564            setvar("executable",fname); module_error("executable");
00565            return -1;
00566        }
00567        if(strncmp(fname,"adm/",4)==0 && 
00568           (!trusted_module() || is_class_module)) return -1;
00569        mkfname(buf,"scripts/%s",fname);
00570        lastopen:
00571        if(mode) return 0;
00572        if(ftest(buf)!=is_file) {
00573            if(lastftype==is_exec) goto isexec;
00574            else return -1;
00575        }
00576     }
00577     return 0;
00578 }
00579 
00580        /* check whether a file is user-submitted */
00581        /* This is deplecated because of the wimshome/ method. */
00582 /* int user_file(char *name) {
00583     if(name[0]=='/' || name[0]=='.' ||
00584        strstr(name,"classes/")!=NULL || 
00585        strstr(name,"forums/")!=NULL ||
00586        strstr(name,"sessions/")!=NULL ||
00587        strstr(name,"doc/")!=NULL) return 1; else return 0;
00588 } */
00589 
00590        /* returns 1 if violation */
00591 int datafile_check(char *name) {
00592     if((untrust&255)==0) return 0;
00593     if(strncmp(name,"data/",strlen("data/"))==0) return 0;
00594     if(strncmp(name,"authors/",strlen("authors/"))==0) return 0;
00595     return 1;
00596 }
00597 
00598        /* returns 0 if success */
00599 void readdatafile(char *name)
00600 {
00601     char *pp;
00602     if(strcmp(name,lastdatafile)==0) return;
00603     lastdata[0]=0; readfile(name,lastdata,WORKFILE_LIMIT);
00604     mystrncpy(lastdatafile,name,sizeof(lastdatafile));
00605     datacache[0]=lastdata; nextdatacache=1;
00606     if(lastdata[0]==tag_string[1]) {
00607        datacache[1]=lastdata; nextdatacache++;
00608     }
00609     pp=strstr(lastdata,tag_string);
00610     if(pp) datacache[nextdatacache]=pp;
00611     else datacache[nextdatacache]=lastdata+strlen(lastdata);
00612 }
00613 
00614 char *_nextdata(char *p)
00615 {
00616     char *pp;
00617     if(!*p) return p;
00618     pp=strstr(p,tag_string);
00619     if(pp) return pp;
00620     else return p+strlen(p);
00621 }
00622 
00623        /* datafile structure: number of records.
00624         * tag=1 if direct access */
00625 unsigned int datafile_recordnum(char *p)
00626 {
00627     char nbuf[MAX_LINELEN+1], *pp;
00628     int i, t, ret;
00629 
00630     t=untrust; ret=0;
00631     if(direct_datafile) mystrncpy(nbuf,p,sizeof(nbuf));
00632     else if(datafile_check(p)!=0 || find_module_file(p,nbuf,0)) goto ret;
00633     readdatafile(nbuf); 
00634     for(i=nextdatacache, pp=datacache[i]; *pp;) {
00635        pp=_nextdata(pp+1); i++;
00636        if(i<DATAFIELD_LIMIT) {
00637            datacache[i]=pp; nextdatacache=i;
00638        }
00639     }
00640     ret=i-1;
00641     ret:
00642     untrust=t;
00643     return ret;
00644 }
00645 
00646        /* datafile structure: find record n, starting from 1 */
00647 char *datafile_fnd_record(char *p, int n, char bf[])
00648 {
00649     char nbuf[MAX_LINELEN+1], *pp, *p2;
00650     int i, t;
00651 
00652     bf[0]=0; t=untrust;
00653     if(n<0) goto ret;
00654     if(direct_datafile) mystrncpy(nbuf,p,sizeof(nbuf));
00655     else if(datafile_check(p)!=0 || find_module_file(p,nbuf,0)) goto ret;
00656     readdatafile(nbuf); if(*lastdata==0) goto ret;
00657     if(n>nextdatacache) {
00658        for(i=nextdatacache, pp=datacache[i]; i<n && *pp;) {
00659            pp=_nextdata(pp+1); i++;
00660            if(i<DATAFIELD_LIMIT) {
00661               datacache[i]=pp; nextdatacache=i;
00662            }
00663        }
00664     }
00665     else pp=datacache[n];
00666     if(!*pp) goto ret;
00667     if(n>1 || (n==1 && *pp!=tag_string[1])) pp+=strlen(tag_string);
00668     else if(n==1) pp+=strlen(tag_string)-1;
00669     if(n<nextdatacache) p2=datacache[n+1];
00670     else {
00671        p2=strstr(pp,tag_string); if(p2==NULL) p2=pp+strlen(pp);
00672        if(n<DATAFIELD_LIMIT-1 && n==nextdatacache) {
00673            nextdatacache++; datacache[nextdatacache]=p2;
00674        }
00675     }
00676     if(p2-pp>=MAX_LINELEN) p2=pp+MAX_LINELEN-1;
00677     if(p2<pp) p2=pp;
00678     memmove(bf,pp,p2-pp); bf[p2-pp]=0;
00679     ret:
00680     untrust=t; return bf;
00681 }
00682 
00683 char hex2char(char c1, char c2)
00684 {
00685     char tbuf[16];
00686     if(c1<'0' || c1>'f' || c2<'0' || c2>'f') {
00687 invl:
00688        snprintf(tbuf,sizeof(tbuf),"%%%c%c",c1,c2);
00689        setvar(error_data_string,tbuf);
00690        user_error("invalid_char_in_query_string");
00691     }
00692     c1=toupper(c1);c2=toupper(c2);
00693     if(c1>'9' && c1<'A') goto invl;
00694     if(c2>'9' && c2<'A') goto invl;
00695     if(c1>'F' || c2>'F') goto invl;
00696     if(c1>='A') c1=c1-'A'+'9'+1;
00697     if(c2>='A') c2=c2-'A'+'9'+1;
00698     return (c1-'0')*16+c2-'0';
00699 }
00700 
00701        /* Converts back http escaped chars, slight. Does not check buffer length.
00702         * Returns converted string length. */
00703 int _http2env(char outs[], char ins[])
00704 {
00705     int j,k,l;
00706     l=strlen(ins);
00707     for(j=k=0;j<l && !isspace(ins[j]);j++,k++) {
00708        if(isspace(ins[j])) {  /* skip space characters in query string */
00709            k--;continue;
00710        }
00711        if(ins[j]=='%') {
00712              /* skip Carriage-Return. */
00713            if(ins[j+1]=='0' && (ins[j+2]=='d' || ins[j+2]=='D')) {
00714               j+=2; k--; continue;
00715            }
00716            outs[k]=hex2char(ins[j+1],ins[j+2]);
00717            j+=2; continue;
00718        }
00719        outs[k]=ins[j];
00720     }
00721     outs[k]=0;    
00722     return k;
00723 }
00724 
00725        /* Converts back http escaped chars. Does not check buffer length.
00726         * Returns converted string length. */
00727 int http2env(char outs[], char ins[])
00728 {
00729     int j,k,l;
00730     l=strlen(ins);
00731     for(j=k=0;j<l && !isspace(ins[j]);j++,k++) {
00732        if(isspace(ins[j])) {  /* skip space characters in query string */
00733            k--;continue;
00734        }
00735        if(ins[j]=='%') {
00736              /* skip Carriage-Return. */
00737            if(ins[j+1]=='0' && (ins[j+2]=='d' || ins[j+2]=='D')) {
00738               j+=2; k--; continue;
00739            }
00740            outs[k]=hex2char(ins[j+1],ins[j+2]);
00741            j+=2; continue;
00742        }
00743        if(ins[j]=='+') {
00744            outs[k]=' '; continue;
00745        }
00746        if(ins[j]=='?' || ins[j]=='&') {
00747            outs[k]=0; continue;
00748        }
00749        outs[k]=ins[j];
00750     }
00751     outs[k]=0;    
00752     return k;
00753 }
00754 
00755        /* translate a string to http querystring style.
00756         * '&' is not translated.
00757         * Buffer p must be at least MAX_LINELEN. */
00758 void tohttpquery(char *p)
00759 {
00760     char trlist[]="  ()[]{}+-*^|/\"\'!:;,<>\n";
00761     char *pp;
00762     for(pp=p;*pp;pp++) {
00763        if(*pp==' ') {
00764            *pp='+'; continue;
00765        }
00766        if(strchr(trlist,*pp)==NULL) continue;
00767        if(*pp=='+' && pp>p && *(pp-1)=='&') continue;
00768        if(pp>p && *(pp-1)=='\\') {
00769            strcpy(pp-1,pp);pp--;continue;
00770        }
00771        if(*pp=='\n') {
00772            string_modify(p,pp,pp+1,"%%0D%%0A");pp+=5;
00773        }
00774        else {
00775            string_modify(p,pp,pp+1,"%%%02X",*pp);pp+=2;
00776        }
00777     }
00778 }
00779 
00780        /* substitute backslash parameters. Internal use only. */
00781 void slashsubst(char *p)
00782 {
00783     char *p1, *p2, *pt, *pp, namebuf[128];
00784     int n;
00785 
00786     n=strlen(mathfont_prefix); memmove(namebuf,mathfont_prefix,n+1);
00787     for(p1=strchr(p,'\\'); p1!=NULL; p1=strchr(p1,'\\')) {
00788        p1++; for(p2=p1; myisalnum(*p2) || *p2=='_'; p2++);
00789        if(p2<=p1 || p2>p1+100) continue;
00790        memmove(namebuf+n,p1,p2-p1); namebuf[p2-p1+n]=0;
00791        pt=_getvar(namebuf); if(pt==NULL) continue;
00792        if(*p2=='[' && (pp=find_matching(p2+1,']'))!=NULL) {
00793            string_modify(p,pp+1,pp+1,")");
00794            string_modify(p,p1-1,p1,"$(%s",mathfont_prefix);
00795        }
00796        else string_modify(p,p1-1,p1,"$%s",mathfont_prefix);
00797     }
00798 }
00799 
00800        /* two alarm handlers. */
00801 void alarm1(int s)
00802 {
00803     if(killpid>0 && kill(killpid,SIGKILL)) module_error("timeup");
00804     killpid=0;
00805 }
00806 
00807 void alarm2(int s)
00808 {
00809     cleantmpdir();
00810     alarm1(s); module_error("timeup");
00811 }
00812 
00813 void finalalarm(void)
00814 {
00815     time_t curr;
00816     curr=time(0);
00817     if(curr>=limtime) alarm2(SIGALRM);
00818     errno=0;
00819     if(signal(SIGALRM,alarm2)==SIG_ERR)
00820       internal_error(strerror(errno));
00821     alarm(limtime-curr+1);
00822 }
00823 
00824 void initalarm(void)
00825 {
00826     limtimex=nowtime+4*rlimit_cpu/3;
00827     limtime=limtimex+2; finalalarm();
00828 }
00829 
00830 void forkalarm(void)
00831 {
00832     time_t curr;
00833     curr=time(0);
00834     if(curr>=limtimex) {alarm1(SIGALRM); return;}
00835     if(signal(SIGALRM,alarm1)==SIG_ERR)
00836       internal_error(strerror(errno));
00837     alarm(limtimex-curr+1);
00838 }
00839 
00840        /* create pid tag */
00841 void create_pid(void)
00842 {
00843     char buf[MAX_FNAME+1], pbuf[256], obuf[MAX_FNAME+1];
00844     struct stat dst;
00845     struct utimbuf ub;
00846     
00847     if(robot_access || *session_prefix==0) return;
00848     if(cmd_type==cmd_getframe) return;
00849     mkfname(buf,"%s/.pid",s2_prefix);
00850               /* another process running? */
00851     if(readfile(buf,pbuf,sizeof(pbuf))!=NULL) {
00852        mkfname(obuf,"/proc/%s",pbuf);
00853        if(stat(obuf,&dst)==0) user_error("double_click");
00854     }
00855     snprintf(pidbuf,sizeof(pidbuf),"%u",getpid());
00856     accessfile(pidbuf,"w","%s",buf);
00857        /* Touch session time */
00858     if(strstr(session_prefix,"sessions/")==NULL) return;
00859     ub.actime=ub.modtime=nowtime;
00860     utime(session_prefix,&ub);
00861     if(strchr(session_prefix,'_')!=NULL) { /* touch parent too */
00862        char sbuf[MAX_FNAME+1], *p;
00863        mystrncpy(sbuf,session_prefix,sizeof(sbuf));
00864        p=strchr(sbuf,'_'); if(p!=NULL) *p=0;
00865        utime(sbuf,&ub);
00866     }
00867 }
00868 
00869 struct {
00870     char cmd[MAX_EXEC_NAME+1];
00871     unsigned int fd1, fd2;
00872     int pipe_stdin[2];
00873     int pipe_stdout[2];
00874     int pipe_stderr[2];
00875 } mxtab[MAX_MULTIEXEC];
00876 int mxno=0;
00877 
00878 int execredirected(char *cmdf, char *inf, char *outf, char *errf, char *arg[])
00879 {
00880     pid_t pid;
00881     int status, t;
00882 
00883     if(robot_access) return 0;
00884     if(time(0)>=limtimex) {
00885        if(errf!=NULL)
00886          accessfile("No time left to execute subprograms.\n","w","%s",errf);
00887        return -100;
00888     }
00889     lastdatafile[0]=lastftest[0]=0;
00890     fflush(NULL);    /* flush all output streams before forking
00891                       * otherwise they will be doubled */
00892     pid=fork(); if(pid==-1) return -1;
00893     if(!pid) {       /* child */
00894        char buf[MAX_LINELEN+1]; int k;
00895        nice(10);     /* lower priority for children */
00896        if(is_multiexec) {
00897            dup2(mxtab[multiexec_index].pipe_stdin[0],0);
00898            dup2(mxtab[multiexec_index].pipe_stdout[1],1);
00899            dup2(mxtab[multiexec_index].pipe_stderr[1],2);
00900        }
00901        else {
00902            if(inf!=NULL) freopen(inf,"r",stdin);
00903            if(outf!=NULL) freopen(outf,"w",stdout);
00904            if(errf!=NULL) freopen(errf,"w",stderr);
00905        }
00906               /* This is to patch LinuxPPC uid wrapping 
00907                * for scripts */
00908        t=0; if(strchr(cmdf,'/')) {
00909            int tf;
00910            char tbuf[16];
00911            tf=open(cmdf,O_RDONLY); read(tf,tbuf,8); close(tf);
00912            if(memcmp(tbuf+1,"ELF",3)!=0) t=1;
00913        }
00914        if(wrapexec==-1) {
00915            setreuid(getuid(),getuid());setregid(getgid(),getgid());
00916        }
00917        if(wrapexec==1 || (t==1 && wrapexec==0)) {
00918            setreuid(geteuid(),geteuid());setregid(getegid(),getegid());
00919        }
00920        errno=0;
00921        if(strchr(cmdf,'/')) execve(cmdf,arg,environ);
00922        else execvp(cmdf,arg);
00923        snprintf(buf,sizeof(buf),"Failed to execute");
00924        for(k=0;arg[k];k++) {
00925            t=strlen(buf);
00926            snprintf(buf+t,sizeof(buf)-t," %s",arg[k]);
00927        }
00928        t=strlen(buf);
00929        snprintf(buf+t,sizeof(buf)-t,"\n   %s\n",strerror(errno));
00930        accessfile(buf,"a","%s/exec.fail",tmp_dir);
00931        exit(127);
00932     }
00933     else {    /* parent */
00934        wrapexec=0; status=0; 
00935        if(exec_wait && !is_multiexec) {
00936            killpid=pid; forkalarm();
00937            waitpid(pid,&status,0); killpid=0; finalalarm();
00938        }
00939        return WEXITSTATUS(status);
00940     }
00941 }
00942 
00943        /* preparation for resident execution.
00944         * Returns 1 if already up, otherwise 0. */
00945 int multiexec(char *cmd, char **abuf)
00946 {
00947     char *p;
00948     int i;
00949     
00950     if(robot_access) return 0;
00951     if(strstr(tmp_dir,"sessions/")==NULL) return 0;
00952     if(strstr(tmp_debug,"yes")!=NULL && checkhost(manager_site)>=1) 
00953       setenv("multiexec_debug","yes",1);
00954     p=getvar("wims_multiexec");
00955     if(p==NULL || wordchr(p,cmd)==NULL) return 0; /* not allowed */
00956     if(!multiexec_random[0]) {
00957        snprintf(multiexec_random, sizeof(multiexec_random),
00958                "%lX%lX%lX%lX%lX%lX%lX%lX",
00959                random(),random(),random(),random(),
00960                random(),random(),random(),random());
00961        setenv("multiexec_random",multiexec_random,1);
00962     }
00963     for(i=0;i<mxno && strcmp(cmd,mxtab[i].cmd)!=0; i++);
00964     multiexec_index=i;
00965     if(i==mxno) {
00966        if(mxno>=MAX_MULTIEXEC) return 0;
00967        if(pipe(mxtab[i].pipe_stdin)<0) return 0;
00968        if(pipe(mxtab[i].pipe_stdout)<0) return 0;
00969        if(pipe(mxtab[i].pipe_stderr)<0) return 0;
00970        mystrncpy(mxtab[i].cmd,cmd,sizeof(mxtab[i].cmd));
00971        mxno++;       is_multiexec=1;
00972        exportall(); setenv("wims_exec_parm",multiexec_random,1);
00973        execredirected(abuf[0],NULL,NULL,NULL,abuf);
00974     }
00975     is_multiexec=0;
00976     return 1;
00977 }
00978 
00979        /* my system(), but with variable parms
00980         * More secure than system(), and direct fork. */
00981 int call_ssh(char *s,...)
00982 {
00983     va_list vp;
00984     char buf[MAX_LINELEN+1];
00985     char *arg[1024];
00986     char *inf=NULL, *outf=NULL, *errf=NULL;
00987     char *cmdf, *p, *p2;
00988     int i, d;
00989 
00990     if(robot_access) return 0;
00991     va_start(vp,s);
00992     vsnprintf(buf,sizeof(buf),s,vp);
00993     va_end(vp);
00994     p=find_word_start(buf); if(*p==0) return 0;
00995     cmdf=p;
00996     for(i=0;*p!=0 && i<1000; p=find_word_start(p2)) {
00997        switch(*p) {
00998            case '\'': {
00999               p++; p2=strchr(p,'\''); if(p2==NULL) p2=p+strlen(p);
01000               d=0; break;
01001            }
01002            case '"': {
01003               p++; p2=strchr(p,'"'); if(p2==NULL) p2=p+strlen(p);
01004               d=0; break;
01005            }
01006            default: d=1; p2=find_word_end(p); break;
01007        }
01008        if(*p2) *p2++=0;
01009        if(!d) {arg[i++]=p; continue;}
01010        switch(*p) {
01011            case '<': inf=++p; break;
01012            case '>': {
01013               p++; if(*p=='&') {
01014                   merge: p++; errf=outf=p; break;
01015               }
01016               else outf=p;
01017               break;
01018            }
01019            case '&': {
01020               p++; if(*p=='>') goto merge;
01021               else break;
01022            }
01023            case '2': {
01024               if(*(p+1)=='>') {errf=p+2; break;}
01025            }
01026            default: arg[i++]=p; break;
01027        }
01028     }
01029     arg[i]=NULL;
01030     return execredirected(cmdf,inf,outf,errf,arg);
01031 }
01032 
01033        /* Read/write to a file with variable parms to print filename */
01034 void accessfile(char *content, char *type, char *s,...)
01035 {
01036     va_list vp;
01037     char buf[MAX_FNAME+1];
01038     int fd;
01039 
01040     if(robot_access) return;
01041     va_start(vp,s);
01042     vsnprintf(buf,sizeof(buf),s,vp);
01043     va_end(vp);
01044     if(strlen(buf)>=MAX_FNAME-1) nametoolong(buf);
01045     switch(*type) {
01046        case 'r': readfile(buf,content,MAX_LINELEN); return;
01047        case 'e': readfile(buf,content,MAX_LINELEN/4); return;  /* limited read */
01048        case 'w': fd=creat(buf,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); break;
01049        case 'a': fd=open(buf,O_WRONLY|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); break;
01050        default : return;
01051     }
01052     lastdatafile[0]=lastftest[0]=0;
01053     if(fd==-1) return;
01054     write(fd,content,strlen(content)); close(fd);
01055 }
01056 
01057        /* system(), but with variable parms
01058         * Uses sh to execute command. */
01059 int call_sh(char *s,...)
01060 {
01061     va_list vp;
01062     char buf[MAX_LINELEN+1];
01063     char *abuf[8];
01064 
01065     if(robot_access) return 0;
01066     va_start(vp,s);
01067     vsnprintf(buf,sizeof(buf),s,vp);
01068     va_end(vp);
01069     abuf[0]="sh"; abuf[1]="-c"; abuf[2]=buf; abuf[3]=NULL;
01070     return execredirected(abuf[0],NULL,NULL,NULL,abuf);
01071 }
01072 
01073 void _getdef(char buf[], char *name, char value[])
01074 {
01075     char *p1, *p2, *p3, *p4;
01076 
01077     if(*name==0) goto nothing;     /* this would create segfault. */
01078     for(p1=strstr(buf,name); p1!=NULL; p1=strstr(p1+1,name)) {
01079        p2=find_word_start(p1+strlen(name));
01080        if((p1>buf && !isspace(*(p1-1))) || *p2!='=') continue;
01081        p3=p1; while(p3>buf && *(p3-1)!='\n') p3--;
01082        p3=find_word_start(p3);
01083        if(p3<p1 && *p3!='!') continue;
01084        if(p3<p1) {
01085            p3++; p4=find_word_end(p3);
01086            if(find_word_start(p4)!=p1) continue;
01087            if(p4-p3!=3 || (strncmp(p3,"set",3)!=0 &&
01088               strncmp(p3,"let",3)!=0 &&
01089               strncmp(p3,"def",3)!=0)) {
01090               if(p4-p3!=6 || strncmp(p3,"define",6)!=0) continue;
01091            }
01092        }
01093        p2++;p3=strchr(p2,'\n'); if(p3==NULL) p3=p2+strlen(p2);
01094        p2=find_word_start(p2);
01095        if(p2>p3) goto nothing;
01096        if(p3-p2>=MAX_LINELEN) user_error("cmd_output_too_long");
01097        memmove(value,p2,p3-p2); value[p3-p2]=0;
01098        strip_trailing_spaces(value); return;
01099     }
01100 nothing:
01101     value[0]=0; return;
01102 }
01103 
01104        /* Get variable definition from a file.
01105         * Result stored in buffer value of length MAX_LINELEN. */
01106 void getdef(char *fname, char *name, char value[])
01107 {
01108     char buf[MAX_LINELEN+1], tbuf[MAX_LINELEN+1], nbuf[MAX_NAMELEN+1];
01109     char *p1, *p2;
01110 
01111     value[0]=0; if(readfile(fname,buf,sizeof(buf)-16)==NULL) return;
01112     mystrncpy(value,name,MAX_LINELEN);
01113     for(p1=value; *p1; p1=p2) {
01114        while(*p1 && !myisalnum(*p1) && *p1!='_') p1++;
01115        if(*p1==0) break;
01116        for(p2=p1; myisalnum(*p2) || *p2=='_'; p2++);
01117        if(p2-p1>MAX_NAMELEN) continue;
01118        memmove(nbuf,p1,p2-p1); nbuf[p2-p1]=0;
01119        _getdef(buf,nbuf,tbuf);
01120        string_modify(value,p1,p2,"%s",tbuf);
01121        p2=p1+strlen(tbuf);
01122     }
01123 }
01124 
01125 int _setdef_changed;
01126 
01127 void _setdef(char buf[], char *name, char *value)
01128 {
01129     char *p1, *p2, *p3;
01130     int n;
01131     
01132     for(p1=strstr(buf,name); p1!=NULL; p1=strstr(p1+1,name)) {
01133        p2=find_word_start(p1+strlen(name));
01134        if((p1>buf && !isspace(*(p1-1))) || *p2!='=') continue;
01135        p3=p1; while(p3>buf && *(p3-1)==' ') p3--;
01136        if(p3>buf && *(p3-1)!='\n') continue;
01137        p2++;p3=strchr(p2,'\n'); if(p3==NULL) p3=p2+strlen(p2);
01138        if(strlen(value)!=p3-p2 || strncmp(value,p2,p3-p2)!=0) {
01139            string_modify(buf,p2,p3,"%s",value);
01140            _setdef_changed++;
01141        }
01142        return;
01143     }
01144     n=strlen(buf);
01145     if(n>0 && buf[n-1]!='\n') 
01146       snprintf(buf+n,MAX_LINELEN-n,"\n%s=%s\n",name,value);
01147     else
01148       snprintf(buf+n,MAX_LINELEN-n,"%s=%s\n",name,value);
01149     _setdef_changed++;
01150 }
01151 
01152        /* Set variable definition to a file. */
01153 void setdef(char *fname, char *name)
01154 {
01155     char buf[MAX_LINELEN+1];
01156     char *p1, *p2, *p3;
01157 
01158     _setdef_changed=0;
01159     if(strchr(name,'=')==NULL) return;
01160     for(p1=name;*p1;p1++) {
01161        if(isspace(*p1) && *p1!=' ' && *p1!='\n') *p1=' ';
01162        if(*p1==' ') {
01163            for(p2=p1+1; isspace(*p2) && *p2!='\n'; p2++);
01164            if(p2>p1+1) strcpy(p1+1,p2);
01165            p2=p1+1; if(*p2=='=' || *p2=='\n') strcpy(p1,p2);
01166        }
01167     }
01168     if(readfile(fname,buf,sizeof(buf))==NULL) buf[0]=0;
01169     for(p1=find_word_start(name); p1!=NULL; p1=p2) {
01170        p2=strchr(p1,'\n'); if(p2!=NULL) *p2++=0;
01171        p1=find_word_start(p1);
01172        p3=strchr(p1,'='); if(p3==NULL) continue;
01173        *p3++=0; p3=find_word_start(p3);
01174        _setdef(buf,p1,p3);
01175     }
01176     if(_setdef_changed) accessfile(buf,"w","%s",fname);
01177 }
01178 
01179        /* check whether connecting host is part of given list.
01180         * Returns 0 if no, 1 if yes. */
01181 int checkhost(char *hlist)
01182 {
01183     char buf[MAX_LINELEN+1];
01184     char lbuf[1024], hbuf1[256], hbuf2[256];
01185     char *p1, *p2, *pb, *pe, *pp;
01186     
01187     if(*remote_addr==0) return 0;
01188     snprintf(hbuf1,sizeof(hbuf1),"+%s+",remote_addr);
01189     if(*remote_host!=0) {
01190        snprintf(hbuf2,sizeof(hbuf2),"+%s+",remote_host);
01191        for(p1=hbuf2; *p1; p1++) *p1=tolower(*p1);
01192     }
01193     else hbuf2[0]=0;
01194     mystrncpy(buf,find_word_start(hlist),sizeof(buf)); strip_trailing_spaces(buf);
01195     for(p1=buf; *p1; p1++) {
01196        *p1=tolower(*p1);
01197        if(!myisalnum(*p1) && strchr(".-_",*p1)==NULL) *p1=' ';
01198     }
01199     if(strcmp(buf,"all")==0) return 1;    /* all is all */
01200     for(p1=find_word_start(buf); *p1; p1=find_word_start(p2)) {
01201        p2=find_word_end(p1); if(*p2) *p2++=0;
01202        if(p2-p1<3) continue;
01203        if(myisalnum(*p1)) pb="+"; else pb="";
01204        if(myisalnum(*(p2-1))) pe="+"; else pe="";
01205        snprintf(lbuf,sizeof(lbuf),"%s%s%s",pb,p1,pe);
01206        for(pp=p1; *pp && (myisdigit(*pp) || *pp=='.'); pp++); 
01207        if(*pp) pp=hbuf2;    /* host name */
01208        else pp=hbuf1;              /* ip number */
01209        if(strstr(pp,lbuf)!=NULL) return 1;       /* found */
01210     }
01211     return 0;
01212 }
01213 
01214        /* return 1 if a word of bf2 is a substring of host.
01215         * Like checkhost, but with time check.
01216         * The content of bf2[] is destroyed. */
01217 int checkhostt(char bf2[])
01218 {
01219     char *p1, *p2, *p3;
01220        /* compare with starting time */
01221     for(p1=strchr(bf2,'>'); p1!=NULL; p1=strchr(p1+1,'>')) {
01222        if(p1>bf2 && !isspace(*(p1-1))) continue;
01223        p3=find_word_start(++p1); p2=find_word_end(p3);
01224        if(p2-p3!=14) continue;
01225        p3[8]='.'; p3[11]=':'; if(*p2) *p2++=0;
01226        if(strncmp(nowstr,p3,14)<0) return 0;
01227        strcpy(p1-1,p2); p1-=2;
01228     }
01229        /* compare with ending time */
01230     for(p1=strchr(bf2,'<'); p1!=NULL; p1=strchr(p1+1,'<')) {
01231        if(p1>bf2 && !isspace(*(p1-1))) continue;
01232        p3=find_word_start(++p1); p2=find_word_end(p3);
01233        if(p2-p3!=14) continue;
01234        p3[8]='.'; p3[11]=':'; if(*p2) *p2++=0;
01235        if(strncmp(nowstr,p3,14)>0) return 0;
01236        strcpy(p1-1,p2); p1-=2;
01237     }
01238     p1=find_word_start(bf2); if(*p1==0) return 1;
01239     return checkhost(p1);
01240 }
01241 
01242        /* bad identification */
01243 void bad_ident(void)
01244 {
01245     if(cookiegot[0]!=0) {
01246     }
01247     user_error("bad_ident");
01248 }
01249 
01250 void instex_flush(void)
01251 {
01252     char *p;
01253     setenv("texgif_style","",1);
01254     setenv("texgif_tmpdir",tmp_dir,1);
01255     setenv("texgif_src",instex_src,1);
01256     setenv("texgif_outfile",instex_fname,1);
01257     unsetenv("w_instex_color");
01258     getwimstexsize=0; fix_tex_size(); getwimstexsize=1;
01259     for(p=instex_fname;*p;p++) if(*p=='\n') *p=' ';
01260     wrapexec=0; call_ssh("%s/%s >%s/ins.Out 2>%s/ins.Err",
01261                       bin_dir,instex_processor,
01262                       tmp_dir,tmp_dir);
01263     call_ssh("mv %s %s >/dev/null 2>/dev/null", instex_fname,s2_prefix);
01264     instex_src[0]=instex_fname[0]=0; instex_cnt=0;
01265 }
01266 
01267        /* put last.phtml */
01268 void putlastout(void)
01269 {
01270     int t;
01271     if(instex_cnt>0) instex_flush();
01272     t=catfile(stdout,"%s/%s",s2_prefix,lastout);
01273     if(t==0) printf("Content-type: text/plain\r\n\r\n");
01274 }
01275 
01276 struct sockaddr_un sun;
01277 
01278        /* returns >=0 if OK. */
01279 int kerneld(char *p, int bufsize)
01280 {
01281     int sock, s, t, t1, l, *ip;
01282     struct timeval tv;
01283     fd_set rset;
01284     
01285     sock=socket(PF_UNIX,SOCK_STREAM,0);
01286     s=connect(sock,(const struct sockaddr *)&sun,sizeof(sun));
01287     if(s) {bad: close(sock); return -1;}
01288     ip=(int *) p;
01289     l=strlen(p+sizeof(int)); *ip=l;
01290     s=write(sock,p,l+sizeof(int)); if(s!=l+sizeof(int)) goto bad;
01291     for(t=0, l=bufsize-1; t<l+sizeof(int);) {
01292        tv.tv_sec=2; tv.tv_usec=0;
01293        FD_ZERO(&rset); FD_SET(sock,&rset);
01294        if(select(sock+1,&rset,NULL,NULL,&tv)<=0) goto bad;
01295        t1=read(sock,p+t,l-t);
01296        if(t1+t<sizeof(int)) goto bad;
01297        l=*ip; if(l<=0) goto bad;
01298        if(l>=bufsize-sizeof(int)-4) user_error("cmd_output_too_long");
01299        t+=t1;
01300     }
01301     p[l+sizeof(int)]=0;
01302     close(sock);
01303     return l+sizeof(int);
01304 }
01305 
01306 void _daemoncmd(char *p)
01307 {
01308     char buf[MAX_LINELEN+1+sizeof(int)];
01309     char *p1, *p2, *p3;
01310     mystrncpy(buf+sizeof(int),p,sizeof(buf)-sizeof(int));
01311     if(kerneld(buf,sizeof(buf))<0) 
01312       internal_error("Daemon communication error.");
01313     p1=find_word_start(buf+sizeof(int));
01314     p2=find_word_end(p1); if(*p2) *p2++=0;
01315     if(strcmp(p1,"OK")==0) {
01316        mystrncpy(p,p2,MAX_LINELEN); return;
01317     }
01318     p1=find_word_start(p2); p2=find_word_end(p1); if(*p2) *p2++=0;
01319     p2=find_word_start(p2); p3=find_word_end(p2); 
01320     if(*p3) {
01321        *p3++=0; p3=find_word_start(p3); strip_trailing_spaces(p3);
01322        setvar("wims_error_data",p3);
01323     }
01324     switch(*p1) {
01325        case '1': user_error(p2);
01326        case '2': module_error(p2);
01327        case '3':
01328        default: internal_error(p2);
01329     }
01330     *p=0;
01331 }
01332