#pragma implementation #include #include #include #include #include #include #include #include #include "web.h" #include "web.m" #include #include #include #include #include #include #include #include "../../paths.h" #include "../../dialog/html.h" static DEBUG_KEY keyxml ("xml","Xml processing for the web module"); static DEBUG_KEY keyxmldata ("xmldata","Xml data sent to xsltproc"); static DEBUG_KEY keyhtml ("xmlhtml","HTML directly generated by the application"); static DEBUG_KEY keyxmlout ("xmlout","HTML output convert from XML"); class WEB_OUTPUT: public HTML_OUTPUT{ int cli; bool html_mode; SSTRINGS tb; SSTRING type; POPEN *pop; SSTRING html_host; public: WEB_OUTPUT (int _cli, const char *_name){ cli = _cli; html_mode = false; pop = NULL; html_host = _name; } void htmlmode(){ html_mode = true; } ~WEB_OUTPUT(){ close (cli); delete pop; } void settype(const char *type, const char *title); void write (const char *s); void write (const void *buf, int len); void done(); void copy (const char *fname); void sendintro(const char *content_type, int length, int expires, bool nocache); int gethandle(){ return cli; } }; static WEB_OUTPUT *curweb = NULL; void WEB_OUTPUT::settype (const char *s, const char *title) { type = s; if (strcmp(s,"html")==0){ htmlmode(); }else{ // We have to locate the XSL template // It is in the following places // /etc/linuxconf/web // /usr/lib/linuxconf/web static const char *tb[]={ "/etc/linuxconf/web", USR_LIB_LINUXCONF "/web", NULL }; for (int i=0; i<2; i++){ SSTRING path; path.setfromf ("%s/%s.xsl",tb[i],s); debug_printf (keyxml,"Looking for template %s %d: %s\n",path.get(),file_type(path.get()),title); if (file_exist(path.get())){ path.append (" -"); pop = new POPEN ("xsltproc",path.get()); if (!pop->isok()){ fprintf (stderr,"Can't execute xsltproc\n"); delete pop; pop = NULL; }else{ write ("\n"); xml_setval ("hostname",html_host); } } } } } void WEB_OUTPUT::write (const char *s) { write (s,strlen(s)); } void WEB_OUTPUT::write (const void *buf, int len) { if (html_mode){ debug_printf (keyhtml,"%*.*s",len,len,(char*)buf); ::write (cli,buf,len); }else{ debug_printf (keyxmldata,"%*.*s",len,len,(char*)buf); if (pop != NULL){ const char *start = (const char *)buf; const char *end = start + len; const char *pt = (const char *)memchr(start,'&',len); while (pt != NULL){ int nl = (int)(pt-(char*)start); if (nl > 0) pop->send (start,nl); pop->send ("&"); start = pt+1; pt = (const char*)memchr (start,'&',(end-start)); } if (start < end) pop->send (start,(int)(end-start)); } } } void WEB_OUTPUT::done() { debug_printf (keyxml,"done pop=%p\n",pop); if (pop != NULL){ write ("\n"); pop->closepipe(); while (pop->wait(10)>0){ char buf[1000]; while (pop->readout(buf,sizeof(buf))!=-1){ ::write(cli,buf,strlen(buf)); debug_printf (keyxmlout,"%s",buf); } while (pop->readerr(buf,sizeof(buf))!=-1){ fprintf (stderr,"ERRXSL: %s\n",buf); } } delete pop; pop = NULL; } close (cli); } /* Send the header of the html document. */ void WEB_OUTPUT::sendintro( const char *content_type, int length, // Length or -1 int expires, // How much seconds this document is expected to // be valid bool nocache) { time_t tim = time(NULL); htmlmode(); printf ("HTTP/1.0 200 Document follows\r\n"); printf ("MIME-Version: 1.0\r\n"); extern char *revision; printf ("Server: linuxconf/%s\r\n",revision); char buf[200]; strcpy (buf,asctime(gmtime(&tim))); strip_end (buf); printf ("Date: %s\r\n",buf); printf ("Content-Type: %s\r\n",content_type); if (length != -1){ printf ("Content-Length: %d\r\n",length); } if (nocache){ printf ("Cache-Control: no-cache\r\n"); }else{ tim += expires; char bufexp[200]; strcpy (bufexp,asctime(gmtime(&tim))); strip_end (bufexp); printf ("Expires: %s\r\n",bufexp); printf ("Last-Modified: %s\r\n",buf); } printf ("\r\n"); } /* Send a file (a bitmap generally to the currently served client */ void WEB_OUTPUT::copy (const char *fname) { char path[PATH_MAX]; if (html_locatefile(fname,"",path,PATH_MAX)==-1){ const char *ptpng = strstr(fname,".png"); if (strncmp(fname,"images/",7)==0 && ptpng!=NULL){ SSTRING name; name.setfrom (fname+7,ptpng-fname-7); SSTRING name_xpm; name_xpm.setfromf ("images/%s.xpm",name.get()); if (html_locatefile(name_xpm.get(),"",path,PATH_MAX)!=-1){ /* #Specification: HTML / buttons / converting XPM When a PNG file is requested, but only an XPM is available, it is converted on the fly */ static const char *convert_path = NULL; if (convert_path == NULL){ if (file_exist ("/usr/X11R6/bin/convert")){ convert_path = "/usr/X11R6/bin/convert"; }else if (file_exist ("/usr/bin/convert")){ convert_path = "/usr/bin/convert"; }else{ fprintf (stderr,"No convert utility found, can't convert bitmaps\n"); convert_path = USR_LIB_LINUXCONF "/lib/shownoconvert.sh"; } } sendintro("image/png",-1,24*60*60,false); if (fork()==0){ SSTRING tmp; tmp.setfromf ("%s %s png:-",convert_path,path); dup2 (gethandle(),1); system (tmp.get()); _exit (0); }else{ int retstatus; wait (&retstatus); } #if 0 POPEN pop (convert_path,tmp.get()); if (pop.isok()){ FILE *fout = fdopen (gethandle(),"w"); if (fout != NULL){ while (pop.wait(10)>0){ char line[800]; int len; while ((len=pop.readoutraw(line,sizeof(line)-1))>0){ fwrite (line,1,len,fout); } } fclose (fout); } } #endif }else{ /* #Specification: HTML / buttons / generating PNGs When a png image is missing for a button, it is generated using the GD library. The button file name is used as the text. */ sendintro("image/png",-1,24*60*60,false); if (name.getlen() < 100){ FILE *fout = fdopen (gethandle(),"w"); button_text2png (name.get(),fout); fclose (fout); } } }else{ printf ("500 file %s not found\r\n",fname); } }else{ FILE *fin = fopen (path,"r"); if (fin == NULL){ printf ("500 can't open file %s\r\n",path); }else{ struct stat st; int size = -1; if (stat(path,&st)!=-1) size = st.st_size; sendintro(strstr(fname,".png")!=NULL ? "image/png" : "text/html" ,size,24*60*60,false); char buf[3*4096]; int n; while ((n=fread(buf,1,sizeof(buf)-1,fin))> 0){ //if (debug) fwrite (buf,1,n,stderr); buf[n] = '\0'; char *pt = strstr(buf,"$(HOSTNAME)"); if (pt == NULL){ write (buf,n); }else{ if (pt > buf) curweb->write(buf,(int)(pt-buf)); // Plug the hostname write (html_host.get(),html_host.getlen()); pt += 11; int len = n - (int)(pt-buf); if (len > 0){ write (pt,len); } } } printf ("\r\n"); fclose (fin); } } } MODULE_DEFINE_VERSION(web); static const char *keymenu=NULL; void linuxconf_main(bool); int linuxconf_gethtmltimeout(); static HELP_FILE introweb ("main","introweb"); static const int httpmaxsize=40000; extern int target_level; /* #Specification: html mode / multi user A single process is handling all requests, one at a time. At most 200 simultaneous http request may occur. The exceeding ones are silently dropped! */ static const int MAX_WWW_CLIENTS=200; static SSTRING *tbs[MAX_WWW_CLIENTS]; static CMDSOCK *cmd = NULL; static MESSAGE_DEF listspc ("listspc",NULL); static void web_listspc () { curweb->sendintro ("text/html",-1,15,true); curweb->printf ( "\n" "\n" "%s\n" "\n" "\n",MSG_U(T_SPCLINKS,"Special links")); curweb->printf ("

