README: acknowledge added.
[vde.git] / vdetelweb / web.c
blob944c88186d79418da3eb345ec6af1aef057626b8
1 /*
2 * VDETELWEB: VDE telnet and WEB interface
4 * web.c: http micro server for vde mgmt
5 *
6 * Copyright 2005 Renzo Davoli University of Bologna - Italy
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 * $Id$
25 #include <stdio.h>
26 #define __USE_GNU
27 #include <signal.h>
28 #include <stdarg.h>
29 #include <syslog.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/poll.h>
35 #include <sys/ioctl.h>
36 #include <linux/un.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <string.h>
40 #include <getopt.h>
41 #include "vdetelweb.h"
42 #include <lwipv6.h>
44 #define WEB_TCP_PORT 80
45 #define WEB_IDENTIFY 0x0
46 #define WEB_AUTHORIZED 0x1
47 #define WEB_UNAUTHORIZED 0x2
48 #define WEB_OP_GET 0x0
49 #define WEB_OP_POST 0x1
50 #define WEB_OP_POSTDATA 0x2
52 static char base64ab[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
53 static char *base64passwd;
54 typedef void (*voidfun)();
55 struct webstat {
56 unsigned char status;
57 unsigned char op;
58 unsigned int bodylen;
59 char linebuf[BUFSIZE];
60 char path[BUFSIZE];
61 int bufindex;
65 static void lowercase(char *s)
67 while (*s != 0) {
68 tolower(*s);
69 s++;
73 static convert2base64(char *from,char *to,int tosize)
75 int convbuf;
76 int n=strlen(from);
77 while (n>0 && tosize>3) {
78 convbuf=*from;
79 from++;n--;
80 convbuf<<=8;
81 if (n>0) convbuf|=*from;
82 from++;n--;
83 convbuf<<=8;
84 if (n>0) convbuf|=*from;
85 from++;n--;
86 *(to++)=base64ab[convbuf>>18];
87 *(to++)=base64ab[convbuf>>12 & 0x3f];
88 *(to++)=(n<-1)?'=':base64ab[convbuf>>6 & 0x3f];
89 *(to++)=(n<0)?'=':base64ab[convbuf & 0x3f];
90 tosize -= 4;
92 *to=0;
95 static void createbase64passwd()
97 char buf[BUFSIZE];
98 char buf64[BUFSIZE*4/3];
99 snprintf(buf,BUFSIZE,"admin:%s",passwd);
100 convert2base64(buf,buf64,BUFSIZE*4/3);
101 base64passwd=strdup(buf64);
104 static void lwip_printf(int fd, const char *format, ...)
106 char outbuf[BUFSIZE];
107 va_list arg;
108 va_start (arg, format);
109 vsnprintf(outbuf,BUFSIZE,format,arg);
110 lwip_write(fd,outbuf,strlen(outbuf));
113 static void web_close(int fn,int fd)
115 //printf("web_close %d %d\n",fn,fd);
116 free(status[fn]);
117 delpfd(fn);
118 lwip_close(fd);
121 static int vde_getanswer(voidfun f,void *arg,int vdefd)
123 char buf[BUFSIZE];
124 char linebuf[BUFSIZE+1];
125 int n=0,ib=0,il=0,indata=0,eoa=0;
126 do {
127 n=read(vdefd,buf,BUFSIZE);
128 for(ib=0;ib<n;ib++)
130 linebuf[il++]=buf[ib];
131 if (buf[ib] == '\n') {
132 linebuf[il-1]='\r';
133 linebuf[il]='\n';
134 linebuf[il+1]=0;
135 il++;
136 if (indata) {
137 if (linebuf[0]=='.' && linebuf[1]=='\r')
138 indata=0;
139 else
140 f(arg,linebuf,il,indata,0);
141 } else if (strncmp(linebuf,"0000",4)==0)
142 indata=1;
143 else {
144 if(linebuf[0]=='1' &&
145 linebuf[1] >= '0' && linebuf[1] <= '9' &&
146 linebuf[2] >= '0' && linebuf[2] <= '9' &&
147 linebuf[3] >= '0' && linebuf[3] <= '9') {
148 f(arg,linebuf+5,il-5,0,atoi(linebuf));
149 eoa=atoi(linebuf);
152 il=0;
155 } while (!eoa);
156 return(eoa);
159 struct vdesub {
160 char *name;
161 char *descr;
162 char *syntax;
163 struct vdesub *next;
166 struct vdemenu {
167 char *name;
168 char *descr;
169 struct vdesub *sub;
170 struct vdemenu *next;
173 static struct vdemenu *menuhead;
175 static struct vdemenu *vde_findmenu(struct vdemenu *head,char *name)
177 if (head == NULL)
178 return NULL;
179 else
180 if (strcmp(head->name,name)==0)
181 return head;
182 else
183 return vde_findmenu(head->next,name);
186 static void vde_addsub(struct vdesub **headp,char *name,char *syntax,char *help)
188 if (*headp == NULL) {
189 *headp=malloc(sizeof(struct vdesub));
190 if (*headp != NULL) {
191 (*headp)->name=name;
192 (*headp)->descr=help;
193 (*headp)->syntax=syntax;
194 (*headp)->next=NULL;
196 } else
197 vde_addsub(&((*headp)->next),name,syntax,help);
200 static void vde_addcmd(struct vdemenu *head,char *menu,char *name,char *syntax,char *help)
202 if (head != NULL) {
203 if (strcmp(head->name,menu) == 0)
204 vde_addsub(&(head->sub),name,syntax,help);
205 else
206 vde_addcmd(head->next,menu,name,syntax,help);
210 static void vde_addmenu(struct vdemenu **headp,char *name,char *help)
212 if (*headp == NULL) {
213 *headp=malloc(sizeof(struct vdemenu));
214 if (*headp != NULL) {
215 (*headp)->name=name;
216 (*headp)->descr=help;
217 (*headp)->sub=NULL;
218 (*headp)->next=NULL;
220 } else
221 vde_addmenu(&((*headp)->next),name,help);
224 static void vde_helpline(struct vdemenu **headp,char *buf,int len,int indata,int rv)
226 static int nl=0;
227 static int syntaxpos,helppos;
228 nl++;
229 if (nl==2) {
230 int i;
231 for (i=0;i<len && buf[i]=='-';i++) ;
232 for (;i<len && buf[i]==' ';i++) ;
233 syntaxpos=i;
234 for (;i<len && buf[i]=='-';i++) ;
235 for (;i<len && buf[i]==' ';i++) ;
236 helppos=i;
238 else if (nl > 2 && indata) {
239 int i;
240 char *name;
241 char *syntax;
242 char *help;
243 int namelen;
244 for (namelen=0;namelen<syntaxpos && buf[namelen]!=' ';namelen++) ;
245 if (strncmp(buf+syntaxpos,"======",5) ==0) {
246 /* MENU */
247 name=strndup(buf,namelen);
248 help=strndup(buf+helppos,len-helppos-2);
249 vde_addmenu(headp,name,help);
250 } else {
251 int slash;
252 for (slash=0;slash<namelen && buf[slash]!='/';slash++) ;
253 if (slash<namelen) {
254 int synlen;
255 buf[slash]=0;slash++;
256 namelen-=slash;
257 for (synlen=helppos-syntaxpos; synlen>0 && buf[syntaxpos+synlen-1]==' ';synlen--) ;
258 name=strndup(buf+slash,namelen);
259 if (synlen>0)
260 syntax=strndup(buf+syntaxpos,synlen);
261 else
262 syntax="";
263 help=strndup(buf+helppos,len-helppos-2);
264 vde_addcmd(*headp,buf,name,syntax,help);
270 static struct vdemenu *vde_gethelp(int vdefd)
272 struct vdemenu *head=NULL;
273 write(vdefd,"help\n",5);
274 vde_getanswer(vde_helpline,&head,vdefd);
275 return head;
278 static void lwip_showline(int *fdp,char *buf,int len,int indata,int rv)
280 if (indata)
281 lwip_write(*fdp,buf,len);
284 static int lwip_showout(int fd, int vdefd)
286 return vde_getanswer(lwip_showline,&fd,vdefd);
289 static int hex2num(int c)
291 if (c>96) c-=32;
292 c -='0';
293 if (c>9)
294 c-=7;
295 return c;
298 static char *uriconv(char *in)
300 char *s=in;
301 char *t=in;
302 while ((*t=*s) != 0) {
303 if (*s=='+')
304 *t=' ';
305 if (*s=='%') {
306 *t=(hex2num(*(s+1))<<4)+hex2num(*(s+2));
307 s+=2;
309 s++;t++;
311 return in;
314 static void postdata_parse(int fd,int vdefd,char *menu,char *postdata)
316 char cmdbuf[BUFSIZE];
317 int cmdlen,arglen,rv;
318 char *postcmd,*cmd,*endcmd,*arg=NULL;
319 /*printf("PD **%s**\n",postdata);*/
320 if ((postcmd=strstr(postdata,"X="))!=NULL) {
321 /* enter in a text field (catched through the hidden button) */
322 cmd=NULL;
323 while(postdata)
325 char *token=strsep(&postdata,"&");
326 int l=strlen(token);
327 char *targ=index(token,'=');
328 if(strncmp("X=",token,2) != 0) {
329 if (targ+1 < token+l)
330 if(cmd==NULL) {
331 char *point;
332 if ((point=strstr(token,".arg")) != NULL)
333 *point=0;
334 cmd=token;
335 arg=targ+1;
336 } else
337 cmd="";
340 if(cmd!=NULL && *cmd != 0) {
341 strncpy(cmdbuf,menu,BUFSIZE);
342 strncat(cmdbuf,"/",BUFSIZE);
343 strncat(cmdbuf,cmd,BUFSIZE);
344 strncat(cmdbuf," ",BUFSIZE);
345 strncat(cmdbuf,uriconv(arg),BUFSIZE);
346 write(vdefd,cmdbuf,strlen(cmdbuf));
347 lwip_printf(fd,"<P> </P><B>%s %s</B><PRE>",prompt,cmdbuf);
348 rv=lwip_showout(fd,vdefd);
349 lwip_printf(fd,"</PRE><B>Result: %s</B>\r\n",strerror(rv-1000));
352 else if ((postcmd=strstr(postdata,"COMMAND="))!=NULL) {
353 /* accept button */
354 postcmd+=8;
355 for(cmdlen=0;postcmd[cmdlen] != '&' && postcmd[cmdlen] != 0; cmdlen++)
357 strncpy(cmdbuf,menu,BUFSIZE);
358 strncat(cmdbuf,"/",BUFSIZE);
359 cmd=cmdbuf+strlen(cmdbuf);
360 strncat(cmdbuf,postcmd,(BUFSIZE<cmdlen)?BUFSIZE:cmdlen);
361 endcmd=cmdbuf+strlen(cmdbuf);
362 strncat(cmdbuf,".arg",BUFSIZE);
363 if ((arg=strstr(postdata,cmd))!=NULL) {
364 arg+=strlen(cmd)+1;
365 for(arglen=0;arg[arglen] != '&' && arg[arglen] != 0; arglen++)
367 arg[arglen]=0;
368 *endcmd=0;
369 if (*arg != 0) {
370 strncat(cmdbuf," ",BUFSIZE);
371 strncat(cmdbuf,uriconv(arg),BUFSIZE);
373 } else
374 *endcmd=0;
375 write(vdefd,cmdbuf,strlen(cmdbuf));
376 lwip_printf(fd,"<P> </P><B>%s %s</B><PRE>",prompt,cmdbuf);
377 rv=lwip_showout(fd,vdefd);
378 lwip_printf(fd,"</PRE><B>Result: %s</B>\r\n",strerror(rv-1000));
382 static char css[]=
383 "<style type=\"text/CSS\"\r\n"
384 "<!--\r\n"
385 ".core {\r\n"
386 "font-family: Helvetica;\r\n"
387 "color: #0000FF;\r\n"
388 "background-color: #FFFFFF;\r\n"
389 "text-align: justify;\r\n"
390 "margin-left: 5pt;\r\n"
391 "margin-top: 5pt;\r\n"
392 "margin-right: 5pt;\r\n"
393 "margin-bottom: 5pt;\r\n"
394 "}\r\n"
395 ".sidebar {\r\n"
396 "font-family: Helvetica;\r\n"
397 "font-size: 12px;\r\n"
398 "color: #ff0000;\r\n"
399 "}\r\n"
400 "-->\r\n"
401 "</style>\r\n";
403 static char okmsg[]=
404 "HTTP/1.1 200 OK\r\n"
405 "Content-Type: text/html\r\n"
406 "\r\n";
408 static char errmsg[]=
409 "HTTP/1.1 404 Not Found\r\n"
410 "Content-Type: text/html\r\n"
411 "\r\n"
412 "<HTML><HEAD>\r\n"
413 "<TITLE>404 Not Found</TITLE>\r\n"
414 "</HEAD><BODY>\r\n"
415 "<H1>Not Found</H1>\r\n"
416 "The requested URL was not found on this server.\r\n"
417 "<hr>VDE 2.0 WEB MGMT INTERFACE\r\n"
418 "</BODY></HTML>\r\n";
420 static void web_this_form(int fd,struct vdemenu *this)
422 struct vdesub *sub;
423 for (sub=this->sub;sub!=NULL;sub=sub->next) {
424 if (*(sub->syntax) == 0) {
425 lwip_printf(fd,
426 "<TR><TD width=50><INPUT type=submit size=100 name=\"%s\" value=\"%s\"></TD>\r\n"
427 "<TD width=100></TD>\r\n"
428 "<TD width=100></TD>\r\n"
429 "<TD width=300>%s</TD></TR>\r\n",
430 "COMMAND",sub->name,sub->descr);
431 } else {
432 lwip_printf(fd,
433 "<TR><TD width=50><INPUT type=submit size=100 name=\"%s\" value=\"%s\"></TD>\r\n"
434 "<TD width=100>%s</TD>\r\n"
435 "<TD width=100><INPUT type=text name=\"%s.arg\"></TD>\r\n"
436 "<TD width=300>%s</TD></TR>\r\n",
437 "COMMAND",sub->name,sub->syntax,sub->name,sub->descr);
442 static void web_menu_index(int fd)
444 struct vdemenu *this;
445 lwip_printf(fd,"<P><A HREF=\"index.html\">Home Page</A></P>\r\n");
446 for (this=menuhead;this!=NULL;this=this->next)
447 lwip_printf(fd,"<P><A HREF=\"%s.html\">%s</A></P>\r\n",this->name,this->name);
450 static void web_create_page(char *path,int fd,int vdefd,char *postdata)
452 struct vdemenu *this;
453 char *tail;
454 if ((tail=strstr(path,".html")) != NULL)
455 *tail=0;
456 if (*path==0 || ((this=vde_findmenu(menuhead,path)) != NULL)) {
457 lwip_write(fd,okmsg,sizeof(okmsg));
458 lwip_printf(fd,
459 "<HTML><HEAD>\r\n"
460 "<TITLE>%s %s</TITLE>\r\n",
461 prompt, (*path==0)?"Home Page":path);
462 lwip_write(fd,css,sizeof(css));
463 lwip_printf(fd,
464 "</HEAD><BODY class=core>\r\n"
465 "<H1>%s %s</H1>\r\n"
466 "<TABLE BORDER=0><TD width=80 bgcolor=#aacbff valign=top class=sidebar>",
467 prompt, (*path==0)?"Home Page":this->descr);
468 web_menu_index(fd);
469 if (*path==0) {/* HOME PAGE */
470 int rv;
471 write(vdefd,"showinfo\r\n",10);
472 lwip_printf(fd,
473 "</TD><TD><PRE>\r\n");
474 rv=lwip_showout(fd,vdefd);
475 lwip_printf(fd,"</PRE>\r\n");
476 if (rv != 1000)
477 lwip_printf(fd,"<B>%s</B>\r\n",strerror(rv-1000));
478 } else {
479 lwip_printf(fd,
480 "</TD><TD><FORM action=\"%s.html\" method=post table-layout=fixed>\r\n<TABLE><THEAD><TR>\r\n"
481 "<TD><INPUT type=submit name=X style=\"visibility:hidden\" ></TD>\r\n"
482 "<TD><B>Syntax</B></TD><TD><B>Args</B>\r\n"
483 "</TD><TD><B>Description</B></TD></TR></THEAD>\r\n",path);
484 web_this_form(fd,this);
485 lwip_printf(fd,"</TABLE></FORM>\r\n");
486 if (postdata != NULL) {
487 postdata_parse(fd,vdefd,path,postdata);
490 lwip_printf(fd,
491 "</TD></TABLE>\r\n"
492 "<hr>VDE 2.0 WEB MGMT INTERFACE\r\n"
493 "</BODY></HTML>\r\n");
494 } else
495 lwip_write(fd,errmsg,sizeof(errmsg));
498 static char authmsg[]=
499 "HTTP/1.1 401 Authorization Required\r\n"
500 "WWW-Authenticate: Basic realm=\"";
502 //"Content-Length: 187\r\n"
503 //"Connection: close\r\n"
504 static char authmsg2[]= "\"\r\n"
505 "Content-Type: text/html\r\n"
506 "\r\n"
507 "<HTML><HEAD>\r\n"
508 "<TITLE>401 Authorization Required</TITLE>\r\n"
509 "</HEAD><BODY>\r\n"
510 "<H1>Authorization Required</H1>\r\n"
511 "Login and Password required\r\n"
512 "<hr>\r\nVDE 2.0 WEB MGMT INTERFACE</H1>\r\n"
513 "</BODY></HTML>\r\n";
516 int web_core(int fn,int fd,int vdefd)
518 struct webstat *st=status[fn];
519 //printf("CORE %s\n",st->linebuf);
520 if (st->op==WEB_OP_POSTDATA) {
521 //printf("POSTDATA %s\n",st->linebuf);
522 web_create_page(&(st->path[1]),fd,vdefd,st->linebuf);
523 return 1;
524 } else if (strncmp(st->linebuf,"GET",3) == 0) {
525 //printf("GET %s\n",st->linebuf);
526 sscanf(st->linebuf+4,"%s",st->path);
527 st->op=WEB_OP_GET;
528 return 0;
529 } else if (strncmp(st->linebuf,"POST",3) == 0) {
530 //printf("POST %s\n",st->linebuf);
531 sscanf(st->linebuf+5,"%s",st->path);
532 st->op=WEB_OP_POST;
533 return 0;
534 } else if (strncmp(st->linebuf,"Content-Length: ",16) == 0) {
535 st->bodylen=atoi(st->linebuf+16);
536 //printf("BODYLEN %d\n",st->bodylen);
537 return 0;
538 } else if (strncmp(st->linebuf,"Authorization: Basic",20) == 0) {
539 int k=20;
540 while (st->linebuf[k] == ' ') k++;
541 if (strncmp(st->linebuf+k,base64passwd,strlen(base64passwd))==0)
542 st->status=WEB_AUTHORIZED;
543 return 0;
544 } else if (st->linebuf[0]=='\n' || st->linebuf[0]=='\r') {
545 switch (st->status) {
546 case WEB_IDENTIFY:
547 lwip_write(fd,authmsg,sizeof(authmsg));
548 lwip_write(fd,prompt,strlen(prompt));
549 lwip_write(fd,authmsg2,sizeof(authmsg2));
550 return 1;
551 break;
552 case WEB_AUTHORIZED:
553 lowercase(st->path);
554 if (strcmp(st->path,"/index.html") == 0)
555 st->path[1]=0;
556 if (st->op == WEB_OP_GET) {
557 web_create_page(&(st->path[1]),fd,vdefd,NULL);
558 return 1;
559 } else {
560 st->op=WEB_OP_POSTDATA;
561 return 0;
564 } else
565 return 0;
568 int webdata(int fn,int fd,int vdefd)
570 char buf[BUFSIZE];
571 int n,i;
572 struct webstat *st=status[fn];
573 n=lwip_read(fd,buf,BUFSIZE);
574 if (n==0) {
575 web_close(fn,fd);
577 else if (n<0)
578 printlog(LOG_ERR,"web read err: %s",strerror(errno));
579 else {
580 for (i=0;i<n && st->bufindex<BUFSIZE;i++) {
581 st->linebuf[(st->bufindex)++]=buf[i];
582 if (buf[i]=='\n' || (st->op==WEB_OP_POSTDATA && st->bufindex==st->bodylen)) {
583 st->linebuf[(st->bufindex)]=0;
584 if (web_core(fn,fd,vdefd)) {
585 web_close(fn,fd);
586 break;
587 } else
588 st->bufindex=0;
594 int webaccept(int fn,int fd,int vdefd)
596 struct sockaddr_in cli_addr;
597 int newsockfd;
598 unsigned int clilen;
599 struct webstat *st;
600 int newfn;
602 clilen = sizeof(cli_addr);
603 newsockfd = lwip_accept(fd, (struct sockaddr *) &cli_addr, &clilen);
605 if (newsockfd < 0) {
606 printlog(LOG_ERR,"web accept err: %s",strerror(errno));
609 newfn=addpfd(newsockfd,webdata);
610 status[newfn]=st=malloc(sizeof(struct webstat));
611 st->status=WEB_IDENTIFY;
612 st->op=0;
613 st->bufindex=0;
614 return 0;
617 void web_init(int vdefd)
619 int sockfd;
620 int one=1;
621 struct sockaddr_in serv_addr;
622 sockfd=lwip_socket(AF_INET, SOCK_STREAM, 0);
624 if (!sockfd) {
625 printlog(LOG_ERR,"web socket err: %s",strerror(errno));
627 if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
628 sizeof(one)) < 0){
629 printlog(LOG_ERR,"web setsockopt: %s",strerror(errno));
630 return;
632 if(fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0){
633 printlog(LOG_ERR,"Setting O_NONBLOCK web: %s",strerror(errno));
634 return;
637 bzero((char *) &serv_addr, sizeof(serv_addr));
638 serv_addr.sin_family = AF_INET;
639 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
640 serv_addr.sin_port = htons(WEB_TCP_PORT);
642 if (lwip_bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
643 printlog(LOG_ERR,"web bind err: %s",strerror(errno));
646 lwip_listen(sockfd, 5);
648 createbase64passwd();
649 menuhead=vde_gethelp(vdefd);
650 addpfd(sockfd,webaccept);