Subversion Repositories wimsdev

Rev

Rev 11726 | Rev 12021 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*    Copyright (C) 1998-2003 XIAO, Gang of Universite de Nice - Sophia Antipolis
  2.  *
  3.  *  This program is free software; you can redistribute it and/or modify
  4.  *  it under the terms of the GNU General Public License as published by
  5.  *  the Free Software Foundation; either version 2 of the License, or
  6.  *  (at your option) any later version.
  7.  *
  8.  *  This program is distributed in the hope that it will be useful,
  9.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11.  *  GNU General Public License for more details.
  12.  *
  13.  *  You should have received a copy of the GNU General Public License
  14.  *  along with this program; if not, write to the Free Software
  15.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16.  */
  17. /* routines to process variables */
  18.  
  19. #include "wims.h"
  20. char *computed_var_start; /* pointer to read-in var def file */
  21. int session_var_ready=0;
  22. char last_host[32]="";
  23. char *robotcheck="";
  24.  
  25. char *header_var_name[]={
  26.     "REMOTE_ADDR", "HTTP_REFERER", "QUERY_STRING", "HTTP_USER_AGENT",
  27.      "HTTP_COOKIE"
  28. };
  29. #define HEADER_VAR_NO (sizeof(header_var_name)/sizeof(header_var_name[0]))
  30.  
  31. struct special_name {char *name; int value;} special_name[]={
  32.   {"MAX_EXAMS",MAX_EXAMS},
  33.   {"MAX_EXOS",MAX_EXOS},
  34.   {"MAX_OEFCHOICES",MAX_OEFCHOICES},
  35.   {"MAX_OEFREPLIES",MAX_OEFREPLIES},
  36.   {"MAX_SHEETS",MAX_SHEETS},
  37.   {"MAX_VOTES",MAX_VOTES},
  38. };
  39. int special_name_no=(sizeof(special_name)/sizeof(special_name[0]));
  40. char *var_allow[]={
  41.     "deny" , "init" , "config" ,
  42.       "reply", "any", "help"
  43. };
  44. enum {
  45.     var_allow_deny, var_allow_init, var_allow_config,
  46.       var_allow_reply, var_allow_any, var_allow_help
  47. } VAR_ALLOWS;
  48. #define VAR_ALLOW_NO (sizeof(var_allow)/sizeof(var_allow[0]))
  49.  
  50. /* install a temporary directory for the session */
  51. void mktmpdir(char *p)
  52. {
  53.     char *base;
  54.     if(p==NULL || *p==0 || strstr(p,"robot")!=NULL) return;
  55.     if(strstr(tmp_dir,"sessions")!=NULL) return;
  56.     if(ftest("../chroot/tmp/sessions/.chroot")==is_file) base="chroot/tmp";
  57.     else base="tmp";
  58.     mkfname(tmp_dir,"../%s/sessions/%s",base,p);
  59.     remove_tree(tmp_dir); mkdirs(tmp_dir);
  60.     chmod(tmp_dir,S_IRUSR|S_IWUSR|S_IXUSR
  61.        |S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
  62.     setenv("tmp_dir",tmp_dir,1); setenv("TMPDIR",tmp_dir,1);
  63. }
  64.  
  65. /* Open session variable file */
  66. FILE *fopen_session_var_file(char read_or_write[])
  67. {
  68.     char *nbuf; FILE *f;
  69.  
  70.     nbuf=mkfname(NULL,"%s/var",session_prefix);
  71.     if(read_or_write[0]=='r') {
  72.      if(open_working_file(&m_file,nbuf)!=0) return NULL;
  73.      mkfname(m_file.name,"session_var");
  74.      return stdin;
  75.     }
  76.     f=fopen(nbuf,read_or_write);
  77.     if(f==NULL) {
  78. /* expired_session */
  79.      internal_error("fopen_session_var_file(): unable to create session variable file.");
  80.     }
  81.     mkfname(m_file.name,"session_var");
  82.     m_file.l=0;
  83.     return f;
  84. }
  85.  
  86. /* Open a module file, read only, with name checking.
  87.  * returns NULL if error.
  88.  */
  89. int read_module_file(char *fname)
  90. {
  91.     char nbuf[MAX_FNAME+1];
  92.  
  93.     if(get_cached_file(fname)>=0) return 0;
  94.     if(find_module_file(fname,nbuf,0)) return -1;
  95.     if(open_working_file(&m_file,nbuf)!=0) return -1;
  96.     untrust|=(untrust>>8);
  97.     mystrncpy(m_file.name,fname,sizeof(m_file.name));
  98.     if(strncmp(module_prefix,module_dir,strlen(module_dir))!=0) m_file.nocache|=8;
  99.     return 0;
  100. }
  101. /* list of variables get from the query parms */
  102. int varget[]={ro_module,ro_lang,ro_useropts,ro_worksheet};
  103. #define varget_no (sizeof(varget)/sizeof(varget[0]))
  104.  
  105. /* set up ref strings according to protocol */
  106. void set_protocol(void)
  107. {
  108.     if(strcmp(protocol,"https")!=0) return;
  109.     if(strncmp(ref_name,"http:",5)==0) {
  110.      string_modify(ref_name,ref_name+4,ref_name+4,"s");
  111.      string_modify(ref_base,ref_base+4,ref_base+4,"s");
  112.      force_setvar("wims_ref_name",ref_name);
  113.     }
  114. }
  115.  
  116. /* verify class participant connection data */
  117. void classlock(void)
  118. {
  119.     int lvl;
  120.     char *p;
  121.  
  122.     p=getvar("wims_classlock"); if(p==NULL) lvl=0; else {
  123.      p=find_word_start(p);
  124.      lvl=*p-'0'; if(lvl<0 || lvl>7) lvl=0;
  125.     }
  126.     if(lvl==7) { /* closed */
  127.      p=getvar("wims_user");
  128.      if(p==NULL || strcmp(p,"supervisor")!=0) user_error("class_closed");
  129.     }
  130.     if(lvl==2 || lvl==4 || lvl==6) { /* https */
  131.      p=getenv("HTTPS");
  132.      if(p==NULL || strcasecmp(p,"on")!=0) user_error("need_https");
  133.     }
  134.     if(lvl==3 || lvl>=5) {
  135.      if(strcmp(last_host,remote_addr)!=0) user_error("bad_host");
  136.     }
  137.     if((lvl==1 || lvl>=4) && cookiegot[0]==0) { /* cookie */
  138.      setcookie=1;
  139.      setvar("cookie_module",getvar(ro_name[ro_module]));
  140.      setvar("cookie_cmd",getvar(ro_name[ro_cmd]));
  141.      force_setvar(ro_name[ro_module],home_module);
  142.      force_setvar(ro_name[ro_cmd],commands[cmd_new]);
  143.      setvar("wims_askcookie","yes");
  144.      return;
  145.     }
  146.     else setcookie=0;
  147. }
  148.  
  149. /* get static session variables */
  150. void get_static_session_var(void)
  151. {
  152.     char *p, *pe, *p2, *p3;
  153.     char sbuf[MAX_FNAME+1], tbuf[MAX_LINELEN+1];
  154.     mystrncpy(sbuf,session_prefix,sizeof(sbuf));
  155.     for(p=sbuf+strlen(sbuf);p>sbuf && *p!='_' && *p!='/'; p--);
  156.     if(p>sbuf && *p=='_') *p=0;
  157.     accessfile(tbuf,"r","%s/var.stat",sbuf);
  158.     p=strrchr(sbuf,'/'); if(p!=NULL) p++; else p=sbuf;
  159.     mktmpdir(p);
  160.     for(p=find_word_start(tbuf);*p;p=find_word_start(pe)) {
  161.      pe=strchr(p,'\n'); if(pe!=NULL) *pe++=0; else pe=p+strlen(p);
  162.      p2=strchr(p,'='); if(p2==NULL) continue;
  163.      *p2++=0; force_setvar(p,p2);
  164.     }
  165.     p=getvar("wims_class"); if(p==NULL || *p==0) return;
  166.     mkfname(class_dir,"%s/%s",class_base,p);
  167.     classlock();
  168.     p3=getvar("wims_class_refcolor"); if(p3!=NULL && *p3!=0)
  169.       force_setvar("wims_ref_bgcolor",p3);
  170.     p3=getvar("wims_class_ref_menucolor"); if(p3!=NULL && *p3!=0)
  171.       force_setvar("wims_ref_menucolor",p3);
  172.     p3=getvar("wims_class_ref_button_color"); if(p3!=NULL && *p3!=0)
  173.       force_setvar("wims_ref_button_color",p3);
  174.     p3=getvar("wims_class_ref_button_bgcolor"); if(p3!=NULL && *p3!=0)
  175.       force_setvar("wims_ref_button_bgcolor",p3);
  176.     p3=getvar("wims_class_ref_button_help_bgcolor"); if(p3!=NULL && *p3!=0)
  177.       force_setvar("wims_ref_button_help_bgcolor",p3);
  178.     p3=getvar("wims_class_ref_button_help_color"); if(p3!=NULL && *p3!=0)
  179.       force_setvar("wims_ref_button_help_color",p3);
  180.     p2=getvar(ro_name[ro_module]);
  181.     if(p2==NULL || strncmp(p2,"classes/",strlen("classes/"))!=0) return;
  182.     mkfname(sbuf,"classes/%s",lang);
  183.     if(strcmp(sbuf,p2)!=0) force_setvar(ro_name[ro_module],sbuf);
  184. }
  185.  
  186. /* set one static session variable */
  187. void set_static_session_var(char *name, char *val)
  188. {
  189.     char *p;
  190.     char sbuf[MAX_FNAME+1], tbuf[MAX_LINELEN+1];
  191.     mystrncpy(sbuf,session_prefix,sizeof(sbuf));
  192.     for(p=sbuf+strlen(sbuf);p>sbuf && *p!='_' && *p!='/'; p--);
  193.     if(p>sbuf && *p=='_') *p=0;
  194.     snprintf(sbuf+strlen(sbuf),sizeof(sbuf)-strlen(sbuf),"/var.stat");
  195.     snprintf(tbuf,sizeof(tbuf),"%s=%s",name,val);
  196.     setdef(sbuf,tbuf); setvar(name,val);
  197. }
  198.  
  199. /* The session is probably robot. */
  200. void robot_doubt(void)
  201. {
  202.     char *h, *p;
  203.  
  204.     p=getvar("special_parm"); h=getvar("module");
  205.     if(p==NULL || h==NULL) {
  206.      bad: user_error("robot_doubt"); return;
  207.     }
  208.     p=find_word_start(p); strip_trailing_spaces(p);
  209.     if(strcmp(p,"wims")!=0 || strcmp(h,home_module)!=0) goto bad;
  210.     set_static_session_var("wims_robotcheck","manual");
  211. }
  212.  
  213. /* User has changed module within an operation.
  214.  * Probably due to robot access.
  215.  */
  216. void bad_module(void)
  217. {
  218.     char *p;
  219.     p=getvar("wims_user"); if(p==NULL) p="";
  220.     if(*p==0 && strcmp(robotcheck,"manual")!=0) set_static_session_var("wims_robotcheck","robot");
  221.     else setvar("wims_human_access","yes");
  222.     user_error("module_change");
  223. }
  224.  
  225. /* returns 1 if session directory exists */
  226. int session_exists(char *s)
  227. {
  228.     if(ftest(mkfname(NULL,"../%s/%s/var",SESSION_BASE,s))==is_file) return 1;
  229.     else return 0;
  230. }
  231.  
  232. /* Check the validity of session number .
  233.  * returns 0 if OK, else -1.
  234.  */
  235. int check_session(void)
  236. {
  237.     char tbuf[MAX_LINELEN+1], vbuf[MAX_LINELEN+1];
  238.     char *p, *pp, *pr;
  239.     int i,m,n,pl,rapid,badmod;
  240.     struct stat st;
  241.  
  242.     rapid=badmod=0; pr="";
  243.     if(fopen_session_var_file("r")==NULL) return -1;
  244.     if(ftest(s2_prefix)!=is_dir) mkdirs(s2_prefix);
  245.     session_var_ready=1; memmove(&svar_file,&m_file,sizeof(WORKING_FILE));
  246. /* REMOTE_ADDR */
  247.     wgetline(vbuf,MAX_LINELEN,&m_file);
  248.     mystrncpy(last_host,vbuf+strlen("REMOTE_ADDR="),sizeof(last_host));
  249.     m_file.linepointer++; /* now it points to query_string */
  250.     pp=getenv("QUERY_STRING");
  251.     if(pp!=NULL && *pp!=0 && strlen(pp)<=MAX_LINELEN) {
  252. /* we compare the query string with the last one. */
  253.      char *p1, *p2;
  254.      wgetline(tbuf,MAX_LINELEN,&m_file);
  255.      p1=tbuf+strlen("QUERY_STRING=");
  256.      if(strncmp(tbuf,"QUERY_STRING=",strlen("QUERY_STRING="))==0 &&
  257.         strcmp(pp,p1)==0 && strstr(session_prefix,"_test")==NULL) {
  258. /* query string does not change */
  259.          if(ftest(mkfname(NULL,"%s/%s",s2_prefix,lastout))==is_file &&
  260.             ftest_size > 0) {
  261.           uselast:
  262.           putlastout(); delete_pid(); exit(0);
  263.          }
  264.          else {
  265.           if(cmd_type==cmd_new || cmd_type==cmd_renew ||
  266.              cmd_type==cmd_reply || cmd_type==cmd_next) {
  267.               cmd_type=cmd_resume;
  268.               force_setvar(ro_name[ro_cmd],"resume");
  269.               forceresume=1;
  270.           }
  271.          }
  272.      }
  273. /* stop rapidfire requests */
  274.      if((cmd_type==cmd_new || cmd_type==cmd_renew) &&
  275.         strncmp(p1,"session=",strlen("session="))==0 &&
  276.         strncmp(pp,"session=",strlen("session="))==0) {
  277.          p1=strchr(p1,'&'); if(p1==NULL) p1="";
  278.          p2=strchr(pp,'&'); if(p2==NULL) p2=""; pr=p2;
  279.          if(strcmp(p1,p2)==0) rapid=1;
  280.      }
  281.     }
  282.     m_file.linepointer=3;
  283.     wgetline(vbuf,MAX_LINELEN,&m_file); /* stored user_agent */
  284. /*    p=getenv("HTTP_USER_AGENT"); if(p==NULL) p="";
  285.     if(strcmp(vbuf+strlen("HTTP_USER_AGENT="),p)!=0) bad_ident(); */
  286.     m_file.linepointer=HEADER_VAR_NO;
  287.     pl=strlen(var_prefix);i=-1;
  288.     while(wgetline(tbuf,MAX_LINELEN,&m_file)!=EOF) {
  289.      if(tbuf[0]==0) break; /* blank line */
  290.      if(strncmp(tbuf,var_prefix,pl)!=0) break;
  291.      i++;if(i>=RO_NAME_NO) break;
  292.      for(n=0;n<varget_no && varget[n]!=i;n++);
  293.      if(n>=varget_no) continue;
  294.      m=pl+strlen(ro_name[i]);
  295.      if(tbuf[m]!='=' || tbuf[m+1]==0) continue;
  296.      if(i==ro_module && cmd_type!=cmd_new && cmd_type!=cmd_intro && cmd_type!=cmd_help) {
  297.          char *pp;
  298.          pp=getvar(ro_name[i]);
  299.          if(pp!=NULL && *pp!=0 && strcmp(pp,tbuf+m+1)!=0) badmod=1;
  300.      }
  301.      if(i==ro_lang && !user_lang)
  302.        force_setvar(ro_name[i],tbuf+m+1);
  303.      else setvar(ro_name[i],tbuf+m+1);
  304.     }
  305. /* recover internal variables */
  306.     do {
  307.      char *v;
  308.      if(tbuf[0]==0) break;
  309.      v=strchr(tbuf,'=');
  310.      if(v==NULL) break; else *(v++)=0;
  311.      if(strncmp(tbuf,var_prefix,strlen(var_prefix))!=0) setenv(tbuf,v,1);
  312.      else if(tbuf[strlen(var_prefix)]) force_setvar(tbuf+strlen(var_prefix),v);
  313.     }
  314.     while(wgetline(tbuf,MAX_LINELEN,&m_file)!=EOF);
  315.     get_static_session_var();
  316.     robotcheck=getvar("wims_robotcheck"); if(robotcheck==NULL) robotcheck="";
  317. /* form access means manual access. Mark this. */
  318.     if(form_access && strcmp(robotcheck,"manual")!=0) {
  319.      robotcheck="manual";
  320.      set_static_session_var("wims_robotcheck","manual");
  321.     }
  322.     else if(strcmp(robotcheck,"robot")==0) robot_doubt();
  323.     if(badmod) bad_module();
  324.     if(cookiegot[0]!=0) {
  325.      p=getvar("wims_sescookie");
  326.      if(p!=NULL && *p!=0 && strcmp(cookiegot,p)!=0) bad_ident();
  327.     }
  328.     p=getvar("wims_sreferer");
  329.     if(p!=NULL && *p!=0) {
  330.      setenv("HTTP_REFERER",p,1); setvar("httpd_HTTP_REFERER",p);
  331.     }
  332.     if(rapid) {
  333.      int rapidfiredelay;
  334.      char *pw, fnbuf[MAX_FNAME+1];
  335. /* Delay: 10 seconds within worksheets, 1 second otherwise. */
  336.      pw=getvar("wims_developer");
  337.      if(pw!=NULL && *pw!=0) goto delcheckend;
  338.      pw=getvar("wims_user");
  339.      if(pw==NULL || *pw==0 || strcmp(pw,"supervisor")==0) rapidfiredelay=1;
  340.      else {
  341.          pw=strstr(pr,"&+worksheet=");
  342.          if(pw!=NULL && myisdigit(*(pw+strlen("&+worksheet=")))) rapidfiredelay=10;
  343.          else rapidfiredelay=2;
  344.      }
  345.      if(ftest(mkfname(fnbuf,"%s/%s",s2_prefix,lastout))==is_file
  346.         && ftest_size > 0
  347.         && stat(fnbuf,&st) == 0
  348.         && st.st_mtime > nowtime-rapidfiredelay && st.st_mtime <= nowtime)
  349.        goto uselast;
  350.     }
  351. /* set protocol string */
  352.     delcheckend: pp=getvar("wims_protocol");
  353.     if(pp!=NULL && strcmp(pp,"https")==0) {
  354.      protocol="https"; set_protocol();
  355.     }
  356.     useropts(); return 0;
  357. }
  358. /* check whether a session is trapped. */
  359. void trap_check(char *s)
  360. {
  361.     char buf[64];
  362.     char *p;
  363.     time_t t1;
  364.  
  365.     setvar("wims_session_expired",s);
  366.     accessfile(tmplbuf,"r","../tmp/log/trap.check");
  367.     if(tmplbuf[0]==0) return;
  368.     p=getenv("REMOTE_ADDR");if(p==NULL) return;
  369.     snprintf(buf,sizeof(buf),":%s,%s,",s,p);
  370.     p=strstr(tmplbuf,buf); if(p==NULL) return;
  371.     p+=strlen(buf);*find_word_end(p)=0;
  372.     t1=atoi(p);
  373.     if(t1>nowtime) user_error("trapped");
  374. }
  375.  
  376. char cars[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  377.  
  378. void set_cookie(void)
  379. {
  380.     #define keylen 20
  381.     char sesbuf[16], keybuf[keylen+8];
  382.     char *p;
  383.  
  384.     p=getvar(ro_name[ro_session]); if(p==NULL) return;
  385.     mystrncpy(sesbuf,p,sizeof(sesbuf));
  386.     if(strchr(sesbuf,'_')==NULL) { /* main session */
  387.      int i;
  388.      for(i=0;i<keylen;i++) keybuf[i]=cars[random()%36];
  389.      keybuf[keylen]=0; cookiegot[0]=0;
  390.      snprintf(cookieset,sizeof(cookieset),"%s-%s",sesbuf,keybuf);
  391.      set_static_session_var("wims_sescookie",cookieset);
  392.      setcookie=1;
  393.     }
  394.     else { /* subsession */
  395.      p=getvar("wims_sescookie"); if(p && *p)
  396.        mystrncpy(cookieset,p,sizeof(cookieset));
  397.     }
  398. }
  399.  
  400. /* create a session number */
  401. void create_session(void)
  402. {
  403.     long t; char session_str[64],*s;
  404.     char *p, ses_dir_buf[MAX_FNAME+1], sesrandbuf[MAX_LINELEN+1];
  405.     int i;
  406.  
  407. /* no session is created for robots */
  408.     if(robot_access) return;
  409.     sesrandbuf[0]=0;
  410. /* If session is given in request_string: use it. */
  411.     s=getvar(ro_name[ro_session]); if(s==NULL) goto creat;
  412.     mystrncpy(session_str,s,sizeof(session_str));
  413.     s=strchr(session_str,'.'); if(s!=NULL) *s=0;
  414.     s=session_str;
  415.     if(*s!=0) {
  416.      int i;
  417.      mkfname(ses_dir_buf,"%s/%s",session_dir,s);
  418.      i=ftest(ses_dir_buf);
  419.      if(i<0) {
  420.          trap_check(s);
  421. /* subsession */
  422.          if(strlen(s)>10 && strchr(s,'_')!=NULL) {
  423.           char *tt;
  424.           tt=strrchr(ses_dir_buf,'_'); if(tt!=NULL) *tt=0;
  425. /* parent session gone. */
  426.           if(ftest(ses_dir_buf)<0) goto creat;
  427.           goto creat2;
  428.          }
  429.          else goto creat;
  430.      }
  431.      if(i!=is_dir) {
  432.          trap_check(s);
  433.          remove_tree(ses_dir_buf); goto creat;
  434.      }
  435.      return;
  436.     }
  437.     creat:
  438.     t=create_job_ident();
  439.     for(i=0;i<MAX_SESRANDOM;i++)
  440.       snprintf(sesrandbuf+strlen(sesrandbuf),
  441.             sizeof(sesrandbuf)-strlen(sesrandbuf),
  442.             "%d,",sesrandomtab[i]);
  443.     sesrandbuf[strlen(sesrandbuf)-1]='\n';
  444.     snprintf(session_str,sizeof(session_str),"%c%c%08lX",
  445.           cars[random()%36],cars[random()%36],t);
  446.     creat2:
  447.     force_setvar(ro_name[ro_session],session_str);
  448.     setsesdir(session_str);
  449. /* check whether the environment is created. */
  450.     s=getvar(ro_name[ro_session]);
  451.     if(s==NULL || strcmp(s,session_str))
  452.       internal_error("cannot_create_session_number");
  453.     snprintf(ses_dir_buf,sizeof(ses_dir_buf)-100,"%s/%s",
  454.           session_dir,session_str);
  455.     if(mkdir(ses_dir_buf,S_IRWXU)==-1)
  456.       internal_error("cannot_create_session_directory");
  457.     mkfname(s2_prefix,"%s/%s",s2_dir,session_str);
  458.     if(mkdir(s2_prefix,S_IRWXU)==-1) mkdirs(s2_prefix);
  459.     mystrncpy(session_prefix,ses_dir_buf,sizeof(session_prefix)); create_pid();
  460.     if(strchr(session_str,'_')==NULL) {
  461.      if((s=getenv("HTTP_REFERER"))!=NULL && *s!=0 && strlen(s)<MAX_FNAME-20
  462.         && strstr(s,"wims")==NULL) {
  463.          char *tt;
  464.          tt=getenv("SERVER_NAME");
  465.          if(tt==NULL || *tt==0 || strstr(s,tt)==NULL)
  466.            set_static_session_var("wims_sreferer",s);
  467.      }
  468.      if(sesrandbuf[0]) set_static_session_var("wims_sesrandom",sesrandbuf);
  469.     }
  470. /* determine http protocol name. How to detect? */
  471.     p=getenv("HTTPS"); if(p!=NULL && strcmp(p,"on")==0) {
  472.       protocol="https"; set_protocol();
  473.     }
  474.     force_setvar("wims_protocol",protocol);
  475.     new_session=1; session_serial=0;
  476.     setvar("wims_new_session","yes");
  477.     if(strchr(session_str,'_')!=NULL) get_static_session_var();
  478.     set_cookie();
  479. }
  480.  
  481. /* Register time of the request. */
  482. void set_req_time(void)
  483. {
  484.     char tstr[64];
  485.  
  486.     snprintf(tstr,sizeof(tstr),"%04d-%02d-%02d.%02d:%02d:%02d=%lu",
  487.          (now->tm_year)+1900, (now->tm_mon)+1, now->tm_mday,
  488.          now->tm_hour, now->tm_min, now->tm_sec, nowtime);
  489.     force_setvar("wims_req_time",tstr);
  490.     if(cmd_type == cmd_new || cmd_type == cmd_renew)
  491.       force_setvar("wims_module_start_time",tstr);
  492.     if(new_session) force_setvar("wims_session_start_time",tstr);
  493. }
  494. /* choose a seed and put in in wims_seed*/
  495. void seed_time(void)
  496. {
  497.     int r; char buf[64];
  498.     struct timeval t;
  499.     gettimeofday(&t,NULL);
  500.     r=t.tv_usec+t.tv_sec*1000;
  501.     if(r<=0) r=1-r;
  502.     snprintf(buf,sizeof(buf),"%d",r);
  503.     force_setvar("wims_seed",buf);
  504. }
  505.  
  506. /* set up module_prefix. */
  507. void set_module_prefix(void)
  508. {
  509.     char tbuf[MAX_FNAME+1], mmbuf[MAX_FNAME+1], *p, *pp, *ps;
  510.     int t,ft;
  511.     struct stat st;
  512.  
  513.     isclassmodule=0;
  514.     p=getvar(ro_name[ro_module]);
  515.     if(p==NULL || *p==0) user_error("no_module_name");
  516. /* security measure: we should not allow users to go back to
  517.  * parent directories.
  518.  */
  519.     if(strstr(p,parent_dir_string)!=NULL) user_error("wrong_module");
  520.     if(strncmp(p,"classes/",strlen("classes/"))==0) isclassmodule=1;
  521.     if(strncmp(p,"devel/",strlen("devel/"))==0) isdevelmodule=1;
  522.     mkfname(module_prefix,"%s/%s",module_dir,p);
  523. /* Now no symbolic link should appear in the module path. */
  524.     mkfname(tbuf,"modules/%s",p);
  525.     for(t=0,ps=pp=strchr(tbuf+strlen("modules/"),'/'); pp;
  526.      *pp='/', ps=pp, pp=strchr(pp+1,'/'), t++) {
  527.      *pp=0; if(lstat(tbuf,&st)) user_error("wrong_module");
  528.      if(t>0 && S_ISLNK(st.st_mode)) {
  529.          if(strcmp(ps,"/local")!=0 ||
  530.             strncmp(tbuf,"modules/home",strlen("modules/home"))==0)
  531.            user_error("wrong_module");
  532.        }
  533.     }
  534. /* Check validity of the module. */
  535.     mkfname(tbuf,"%s/%s",module_prefix,html_file);
  536.     ft=stat(tbuf,&st);
  537.     if(ft!=0 && p[strlen(p)-3]!='.') {
  538.      int i,j;
  539.      char *l;
  540.      l=getvar(ro_name[ro_lang]);
  541.      j=available_lang_no;
  542.      for(i=-1;i<j && ft!=0;i++) {
  543.          if(i<0) mkfname(mmbuf,"%s.%s",p,l);
  544.          else mkfname(mmbuf,"%s.%s",p,available_lang[i]);
  545.          mkfname(module_prefix,"%s/%s",module_dir,mmbuf);
  546.          mkfname(tbuf,"%s/%s",module_prefix,html_file);
  547.          ft=stat(tbuf,&st);
  548.      }
  549.      if(ft==0) force_setvar(ro_name[ro_module],mmbuf);
  550.     }
  551.     if(ft!=0 && !isclassmodule) user_error("wrong_module");
  552.     setenv("module_dir",module_prefix,1); setvar("module_dir",module_prefix);
  553.     module_index();
  554. }
  555.  
  556. /* set up session_prefix. */
  557. int set_session_prefix(void)
  558. {
  559.     char *p, s[32];
  560.  
  561.     if(robot_access) {
  562.      mystrncpy(session_prefix,robot_session,sizeof(session_prefix));
  563.      mystrncpy(s2_prefix,robot_session,sizeof(session_prefix));
  564.      return 0;
  565.     }
  566.     p=getvar(ro_name[ro_session]);
  567.     if(p==NULL || *p==0) user_error("no_session");
  568. /* same reason as for modules */
  569.     if (strchr(p,'/')!=NULL || strstr(p,parent_dir_string)!=NULL
  570.      || *find_word_end(p)!=0) user_error("wrong_session");
  571.     mystrncpy(s,p,sizeof(s));
  572.     p=strchr(s,'.'); if(p!=NULL) *p=0;
  573.     mkfname(session_prefix,"%s/%s",session_dir,s);
  574.     p=strstr(session_prefix,"_mhelp"); if(p!=NULL) *p=0;
  575.     if(ftest(session_prefix)!=is_dir) return -1;
  576.     mkfname(s2_prefix,"%s/%s",s2_dir,s);
  577.     setenv("session_dir",session_prefix,1);
  578.     setenv("s2_dir",s2_prefix,1);
  579.     if(ftest(mkfname(NULL,"%s/.trap",s2_prefix))==is_file)
  580.       user_error("trapped");
  581.     return 0;
  582. }
  583.  
  584. /* check reserved name values in query_string */
  585. void parse_ro_names(void)
  586. {
  587.     int i;
  588.     char *cmd, *p;
  589.     char sesbuf[64];
  590.     create:
  591.     cmd=getvar(ro_name[ro_cmd]);
  592.     if(cmd==NULL || *cmd==0) user_error("no_command");
  593.     for(i=0;i<CMD_NO;i++) if(strcmp(cmd,commands[i])==0) break;
  594.     if(i>=CMD_NO) user_error("bad_command");
  595.     cmd_type=i;
  596.     if(cmd_type == cmd_new) {
  597.      create_session();
  598.      if(set_session_prefix()==0) {
  599.          check_session();
  600.          set_module_prefix();
  601.      }
  602.      else goto redo;
  603.     }
  604.     if (set_session_prefix()==-1 || (cmd_type != cmd_new && check_session())) {
  605.      redo:
  606.      force_setvar(ro_name[ro_cmd],commands[cmd_new]);
  607.      if(strcmp(ro_name[ro_module],home_module)!=0) user_var_no=0;
  608.      goto create;
  609.     }
  610.     if(!new_session) create_pid();
  611.     session_serial++;
  612.     if(robot_access) session_serial=1;
  613.     snprintf(sesbuf,sizeof(sesbuf),"%d",session_serial);
  614.     force_setvar("wims_session_serial",sesbuf);
  615.     p=getvar(ro_name[ro_session]);
  616.     if(p==NULL || *p==0) internal_error("parse_ro_names(): bad session.\n");
  617.     mystrncpy(sesbuf,p,sizeof(sesbuf));
  618.     p=strchr(sesbuf+5,'.'); if(p!=NULL) *p=0;
  619.     mktmpdir(sesbuf);
  620.     if(!robot_access) {
  621.      setsesdir(sesbuf);
  622.      p=strchr(sesbuf,'_');
  623.      if(p!=NULL) force_setvar("wims_subsession",p);
  624.     }
  625.     snprintf(sesbuf+strlen(sesbuf),sizeof(sesbuf)-strlen(sesbuf),
  626.           ".%d",session_serial);
  627.     force_setvar(ro_name[ro_session],sesbuf);
  628.     if(cmd_type != cmd_new) set_module_prefix();
  629.     set_req_time();
  630.     if(robot_access) check_load(0);
  631.     else {
  632.      if(new_session) auth();
  633.      else {
  634.          p=getvar("wims_user"); if(p==NULL || *p==0) check_load(2);
  635.      }
  636.     }
  637.     if(cmd_type==cmd_help && open_working_file(&m_file,module_about_file)==0)
  638.       var_proc(NULL,0);
  639. }
  640.  
  641.  
  642. /* returns positive or 0 if var_def found, otherwise returns -1. */
  643. int var_def_check(char *name)
  644. {
  645.     char *p, nbuf[MAX_NAMELEN+1];
  646.     int i,tt;
  647.  
  648.     tt=-1;
  649.     for(p=name+strlen(name);p>name && myisdigit(*(p-1));p--);
  650.     if(*p && *p!='0' && p>name) tt=atoi(p);
  651.     else p=name+strlen(name);
  652.     if(p>name+MAX_NAMELEN) p=name+MAX_NAMELEN;
  653.     memmove(nbuf,name,p-name); nbuf[p-name]=0;
  654.     i=search_list(var_def,defined_var_total,sizeof(var_def[0]),nbuf);
  655.     if(i<0) return -1;
  656.     while(i>0 && tt<var_def[i].beg && strcmp(nbuf,var_def[i-1].name)==0) i--;
  657.     while(i<defined_var_total-1 && tt>var_def[i].end &&
  658.            strcmp(nbuf,var_def[i+1].name)==0) i++;
  659.     if(tt<var_def[i].beg || tt>var_def[i].end) return -1;
  660.     return i;
  661. }
  662.  
  663. int var_def_name(char *n, int v)
  664. {
  665.     char *q, *r;
  666.     int j;
  667.     if(strlen(n)>=MAX_NAMELEN) module_error("defn_too_long");
  668.     var_def[v].name=n;
  669.     if((strncmp(n,wims_prefix,wpflen)==0 &&
  670.      (strncmp(n,"wims_priv_",strlen("wims_priv_"))==0 ||
  671.       search_list(internal_name,INTERNAL_NAME_NO,
  672.                sizeof(internal_name[0]),n+wpflen)>=0)) ||
  673.        search_list(ro_name,RO_NAME_NO,sizeof(ro_name[0]),n)>=0) {
  674.      setvar("wims_reserved_name",n);
  675.      module_error("name_is_reserved");
  676.     }
  677.     for(q=n;myisalnum(*q) || *q=='_'; q++);
  678.     if(q==n) {
  679.      illegal: setvar("wims_bad_name",n);
  680.      module_error("illegal_name");
  681.     }
  682.  
  683.     if(*q=='[') {
  684.      *q++=0; r=find_matching(q,']');
  685.      if(r==NULL) goto illegal;
  686.      *r=0; int i;
  687.      for(i=0;i<special_name_no && strcmp(q, special_name[i].name)!=0; i++);
  688.      if(i<special_name_no) j=special_name[i].value; else j=atoi(q);
  689.      if(j<1) j=1;
  690.      if(j>MAX_VAR_NUM) j=MAX_VAR_NUM;
  691.      var_def[v].beg=1; var_def[v].end=j;
  692.      return j;
  693.     }
  694.     if(*q) goto illegal;
  695.     for(r=q; r>n && myisdigit(*(r-1)); r--);
  696.     if(*r && *r!='0' && r>n) {
  697.      var_def[v].beg=var_def[v].end=atoi(r); *r=0;
  698.     }
  699.     else var_def[v].beg=var_def[v].end=-1;
  700.     return 1;
  701. }
  702.  
  703. int var_def_allow(char *p, int v)
  704. {
  705.     int i;
  706.     for(i=0;i<VAR_ALLOW_NO && strcasecmp(p,var_allow[i])!=0; i++);
  707.     if(i>=VAR_ALLOW_NO) module_error("bad_allow");
  708.     else var_def[v].allow=i;
  709.     return i;
  710. }
  711.  
  712. int varsort(const void *p1, const void *p2)
  713. {
  714.     int i; const struct VAR_DEF *pp1, *pp2;
  715.     pp1=p1; pp2=p2; i=strcmp(pp1->name,pp2->name);
  716.     if(i) return i; else return pp1->end - pp2->end;
  717. }
  718.  
  719.      /* parse module's variable definitions */
  720. void get_var_defs(void)
  721. {
  722.     int i, j, k, v, add;
  723.     char *p, *p1, *wlist[MAX_VAR_NUM];
  724.  
  725.     defined_var_total=0;
  726.     if(read_module_file(var_def_file)!=0) return;
  727.     var_def_buf=m_file.textbuf;
  728.     for(m_file.l=v=add=0;v<MAX_VAR_NUM && m_file.l<m_file.linecnt;m_file.l++) {
  729.      if(m_file.lines[m_file.l].isstart!=1) continue;
  730.      p=find_word_start(m_file.lines[m_file.l].address);
  731.      if(*p==0 || *p==comment_prefix_char) continue;  /* empty or comment lines */
  732.      items2words(p);
  733.      if((p1=strchr(p,':'))!=NULL) {  /* new format */
  734.          *p1=' '; i=cutwords(p,wlist,MAX_VAR_NUM); if(i<=1) continue;
  735.          k=var_def_allow(wlist[0],v);
  736.          for(j=1;j<i && v<MAX_VAR_NUM;j++) {
  737.           add+=var_def_name(wlist[j],v); var_def[v].allow=k;
  738.           v++;
  739.          }
  740.      }
  741.      else {
  742.          i=cutwords(p,wlist,3); if(i<2) module_error("too_few_columns");
  743.          add+=var_def_name(wlist[0],v); var_def_allow(wlist[1],v);
  744.          var_def[v].defined_in_parm=0;
  745.          v++;
  746.      }
  747.     }
  748.     if(v>=MAX_VAR_NUM) module_error("too_many_variables");
  749.     defined_var_total=v;
  750.     qsort(var_def,v,sizeof(var_def[0]),varsort);
  751.     p=getvar(ro_name[ro_module]);
  752.     if(p==NULL || strncmp(p,"devel/",6)!=0) return;
  753.     for(v=1;v<defined_var_total;v++) {
  754.      if(strcmp(var_def[v].name,var_def[v-1].name)==0 &&
  755.         var_def[v].beg<=var_def[v-1].end) {
  756.          setvar("wims_bad_name",var_def[v].name);
  757.          module_error("multiple_declaration");
  758.      }
  759.     }
  760. }
  761.  
  762. /* returns 1 if hacked, else 0. */
  763. int try_hack(char *var)
  764. {
  765.     int i, al;
  766.     char vbuf[16];
  767.     i=var_def_check(var);
  768.     if(i<0) return 0;
  769.     al=var_def[i].allow;
  770.     if(al != var_allow_any) switch(cmd_type) {
  771.      case cmd_new:
  772.      case cmd_renew: {
  773.          if (al != var_allow_init && al != var_allow_config) return 0;
  774.          else break;
  775.      }
  776.      case cmd_config: {
  777.          if(al != var_allow_config) return 0;
  778.          else break;
  779.      }
  780.      case cmd_reply: {
  781.          if(al != var_allow_reply) return 0;
  782.          else break;
  783.      }
  784.      case cmd_help: {
  785.          if(al != var_allow_help) return 0;
  786.          else break;
  787.      }
  788.      default: return 0;
  789.     }
  790.     snprintf(vbuf,sizeof(vbuf),"%d",(int) irand(21));
  791.     var_hacking=1; setvar(var,vbuf); var_hacking=0;
  792.     return 1;
  793. }
  794.  
  795. /* set environ variables from last session save
  796.  * The session var file starts with variables which should not
  797.  * be restored. Variables which are restored follow a blank line. */
  798. void set_vars_from_session(void)
  799. {
  800.     char lbuf[MAX_LINELEN+1];
  801.     int i;
  802.     char *p;
  803.  
  804.     if(session_var_ready) memmove(&m_file,&svar_file,sizeof(WORKING_FILE));
  805.     else fopen_session_var_file("r");
  806. /* look for the first blank line. */
  807.     for(i=0;i<m_file.linecnt && (m_file.lines[i].isstart==0 || m_file.lines[i].llen>0);i++);
  808.     for(i++;i<m_file.linecnt && m_file.lines[i].llen==0;i++);
  809.     if(i>=m_file.linecnt) return;
  810.     m_file.linepointer=i;
  811.     if(isdevelmodule && strstr(session_prefix,"_test")==NULL) isdevelmodule=0;
  812.     while(wgetline(lbuf,MAX_LINELEN, &m_file)!=EOF) {
  813.      p=strchr(lbuf,'=');
  814.      if(p==NULL || p<=lbuf || isspace(lbuf[0]) ) {
  815. /* this time it is corrupted var file */
  816.        call_ssh("cat %s/var >%s/corrupt.var.bak",session_prefix,log_dir);
  817.        internal_error("get_vars_from_session(): corrupt session variable file.");
  818.      }
  819.      *p=0;p++;
  820. /* Here we suppose that nobody can tamper session variable
  821.  * file, and do not check variable names against module's
  822.  * definition file. Policy under reserve. */
  823. /* We do not allow override though.
  824.  * Especially because reply variables should
  825.  * be preserved. */
  826.      if(strncmp(lbuf,var_prefix,strlen(var_prefix))!=0)
  827.        setenv(lbuf,p,0);
  828.      else if(lbuf[strlen(var_prefix)]!=0 && getvar(lbuf+strlen(var_prefix))==NULL) {
  829.          if(!isdevelmodule || !try_hack(lbuf+strlen(var_prefix))) {
  830.           setvar(lbuf+strlen(var_prefix),p);
  831.          }
  832.      }
  833.     }
  834.     close_working_file(&m_file,0);
  835. }
  836.  
  837. void start_tracefile(void)
  838. {
  839.   int i;
  840.   putc('\n',trace_file);
  841.   for(i=1; i<=2*trace_indent; i++) putc(' ',trace_file);
  842.   fprintf(trace_file,"%s:",m_file.filepath);
  843.   fflush(trace_file);
  844.   trace_indent++;
  845. }
  846.  
  847. void stop_tracefile(void)
  848. {
  849.   trace_indent--;
  850. }
  851.  
  852. /* Initialize environment variables according to module's
  853.  * variable init or calculation file.
  854.  * init is only used when cmd=new or renew.
  855.  * Requires get_var_defs be run first. */
  856. void var_proc(char *fname,int cache)
  857. {
  858.     int  t;
  859.     char *p, tbuf[MAX_LINELEN+1];
  860.  
  861.     if(fname!=NULL && read_module_file(fname)) return;
  862.     if(untrust&6) get_var_privileges();
  863.     if (trace_file) start_tracefile();
  864.     while(m_file.linepointer<m_file.linecnt) {
  865.      if (trace_file) {fprintf(trace_file," %d",m_file.linepointer+1);
  866.        fflush(trace_file); }
  867.      t=m_file.lines[m_file.linepointer].isstart;
  868.      if((t&~2)!=1 || m_file.lines[m_file.linepointer].llen==0) {
  869.          m_file.linepointer++; continue;
  870.      }
  871.      wgetline(tbuf,MAX_LINELEN,&m_file); substnest=0;
  872.      p=find_word_start(tbuf); if(*p==0) continue;
  873.      if((t&2)!=0) exec_main(p+1);
  874.      else exec_set(p);
  875.     }
  876.     if (trace_file) stop_tracefile();
  877.     close_working_file(&m_file,cache);
  878. }
  879.  
  880. /* Deposit the content of wims_deposit into a file */
  881. void var_deposit(char *p)
  882. {
  883.     char fn[MAX_FNAME+1];
  884.     int l,fd;
  885.     if(!trusted_module()) return;
  886.     if(deplen>0) l=deplen; else {
  887.      while(isspace(*p)) p++;
  888.      l=strlen(p);
  889.     }
  890.     if(l<=0) return;
  891.     if(l>MAX_DEPOSITLEN) l=MAX_DEPOSITLEN; /* silent truncation, should not occur */
  892.     mkfname(fn,"%s/user-deposit",session_prefix);
  893.     fd=creat(fn,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if(fd==-1) return;
  894.     write(fd,p,l); close(fd);
  895.     snprintf(fn,sizeof(fn),"%u",l); setvar("wims_deposit_len",fn);
  896. }
  897.  
  898. /* Check and set variables passed in query_string */
  899. void set_vars_from_parm(void)
  900. {
  901.     int i,j,al;
  902.     char *s, vbuf[MAX_LINELEN+1];
  903.     if(forceresume) return;
  904.     for(i=0; i<user_var_no; i++) {
  905.      j=var_def_check(user_variable[i].name);
  906.      if(j<0) continue;
  907. /* check permissions */
  908.      al=var_def[j].allow;
  909.      if(al != var_allow_any) switch(cmd_type) {
  910.          case cmd_new:
  911.          case cmd_renew:
  912.              if (al != var_allow_init && al != var_allow_config) {
  913. violat:        /* setvar(error_data_string,user_variable[i].name);
  914.               user_error("allow_violation"); */
  915.               goto loopend;
  916.           }
  917.              break;
  918.  
  919.          case cmd_config:
  920.              if(al != var_allow_config) goto violat;
  921.              break;
  922.  
  923.          case cmd_reply:
  924.               if(al != var_allow_reply) goto violat;
  925.              break;
  926.  
  927.          case cmd_help:
  928.               if(al != var_allow_help) goto violat;
  929.               break;
  930.  
  931.          default: goto violat;
  932.      }
  933.      var_def[j].defined_in_parm=1;
  934.      if(strcmp(user_variable[i].name,"wims_deposit")==0) {
  935.          var_deposit(user_variable[i].value); continue;
  936.      }
  937.      mystrncpy(vbuf,user_variable[i].value,sizeof(vbuf));
  938.      if(strchr(vbuf,'$')!=NULL) {
  939.          char *p;
  940.          while((p=strchr(vbuf,'$'))!=NULL)
  941.            string_modify(vbuf,p,p+1,"&#36;");
  942.      }
  943.      s=getvar(user_variable[i].name);
  944.      if(s==NULL || *s==0) setvar(user_variable[i].name, vbuf);
  945.      else {  /* concatenate */
  946.          int k;
  947.          k=strlen(s)+strlen(vbuf);
  948.          if(k>=MAX_LINELEN-2) user_error("string_too_long");
  949.          snprintf(tmplbuf,sizeof(tmplbuf),"%s, %s",s,vbuf);
  950.          setvar(user_variable[i].name, tmplbuf);
  951.      }
  952.      loopend: ;
  953.     }
  954. }
  955.  
  956. /* parms to be eliminated from module_init_parm */
  957. /* char *init_elim[]={
  958.     "module","cmd","session","lang","worksheet","wims_access","useropts"
  959. };
  960. #define init_elim_no (sizeof(init_elim)/sizeof(init_elim[0]))
  961. */
  962.  
  963. void elim_parm(char *str, char *parm)
  964. {
  965.     char *p1, *p2;
  966.     for(p1=strstr(str,parm);p1!=NULL;p1=strstr(p1+1,parm)) {
  967.      if( (p1>str && *(p1-1)!='&') || *(p1+strlen(parm))!='=')
  968.        continue;
  969.      p2=strchr(p1,'&');
  970.      if(p2==NULL) {
  971.          if(p1>str) *(p1-1)=0; else *p1=0;
  972.          return;
  973.      }
  974.      ovlstrcpy(p1,p2+1); p1--;
  975.     }
  976. }
  977.  
  978. /* eliminate technical definitions form parameter string
  979.  * that is parameters from the list RO_NAMES
  980.  */
  981. void prep_init_parm(char rqv[])
  982. {
  983.     int i;
  984.     char *p;
  985.  
  986.     for(p=strstr(rqv,"&+"); p!=NULL; p=strstr(++p,"&+"))
  987.       ovlstrcpy(p+1,p+2);
  988.     for(i=0;i<RO_NAME_NO;i++) elim_parm(rqv,ro_name[i]);
  989.     if(strlen(rqv)>=MAX_LINELEN) rqv[0]=0;
  990.     while(rqv[0]=='&') ovlstrcpy(rqv,rqv+1);
  991.     while(rqv[0]!=0 && rqv[strlen(rqv)-1]=='&') rqv[strlen(rqv)-1]=0;
  992. }
  993.  
  994. /* retain initializing parameters, for use in user references */
  995. void set_init_parm(void)
  996. {
  997.     char *rq, rqv[MAX_LINELEN*2+2], *u, *sh, *seedr;
  998.     char *shname;
  999.     int public_sheet;
  1000.  
  1001.     if(isexam) return;
  1002.     force_setvar("wims_sheet",""); force_setvar("wims_exo","");
  1003.   /*  force_setvar("wims_seed_repeat","");*/
  1004.     rq=getenv("QUERY_STRING");
  1005.     if(rq==NULL || *rq==0) {
  1006.      empty:
  1007.      setvar("module_init_parm",""); return;
  1008.     }
  1009.     if(strlen(rq)>=MAX_LINELEN*2) goto empty;
  1010.     _http2env(rqv,rq); prep_init_parm(rqv);
  1011.     setvar("module_init_parm",rqv); public_sheet=0;
  1012.     /* seedrepeat=1 must be in the url and seedrepeat in the var.def of the module
  1013.      to be used */
  1014.     seedr=getvar("seedrepeat");
  1015.     force_setvar("wims_seed_repeat",seedr);
  1016. /* now determine the sheet number for user */
  1017.     sh=getvar(ro_name[ro_worksheet]); if(sh==NULL) return;
  1018.     if(*sh=='P') {public_sheet=1; sh++;}
  1019.     shname="sheet";
  1020.     u=getvar("wims_user"); if(u==NULL) u="";
  1021.     if(sh!=NULL && *sh!=0) {
  1022.      char buf[MAX_LINELEN+1],ubuf[32], nbuf[1024], *c, *m;
  1023.      char *p1,*p2,*p3,*p4,*p5;
  1024.      int i,j,sheet;
  1025.      sheet=atoi(sh); if(sheet<=0 || sheet>256) return;
  1026.      m=getvar(ro_name[ro_module]);
  1027.      if(m==NULL) internal_error("set_init_parm(): module name disapears.");
  1028.      if(*u==0) public_sheet=1;
  1029.      if(!public_sheet) {
  1030.          c=getvar("wims_class"); if(c==NULL) c="";
  1031.          snprintf(nbuf,sizeof(nbuf),"%s/sheets/.%s%d",
  1032.                class_dir,shname,sheet);
  1033.      }
  1034.      else {
  1035.          char bf[MAX_LINELEN+1];
  1036.          int i;
  1037.          accessfile(bf,"r","%s/.sheets",session_prefix);
  1038.          if(bf[0]==0) return;
  1039.          for(i=1, p1=bf;i<sheet;i++,p1=p2) {
  1040.           p2=strchr(p1,'\n');
  1041.           if(p2!=NULL) *p2++=0; else p2=p1+strlen(p1);
  1042.          }
  1043.          p2=strchr(p1,'\n'); if(p2) *p2=0;
  1044.          snprintf(nbuf,sizeof(nbuf),"bases/sheet/%s.def",p1);
  1045.      }
  1046.      if(readfile(nbuf,buf,sizeof(buf))==NULL) return;
  1047.      for(p1=strstr(buf,"&+");p1!=NULL;p1=strstr(++p1,"&+"))
  1048.          ovlstrcpy(p1+1,p1+2);
  1049.      if(strncmp(m,"classes/",strlen("classes/"))==0) {
  1050.          m="classes/";
  1051.          for(p1=strstr(buf,":classes/");p1;p1=strstr(p1+1,":classes/")) {
  1052.           if(p1==buf || *(p1-1)=='\n') {
  1053.               p1+=strlen(":classes/");
  1054.               p2=find_word_end(p1); if(p2>p1 && *p2=='\n') ovlstrcpy(p1,p2);
  1055.           }
  1056.          }
  1057.      }
  1058.      snprintf(nbuf,sizeof(nbuf),":%s\n%s\n",m,rqv);
  1059.      p1=strstr(buf,nbuf);
  1060.      while(p1>buf && *(p1-1)!='\n') p1=strstr(p1+1,nbuf);
  1061.      if(p1!=NULL) {
  1062.          p2=strchr(buf,':');
  1063.          while(p2>buf && *(p2-1)!='\n') p2=strchr(p2+1,':');
  1064.          for(i=1;p2!=NULL && p2<p1;i++) {
  1065.           p2=strchr(p2+1,':');
  1066.           while(p2>buf && *(p2-1)!='\n') p2=strchr(p2+1,':');
  1067.          }
  1068.          if(p2==NULL) return; /* error which should not occur */
  1069.          snprintf(ubuf,sizeof(ubuf),"%d",i);
  1070. /* look for dependency information */
  1071.          for(j=0, p3=strchr(p1+strlen(nbuf),'\n');
  1072.           j<3 && p3 && *(p3+1)!=':';
  1073.           j++, p3=strchr(p3+1,'\n'));
  1074.          if(j>=3 && p3!=NULL && *(p3+1)!=':') {
  1075.           p3++; p4=strchr(p3,'\n');
  1076.           if(p4) {
  1077.               *p4++=0; if(*p4!=':') { /* options */
  1078.                p5=strchr(p4,'\n'); if(p5) *p5=0;
  1079.                force_setvar("wims_exoption",p4);
  1080.               }
  1081.           }
  1082.           p3=find_word_start(p3); strip_trailing_spaces(p3);
  1083. /* non-empty dependency information */
  1084.           if(*p3 && !public_sheet) {
  1085.               exodepOK=depcheck(sh,i,p3);
  1086.               if(!exodepOK) setvar("wims_exodep","pending");
  1087.           }
  1088.          }
  1089.          if(public_sheet) {
  1090.           char bf[32];
  1091.           snprintf(bf,16,"P%s",sh);
  1092.           force_setvar("wims_sheet",bf);
  1093.          }
  1094.          else force_setvar("wims_sheet",sh);
  1095.          force_setvar("wims_exo",ubuf);
  1096.          wims_sheet=sheet; wims_exo=i;
  1097.      }
  1098.     }
  1099. }
  1100.  
  1101. /* user with class: whether exercise is registered
  1102.  * Returns 1 if got, 0 otherwise. */
  1103. int get_parmreg(void)
  1104. {
  1105.     char *p, *cl, *u, nbuf[MAX_FNAME+1];
  1106.     struct stat st;
  1107.  
  1108.     u=getvar("wims_user"); cl=getvar("wims_class");
  1109.     if(u==NULL || cl==NULL || *u==0 || *cl==0 || strcmp(u,"supervisor")==0
  1110.        || wims_sheet<=0 || wims_exo<=0) return 0;
  1111.     mkfname(nbuf,"%s/.parmreg/%s.%d.%d", class_dir,u,wims_sheet,wims_exo);
  1112.     p=getvar("wims_scorereg"); if(p!=NULL && strcmp(p,"suspend")==0) {
  1113.      unlink(nbuf); return 0;
  1114.     }
  1115.     if(stat(nbuf,&st)) return 0;
  1116. /* latency is 10 min. */
  1117.     if(st.st_mtime<nowtime-600 || st.st_mtime > nowtime) {
  1118.      unlink(nbuf); return 0;
  1119.     }
  1120.     if(open_working_file(&m_file,nbuf)!=0) return 0;
  1121.     mkfname(m_file.name,"parmreg/%s.%d.%d",u,wims_sheet,wims_exo);
  1122.     while(wgetline(tmplbuf,MAX_LINELEN, &m_file)!=EOF) {
  1123.      p=strchr(tmplbuf,'=');
  1124.      if(p==NULL || p<=tmplbuf || isspace(tmplbuf[0]) )
  1125. /* this time it is corrupted var file */
  1126.        internal_error("get_parmreg(): corrupt parmreg file.");
  1127.      *p=0;p++;
  1128.      if(strncmp(tmplbuf,var_prefix,strlen(var_prefix))!=0) setenv(tmplbuf,p,1);
  1129.      else if(tmplbuf[strlen(var_prefix)]!=0) force_setvar(tmplbuf+strlen(var_prefix),p);
  1130.     }
  1131.     parm_restore=1;
  1132.     close_working_file(&m_file,0); return 1;
  1133. }
  1134.  
  1135. /* reset seed according to the values of wims_seed_repeat and
  1136.   wims_seed_score
  1137.  */
  1138. void reset_seed (void) {
  1139.      char *p, *seedr, buf[64];
  1140.      seedr=getvar("wims_seed_repeat");
  1141.      if(seedr!=NULL && atoi(seedr)>0) {
  1142.        p=getvar("wims_seed_score");
  1143.         if(evalue(p)>=10) {
  1144.           seed_time(); force_setvar("wims_seed_score",NULL);
  1145.        }
  1146.      }
  1147.      else seed_time();
  1148.      p=getvar("wims_seed");
  1149.      if(p!=NULL && (cmd_type==cmd_new || cmd_type==cmd_renew || cmd_type==cmd_next)){
  1150.         mystrncpy(buf,p,sizeof(buf)); setenv("tmp_seed",buf,1);
  1151.         exec_setseed(buf);
  1152.     }
  1153. }
  1154.  
  1155. /* set environment variables */
  1156. void set_variables(void)
  1157. {
  1158.     outputing=0; readnest=0;
  1159.     get_var_defs();
  1160.     set_vars_from_parm();
  1161.     if(cmd_type != cmd_new && cmd_type != cmd_renew) {
  1162.        set_vars_from_session();
  1163.        reset_seed();
  1164.     }
  1165.     else {
  1166.      set_init_parm();
  1167.      reset_seed();
  1168.      if(wims_sheet>0 && get_parmreg()) {
  1169.          cmd_type=cmd_resume; force_setvar("cmd","resume");
  1170.          var_proc(main_var_proc_file,0); return;
  1171.      }
  1172.      checkrafale();
  1173.      var_proc(var_init_file,0);
  1174.     }
  1175. /* check_var_bounds(); */
  1176.     var_proc(main_var_proc_file,0);
  1177. }
  1178.  
  1179. /* Output a phtml file. */
  1180. void phtml_put(char *fname,int cache)
  1181. {
  1182.     int t;
  1183.     char tbuf[MAX_LINELEN+1];
  1184.  
  1185.     outputing=1;
  1186.      /* File not found; we give empty output, but no error message. */
  1187.     if(fname!=NULL && read_module_file(fname)!=0) return;
  1188.     if(untrust&6) get_var_privileges();
  1189.     if (trace_file) start_tracefile();
  1190.     while(m_file.linepointer<m_file.linecnt) {
  1191.          if (trace_file) { fprintf(trace_file," %d",m_file.linepointer+1);
  1192.                            fflush(trace_file); }
  1193.      t=m_file.lines[m_file.linepointer].isstart;
  1194.      if((t&~18)!=1) {m_file.linepointer++; continue;}
  1195.      wgetline(tbuf,MAX_LINELEN,&m_file); substnest=0;
  1196.      if((t&2)!=0) {exec_main(tbuf+1); continue;}
  1197.      substit(tbuf); output0(tbuf); _output_("\n");
  1198.     }
  1199.     if (trace_file) stop_tracefile();
  1200.     close_working_file(&m_file,cache);
  1201. }
  1202.  
  1203. /* output a file in base html directory. Internal use only. */
  1204. void phtml_put_base(char *fname,int cache)
  1205. {
  1206.     WORKING_FILE save;
  1207.     char modsave[MAX_FNAME+1];
  1208.     memmove(&save,&m_file,sizeof(WORKING_FILE));
  1209.     mystrncpy(modsave,module_prefix,sizeof(modsave));
  1210.     ovlstrcpy(module_prefix,"html");
  1211.     phtml_put(fname,cache);
  1212.     mystrncpy(module_prefix,modsave,sizeof(module_prefix));
  1213.     memmove(&m_file,&save,sizeof(WORKING_FILE));
  1214. }
  1215.  
  1216. /* Read main.phtml, process it, and write to stdout. */
  1217. void main_phtml_put(char *mname)
  1218. {
  1219.     char *p, buf[1024], txbuf[256], dirnbuf[256], bgbuf[256], spfbuf[256], ebuf[256];
  1220.     char *bcolor, *refcolor, *bg, *tx, *dirn, *vlink, *link, *spf ;
  1221.     define_html_header(); readnest=0;
  1222.     nph_header(200);
  1223.     p=getvar("wims_backslash_insmath");
  1224.     if(p!=NULL && strcasecmp(p,"yes")==0) backslash_insmath=1;
  1225.     p=getvar("wims_expire");
  1226.     if(p!=NULL) p=strstr(p,"no-cache");
  1227.     if(p!=NULL) _output_("Cache-Control: no-cache\r\nPragma: no-cache\r\n");
  1228. /* expiration date */
  1229.     if(mode==mode_default) {
  1230.       if(!robot_access && (cmd_type!=cmd_intro || p!=NULL)) {
  1231.         char *expir="Sat, 26 Jul 1990 05:00:00 GMT";
  1232.         snprintf(ebuf,sizeof(ebuf),"%s",expir);
  1233.       }
  1234.       else {
  1235. /* expiration in 10 days for robot access or intro page. */
  1236.         time_t t=nowtime+(long) 10*24*3600;
  1237.         { struct tm *tt = localtime(&t);
  1238.           strftime(ebuf,sizeof(ebuf),"%a, %d %b %Y %T GMT", tt);
  1239.         }
  1240.       }
  1241.       strip_trailing_spaces(ebuf);
  1242.       output("Expires:%s\r\n",ebuf);
  1243.     }
  1244.  
  1245.     output("Server: %s %s (%s)\n", SHORTSWNAME,wims_version,LONGSWNAME);
  1246.     if(!robot_access && strcasecmp(usecookie,"yes")==0 && setcookie
  1247.        && mode!=mode_popup) {
  1248.      if(cookieset[0]==0) {
  1249.          p=getvar("wims_sescookie");
  1250.          if(p!=NULL && *p!=0) mystrncpy(cookieset,p,sizeof(cookieset));
  1251.      }
  1252.      output("Set-Cookie: %s%s; path=/\r\n",cookieheader,cookieset);
  1253.     }
  1254.     p=getvar("wims_main_font");
  1255.     if(p!=NULL && *p!=0) output("Content-type: text/html; charset=%s\r\n\r\n",p);
  1256.     else _output_("Content-type: text/html\r\n\r\n");
  1257.     bcolor=getvar("wims_bgcolor");
  1258.     if(bcolor==NULL || *bcolor==0) bcolor=bgcolor;
  1259.     link=getvar("wims_link_color");
  1260.     if(link==NULL || *link==0) link="blue";
  1261.     vlink=getvar("wims_vlink_color");
  1262.     if(vlink==NULL || *vlink==0) vlink="blue";
  1263.     refcolor=getvar("wims_ref_bgcolor");
  1264.     if(refcolor==NULL || *refcolor==0) {
  1265.       setvar("wims_ref_bgcolor","white");
  1266.       refcolor="white";
  1267.     }
  1268.     bg=getvar("wims_bgimg"); bgbuf[0]=0;
  1269.     if(bg!=NULL && *bg!=0 && strchr(bg,'\"')==NULL) {
  1270.      if(strchr(bg,'/')==NULL)
  1271.        snprintf(bgbuf,sizeof(bgbuf),"background-image: url(\"gifs/bg/%s\");",bg);
  1272.      else
  1273.        snprintf(bgbuf,sizeof(bgbuf),"background-image: url(\"%s\");",bg);
  1274.     }
  1275.     setvar("wims_bodyimg",bgbuf);
  1276.     tx=getvar("wims_textcolor");
  1277.     if(tx!=NULL && *tx!=0 && strchr(tx,'\"')==NULL) {
  1278.      snprintf(txbuf,sizeof(txbuf),"color:%s;",tx);
  1279.     }
  1280.     else txbuf[0]=0;
  1281.    /* special fonts defined in special_font -should also take the value specialfont in wims.conf */
  1282.     if(spec_font==1){
  1283.         spf=getvar("wims_specialfont");
  1284.         if(spf!=NULL && *spf!=0) { snprintf(spfbuf,sizeof(spfbuf),"%s",spf);}
  1285.           else { snprintf(spfbuf,sizeof(spfbuf),"%s",special_font); }
  1286.         setvar("wims_special_font",spfbuf);
  1287.     }
  1288.     dirn=getvar("wims_main_dirn");     /* "rtl" for arabic writing ; on pourrait laisser vide pour les autres ? */
  1289.     if(dirn!=NULL && *dirn!=0 && strchr(dirn,'\"')==NULL) {
  1290.      snprintf(dirnbuf,sizeof(dirnbuf),"dir=\"%s\"",dirn);
  1291.     }
  1292.     else dirnbuf[0]=0;
  1293.     snprintf(buf,sizeof(buf), "class=\"main_body\" %s",dirnbuf);
  1294.     setvar("wims_htmlbody",buf);
  1295.     phtml_put(mname,0);
  1296. }
  1297.  
  1298. void _write_var(char *name, FILE *varf,int user,int skip)
  1299. {
  1300.     char *s, buf[MAX_NAMELEN+9], nbf[MAX_NAMELEN+9];
  1301.  
  1302.     if((user&2)!=0) {
  1303.      snprintf(nbf,sizeof(nbf),"%s%s",wims_prefix,name); name=nbf;
  1304.     }
  1305.     if((user&1)!=0) {
  1306.      snprintf(buf,sizeof(buf),"%s%s",var_prefix,name); s=_getvar(name);
  1307.     }
  1308.     else {
  1309.      mystrncpy(buf,name,sizeof(buf)); s=getenv(name);
  1310.     }
  1311.     if(s==NULL) s="";
  1312.     if(skip && *s==0) return;
  1313.     fprintf(varf,"%s=",buf);
  1314.     if(strchr(s,'\n')==NULL) fputs(s,varf);
  1315.     else while(*s) {
  1316.        if(*s=='\n') fputc('\\',varf);
  1317.        fputc(*s,varf); s++;
  1318.     }
  1319.     fputc('\n',varf);
  1320. }
  1321.  
  1322. void _write_vars(FILE *varf)
  1323. {
  1324.     int i,j,k;
  1325.     char nbuf[MAX_NAMELEN+1];
  1326.     for(i=0;i<defined_var_total;i++) {
  1327.      if(var_def[i].beg>=0) {
  1328.          if(var_def[i].end<=var_def[i].beg) {
  1329.           snprintf(nbuf,sizeof(nbuf),"%s%d",var_def[i].name,var_def[i].beg);
  1330.           _write_var(nbuf,varf,1,1);
  1331.          }
  1332.          else {
  1333.           char *pbuf[MAX_VAR_NUM];
  1334.           j=varsuite(var_def[i].name,var_def[i].beg,var_def[i].end,pbuf,MAX_VAR_NUM);
  1335.           for(k=0;k<j;k++) _write_var(pbuf[k],varf,1,1);
  1336.          }
  1337.      }
  1338.      else _write_var(var_def[i].name,varf,1,1);
  1339.     }
  1340. }
  1341.  
  1342. /* save exercise parm for registered users */
  1343. void save_parmreg(void)
  1344. {
  1345.     char *p, *u, *sh, *ex, nbuf[MAX_FNAME+1], dbuf[MAX_FNAME+1], tbuf[MAX_FNAME+1];
  1346.     int s;
  1347.     FILE *varf;
  1348.  
  1349.     if(cmd_type!=cmd_reply && cmd_type!=cmd_new && cmd_type!=cmd_renew)
  1350.       return;
  1351.     u=getvar("wims_user");
  1352.     if(class_dir[0]==0 || *u==0) return;
  1353.     sh=getvar("wims_sheet"); ex=getvar("wims_exo");
  1354.     if(sh==NULL || ex==NULL || *sh==0 || *ex==0) return;
  1355.     mkfname(nbuf,"%s/.parmreg/%s.%s.%s",
  1356.           class_dir,u,sh,ex);
  1357.     if(cmd_type==cmd_reply) {
  1358.      unlink(nbuf); return;
  1359.     }
  1360.     p=getvar("wims_scorereg");
  1361.     if(p!=NULL && strcmp(p,"suspend")==0) return;
  1362. /* these two lines must be before random factor
  1363.  * in order to set variable wims_scoring */
  1364.     s=atoi(sh); if(getscorestatus(getvar("wims_class"),s)==0) return;
  1365.     if(strcmp(u,"supervisor")==0) return;
  1366. /* 0.2 is random factor to trigger exercise parm register */
  1367. /*    return;*/
  1368.     if((double) random()>(double) RAND_MAX*0.2) return;
  1369.     mkfname(dbuf,"%s/.parmreg",class_dir);
  1370.     mkdirs(dbuf); varf=fopen(nbuf,"w"); if(varf==NULL) return;
  1371. /* provisoire to be changed
  1372.     mkfname(nbuf,"%s/seed2/%s",class_dir,u);
  1373.     getdef(nbuf,mkfname(dbuf,"%s_%s",sh,ex),tbuf);
  1374. fin  provisoire */
  1375.     _write_var("module_init_parm",varf,1,0);
  1376.     _write_var("wims_sheet",varf,1,1);
  1377.     _write_var("wims_exo",varf,1,1);
  1378.     _write_var("wims_scoring",varf,1,1);
  1379.     _write_var("wims_seed",varf,1,1);
  1380.    /* _write_var("wims_seed_repeat",varf,1,1);*/
  1381.     _write_vars(varf);
  1382.     fclose(varf);
  1383. }
  1384.  
  1385. void exolog(char *fname)
  1386. {
  1387.     FILE *varf, *varg;
  1388.     int c;
  1389.     char logftmp[MAX_FNAME+1], *sess;
  1390.  
  1391. /* add by FG to put w_module_score at the top of the file */
  1392.     if ((getenv("w_module_score"))!=NULL )
  1393.     {
  1394.      sess=getvar("wims_session");
  1395.      mkfname(logftmp,"%s/%s/.tmp%lu",s2_dir,sess,nowtime);
  1396.      rename(fname,logftmp);
  1397.      varf=fopen(fname,"a");
  1398.      if(varf!=NULL) {
  1399.          _write_var("module_score",varf,1,1);
  1400.          varg=fopen(logftmp, "r");
  1401.          if(varg!=NULL) {
  1402.           while ( (c=getc (varg)) != EOF)
  1403.           putc (c,varf);
  1404.           fclose(varg);
  1405.          }
  1406.          fclose(varf);
  1407.          remove(logftmp);
  1408.      }
  1409.     }
  1410. /* end of add */
  1411.     varf=fopen(fname,"a");
  1412.     if(varf!=NULL) {
  1413. /* The first 4 lines should not be modified */
  1414.      fprintf(varf,"\n:\n");
  1415.      _write_var("QUERY_STRING",varf,0,0);
  1416.      _write_var("module",varf,1,0);
  1417.      _write_var("cmd",varf,1,0);
  1418.      _write_var("module_init_parm",varf,1,0);
  1419.      _write_var("wims_scoring",varf,1,1);
  1420.      _write_var("module_score",varf,1,1);
  1421.      fprintf(varf,"w_wims_checktime1=%lu\n\
  1422. w_wims_checktime2=%s\n",nowtime,nowstr);
  1423.      _write_vars(varf);
  1424.      fclose(varf);
  1425.     }
  1426. }
  1427.  
  1428. /* save variables to session var file */
  1429. void save_session_vars(void)
  1430. {
  1431.     int i;
  1432.     char *mp, *sh, *ex, *ll;
  1433.     FILE *varf;
  1434. /* light pages don't need saved variables. ? */
  1435. /*    if(varchr(module_prefix,"light")!=NULL) return;*/
  1436.     if(robot_access) return;
  1437. /* no variable saving if cmd=help? */
  1438.     if(cmd_type==cmd_help) return;
  1439.     lastdatafile[0]=lastftest[0]=0;
  1440.     lessrafale();
  1441.     mp=getvar("wims_nextmodule"); if(mp!=NULL && *mp!=0) {
  1442.      mp=getvar("module"); if(strcmp(mp,home_module)==0)
  1443.        force_setvar("module",getvar("wims_nextmodule"));
  1444.     }
  1445.     varf=fopen_session_var_file("w");
  1446.     for(i=0;i<HEADER_VAR_NO;i++)
  1447.       _write_var(header_var_name[i],varf,0,0);
  1448.     for(i=0;i<RO_NAME_NO;i++)
  1449.       _write_var(ro_name[i],varf,1,0);
  1450.     for(i=0;i<INTERNAL_NAME_NO;i++) {
  1451.      if(internal_name[i].stat==0)
  1452.        _write_var(internal_name[i].name,varf,3,0);
  1453.     }
  1454.     _write_var("password",varf,0,1);
  1455.     save_parmreg();
  1456.     if(new_session) fprintf(varf,"wims_new_session=yes\n");
  1457.     fprintf(varf,"\n");
  1458.     _write_var("module_init_parm",varf,1,0);
  1459.     _write_var("wims_sheet",varf,1,1);
  1460.     _write_var("wims_exo",varf,1,1);
  1461.     _write_var("wims_scoring",varf,1,1);
  1462.     _write_vars(varf);
  1463.     fclose(varf);
  1464.     if(cmd_type==cmd_new || cmd_type==cmd_renew || cmd_type==cmd_reply || cmd_type==cmd_next) {
  1465.      ll=getvar("wims_class_examlog");
  1466.      if(ll!=NULL && *ll!=0) i=atoi(ll); else i=examlog_limit;
  1467.      if(strstr(session_prefix,"_exam")!=NULL && examlogf[0]!=0 &&
  1468.         simuxam==0 && i>0) {
  1469.          mkdirs(examlogd); exolog(examlogf);
  1470.      }
  1471.      else {
  1472.          ll=getvar("wims_class_exolog");
  1473.          sh=getvar("wims_sheet"); ex=getvar("wims_exo");
  1474.          if(ll!=NULL && atoi(ll)>0) {
  1475.            char buf[MAX_FNAME+1];
  1476.            if(sh!=NULL && *sh!=0 && ex!=NULL && *ex!=0){
  1477.              mkfname(buf,"%s/exolog.%s.%s",session_prefix,sh,ex);
  1478.            }
  1479.            else { mkfname(buf,"%s/exolog",session_prefix);}
  1480.            if(cmd_type==cmd_new || cmd_type==cmd_renew) unlink(buf);
  1481.            exolog(buf);
  1482.          }
  1483.       }
  1484.     }
  1485.     if(var_def_buf) free(var_def_buf);
  1486. }
  1487.