%s

\n",MSG_R(T_SPCLINKS)); curweb->printf ("
\n"); curweb->printf ("
%s

\n" ,MSG_U(T_CHGPASS,"Change your password")); curweb->printf ("

%s

\n" ,MSG_U(T_EdITUSERS,"Administer user accounts")); module_sendmessage (listspc,0,NULL); curweb->printf ("\n"); curweb->printf ("\n"); curweb->done(); } /* Get a command (A "get" indeed) from a client (Web browser). parse this command into a path that will silently show the way so linuxconf will silently travel to the proper menu, draw it and quit. Return -1 if there was some error or nothing has happen for a long time (no more job). Nothing to do for Linuxconf. */ static int html_get ( HELP_FILE &intro, int timeout, // in minutes SSTRING &module_key,// Special key allowing a URL to point straight // into a module menuing int &remhandle, // Will contain the handle for the remote // GUI mode or -1 const char *name, int port) { delete curweb; curweb = NULL; html_setcurweb (NULL); remhandle = -1; if (cmd == NULL) cmd = new CMDSOCK (port,1); int ret = -1; while (1){ int ok = cmd->listen(timeout*60); if (ok <= 0){ if (errno != EINTR) break; }else{ char buf[5000]; int nb; int cli; bool is_timeout; if ((nb=cmd->readnext (buf,sizeof(buf)-1,cli,is_timeout))>=0){ if (nb == 0){ cmd->closecli (cli); }else if (cli >= MAX_WWW_CLIENTS){ cmd->closecli (cli); }else if (tbs[cli] == NULL && html_access_check(cli)!=0){ WEB_OUTPUT web (cli,name); web.htmlmode(); web.printf ("500 access denied: Check config/networking/misc/linuxconf network access\r\n"); web.printf ("

\r\n"); web.printf ("By default, linuxconf network access is disabled
\r\n"); web.printf ("Access is generally granted for few locations only
\r\n"); web.done(); cmd->forgetcli (cli); }else{ buf[nb] = '\0'; if (tbs[cli] == NULL){ tbs[cli] = new SSTRING; } tbs[cli]->append (buf); if (tbs[cli]->getlen()>httpmaxsize){ /* #Specification: html mode / input overflow If linuxconf receive an http request exceding 20000 bytes, it is silently flushed. */ html_access_log(cli,MSG_U(E_IVLDHTMLREQ,"Invalid html request, too long")); cmd->closecli(cli); delete tbs[cli]; tbs[cli] = NULL; }else{ char file_request[PATH_MAX]; char username[50]; char password[50]; html_dbglog ("so far",tbs[cli]->get()); bool remadmin = false; int ok = html_parse (tbs[cli]->get(),file_request ,username,password,intro,module_key ,remadmin); perm_setaccess(username,password); if (ok == -1){ html_access_log(cli,MSG_R(E_IVLDHTMLREQ)); cmd->closecli (cli); delete tbs[cli]; tbs[cli] = NULL; }else if (ok > 0){ debug_printf (keyxml,"new curweb, cli %d\n",cli); curweb = new WEB_OUTPUT(cli,name); //curweb->htmlmode(); html_setcurweb (curweb); cmd->forgetcli(cli); delete tbs[cli]; tbs[cli] = NULL; if (file_request[0] != '\0'){ SSTRING mbuf; mbuf.setfromf(MSG_U(L_FREQUEST ,"File request: %s") ,file_request); html_access_log(cli,mbuf.get()); curweb->copy (file_request); html_setdone(); }else if (remadmin){ remhandle = cli; ret = 0; break; }else{ SSTRING mbuf,pathreq; html_setpath_level (pathreq,target_level); mbuf.setfromf (MSG_U(L_REQUEST ,"Request: %s") ,pathreq.get()); html_access_log(cli,mbuf.get()); ret = 0; break; } } } } } } } return ret; } static void web_loop(bool https, bool demo, int argc, char *argv[]) { /* #Specification: linuxconf / command line / --http The --http option of linuxconf tells it to behave like an httpd server. It will all of a sudden start talking ... html. This option is normally used when starting linuxconf from /etc/inetd.conf Suboptions are available (--http --options ...) # --port port_number: Use this portnumber to format URLs. Default to LINUXCONF_HTTP_PORT --name name: Use this name to format URLs. Default to the fully qualified hostname. # */ /* #Specification: Linuxconf / startting from inetd Here is the proper configuration line which must be added to /etc/inetd.conf linuxconf stream tcp wait root /bin/linuxconf --http */ openlog ("linuxconf",LOG_PID,LOG_DAEMON); /* #Specification: linuxconf / command line / --demo The --demo option of linuxconf is like the --http except linuxconf will do a chroot("/demo_linuxconf") and will not do anything except probing the system. The mode "simul" on all the time */ if (getuid()!=0){ xconf_error(MSG_U(E_HTTP ,"--http and --demo may only be used by root")); exit (-1); } if (demo){ /* #Specification: demo mode / PAM PAM authentication is disabled in demo mode to avoid setting up too many things for PAM in the demo chroot environment, */ users_sethook (NULL,NULL,NULL); passwd_sethook (NULL,NULL,NULL); } dialog_setmode (DIALOG_HTML); int port = -1; THISHOST host; const char *name = host.getname1(); for (int i=0; i ("web",PACKAGE_REV); tb.add (new SSTRING(MSG_U(T_USAGE ,"linuxconf --modulemain web usage\n" "\n" " web --http port\n" " web --https port\n" ))); if (context == MENU_CTRL_FILE){ keymenu = MSG_U(M_web,"Linuxconf web interface"); dia.new_menuitem ("web","",keymenu); } if (context == MENU_CTRL_FILE){ if (key == keymenu){ CONTEXT_LOCK l("web"); if (l.isok()){ // ### Place the call to the main menu of this module here } } } return 0; int ret = LNCF_NOT_APPLICABLE; #if 0 // ### Check the variable key to provide your own html hook ret = 0; #endif return ret; int ret = -1; if (argc >= 2){ CONTEXT_LOCK l("web"); if (l.isok()){ if (strcmp(argv[1],"--http")==0){ web_loop(false,false,argc-2,argv+2); ret = 0; }else if (strcmp(argv[1],"--demo")==0){ web_loop(false,true,argc-2,argv+2); ret = 0; }else if (strcmp(argv[1],"--https")==0){ syslog (LOG_ERR,"HTTPS not supported yet!"); fprintf (stderr,"HTTPS not supported yet!\n"); }else{ printusage(); } } }else{ printusage(); } return ret;