home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Divers / samba-1.9.18p7.tar.gz / samba-1.9.18p7.tar / samba-1.9.18p7 / source / cgi.c < prev    next >
C/C++ Source or Header  |  1998-05-12  |  14KB  |  622 lines

  1. /* 
  2.    some simple CGI helper routines
  3.    Copyright (C) Andrew Tridgell 1997-1998
  4.    
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 2 of the License, or
  8.    (at your option) any later version.
  9.    
  10.    This program is distributed in the hope that it will be useful,
  11.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.    GNU General Public License for more details.
  14.    
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. */
  19.  
  20.  
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <sys/stat.h>
  24. #include <string.h>
  25. #include <fcntl.h>
  26. #include <unistd.h>
  27. #include <pwd.h>
  28.  
  29. #define MAX_VARIABLES 10000
  30.  
  31. #ifdef DEBUG_COMMENTS
  32. extern void print_title(char *fmt, ...);
  33. #endif
  34.  
  35. struct var {
  36.     char *name;
  37.     char *value;
  38. };
  39.  
  40. static struct var variables[MAX_VARIABLES];
  41. static int num_variables;
  42. static int content_length;
  43. static int request_post;
  44. static int request_get;
  45. static char *query_string;
  46.  
  47. static void unescape(char *buf)
  48. {
  49.     char *p=buf;
  50.  
  51.     while ((p=strchr(p,'+')))
  52.         *p = ' ';
  53.  
  54.     p = buf;
  55.  
  56.     while (p && *p && (p=strchr(p,'%'))) {
  57.         int c1 = p[1];
  58.         int c2 = p[2];
  59.  
  60.         if (c1 >= '0' && c1 <= '9')
  61.             c1 = c1 - '0';
  62.         else if (c1 >= 'A' && c1 <= 'F')
  63.             c1 = 10 + c1 - 'A';
  64.         else if (c1 >= 'a' && c1 <= 'f')
  65.             c1 = 10 + c1 - 'a';
  66.         else {p++; continue;}
  67.  
  68.         if (c2 >= '0' && c2 <= '9')
  69.             c2 = c2 - '0';
  70.         else if (c2 >= 'A' && c2 <= 'F')
  71.             c2 = 10 + c2 - 'A';
  72.         else if (c2 >= 'a' && c2 <= 'f')
  73.             c2 = 10 + c2 - 'a';
  74.         else {p++; continue;}
  75.             
  76.         *p = (c1<<4) | c2;
  77.  
  78.         memcpy(p+1, p+3, strlen(p+3)+1);
  79.         p++;
  80.     }
  81. }
  82.  
  83.  
  84. static char *grab_line(FILE *f, int *cl)
  85. {
  86.     char *ret;
  87.     int i = 0;
  88.     int len = 1024;
  89.  
  90.     ret = (char *)malloc(len);
  91.     if (!ret) return NULL;
  92.     
  93.  
  94.     while ((*cl)) {
  95.         int c = fgetc(f);
  96.         (*cl)--;
  97.  
  98.         if (c == EOF) {
  99.             (*cl) = 0;
  100.             break;
  101.         }
  102.         
  103.         if (c == '\r') continue;
  104.  
  105.         if (strchr("\n&", c)) break;
  106.  
  107.         ret[i++] = c;
  108.  
  109.         if (i == len-1) {
  110.             char *ret2;
  111.             ret2 = (char *)realloc(ret, len*2);
  112.             if (!ret2) return ret;
  113.             len *= 2;
  114.             ret = ret2;
  115.         }
  116.     }
  117.     
  118.  
  119.     ret[i] = 0;
  120.     return ret;
  121. }
  122.  
  123. /***************************************************************************
  124.   load all the variables passed to the CGI program. May have multiple variables
  125.   with the same name and the same or different values. Takes a file parameter
  126.   for simulating CGI invocation eg loading saved preferences.
  127.   ***************************************************************************/
  128. void cgi_load_variables(FILE *f1)
  129. {
  130.     FILE *f = f1;
  131.     static char *line;
  132.     char *p, *s, *tok;
  133.     int len;
  134.  
  135. #ifdef DEBUG_COMMENTS
  136.     char dummy[100]="";
  137.     print_title(dummy);
  138.     printf("<!== Start dump in cgi_load_variables() %s ==>\n",__FILE__);
  139. #endif
  140.  
  141.     if (!f1) {
  142.         f = stdin;
  143.         if (!content_length) {
  144.             p = getenv("CONTENT_LENGTH");
  145.             len = p?atoi(p):0;
  146.         } else {
  147.             len = content_length;
  148.         }
  149.     } else {
  150.         fseek(f, 0, SEEK_END);
  151.         len = ftell(f);
  152.         fseek(f, 0, SEEK_SET);
  153.     }
  154.  
  155.  
  156.     if (len > 0 && 
  157.         (f1 || request_post ||
  158.          ((s=getenv("REQUEST_METHOD")) && 
  159.           strcasecmp(s,"POST")==0))) {
  160.         while (len && (line=grab_line(f, &len))) {
  161.             p = strchr(line,'=');
  162.             if (!p) continue;
  163.             
  164.             *p = 0;
  165.             
  166.             variables[num_variables].name = strdup(line);
  167.             variables[num_variables].value = strdup(p+1);
  168.  
  169.             free(line);
  170.             
  171.             if (!variables[num_variables].name || 
  172.                 !variables[num_variables].value)
  173.                 continue;
  174.  
  175.             unescape(variables[num_variables].value);
  176.             unescape(variables[num_variables].name);
  177.  
  178. #ifdef DEBUG_COMMENTS
  179.             printf("<!== POST var %s has value \"%s\"  ==>\n",
  180.                    variables[num_variables].name,
  181.                    variables[num_variables].value);
  182. #endif
  183.             
  184.             num_variables++;
  185.             if (num_variables == MAX_VARIABLES) break;
  186.         }
  187.     }
  188.  
  189.     if (f1) {
  190. #ifdef DEBUG_COMMENTS
  191.             printf("<!== End dump in cgi_load_variables() ==>\n"); 
  192. #endif
  193.         return;
  194.     }
  195.  
  196.     fclose(stdin);
  197.  
  198.     if ((s=query_string) || (s=getenv("QUERY_STRING"))) {
  199.         for (tok=strtok(s,"&;");tok;tok=strtok(NULL,"&;")) {
  200.             p = strchr(tok,'=');
  201.             if (!p) continue;
  202.             
  203.             *p = 0;
  204.             
  205.             variables[num_variables].name = strdup(tok);
  206.             variables[num_variables].value = strdup(p+1);
  207.  
  208.             if (!variables[num_variables].name || 
  209.                 !variables[num_variables].value)
  210.                 continue;
  211.  
  212.             unescape(variables[num_variables].value);
  213.             unescape(variables[num_variables].name);
  214.  
  215. #ifdef DEBUG_COMMENTS
  216.                         printf("<!== Commandline var %s has value \"%s\"  ==>\n",
  217.                                variables[num_variables].name,
  218.                                variables[num_variables].value);
  219. #endif                        
  220.             num_variables++;
  221.             if (num_variables == MAX_VARIABLES) break;
  222.         }
  223.  
  224.     }
  225. #ifdef DEBUG_COMMENTS
  226.         printf("<!== End dump in cgi_load_variables() ==>\n");   
  227. #endif
  228. }
  229.  
  230.  
  231. /***************************************************************************
  232.   find a variable passed via CGI
  233.   Doesn't quite do what you think in the case of POST text variables, because
  234.   if they exist they might have a value of "" or even " ", depending on the 
  235.   browser. Also doesn't allow for variables[] containing multiple variables
  236.   with the same name and the same or different values.
  237.   ***************************************************************************/
  238. char *cgi_variable(char *name)
  239. {
  240.     int i;
  241.  
  242.     for (i=0;i<num_variables;i++)
  243.         if (strcmp(variables[i].name, name) == 0)
  244.             return variables[i].value;
  245.     return NULL;
  246. }
  247.  
  248. /***************************************************************************
  249. return a particular cgi variable
  250.   ***************************************************************************/
  251. char *cgi_vnum(int i, char **name)
  252. {
  253.     if (i < 0 || i >= num_variables) return NULL;
  254.     *name = variables[i].name;
  255.     return variables[i].value;
  256. }
  257.  
  258. /***************************************************************************
  259.   return the value of a CGI boolean variable.
  260.   ***************************************************************************/
  261. int cgi_boolean(char *name, int def)
  262. {
  263.     char *p = cgi_variable(name);
  264.  
  265.     if (!p) return def;
  266.  
  267.     return strcmp(p, "1") == 0;
  268. }
  269.  
  270. /***************************************************************************
  271. like strdup() but quotes < > and &
  272.   ***************************************************************************/
  273. char *quotedup(char *s)
  274. {
  275.     int i, n=0;
  276.     int len;
  277.     char *ret;
  278.     char *d;
  279.  
  280.     if (!s) return strdup("");
  281.  
  282.     len = strlen(s);
  283.  
  284.     for (i=0;i<len;i++)
  285.         if (s[i] == '<' || s[i] == '>' || s[i] == '&')
  286.             n++;
  287.  
  288.     ret = malloc(len + n*6 + 1);
  289.  
  290.     if (!ret) return NULL;
  291.  
  292.     d = ret;
  293.  
  294.     for (i=0;i<len;i++) {
  295.         switch (s[i]) {
  296.         case '<':
  297.             safe_strcpy(d, "<",len + n*6 - (d - ret));
  298.             d += 4;
  299.             break;
  300.  
  301.         case '>':
  302.             safe_strcpy(d, ">",len + n*6 - (d - ret));
  303.             d += 4;
  304.             break;
  305.  
  306.         case '&':
  307.             safe_strcpy(d, "&",len + n*6 - (d - ret));
  308.             d += 5;
  309.             break;
  310.  
  311.         default:
  312.             *d++ = s[i];
  313.         }
  314.     }
  315.  
  316.     *d = 0;
  317.  
  318.     return ret;
  319. }
  320.  
  321.  
  322. /***************************************************************************
  323. like strdup() but quotes a wide range of characters
  324.   ***************************************************************************/
  325. char *urlquote(char *s)
  326. {
  327.     int i, n=0;
  328.     int len;
  329.     char *ret;
  330.     char *d;
  331.     char *qlist = "\"\n\r'&<> \t+;";
  332.  
  333.     if (!s) return strdup("");
  334.  
  335.     len = strlen(s);
  336.  
  337.     for (i=0;i<len;i++)
  338.         if (strchr(qlist, s[i])) n++;
  339.  
  340.     ret = malloc(len + n*2 + 1);
  341.  
  342.     if (!ret) return NULL;
  343.  
  344.     d = ret;
  345.  
  346.     for (i=0;i<len;i++) {
  347.         if (strchr(qlist,s[i])) {
  348.             slprintf(d, len + n*2 - (d - ret), "%%%02X", (int)s[i]);
  349.             d += 3;
  350.         } else {
  351.             *d++ = s[i];
  352.         }
  353.     }
  354.  
  355.     *d = 0;
  356.  
  357.     return ret;
  358. }
  359.  
  360.  
  361. /***************************************************************************
  362. like strdup() but quotes " characters
  363.   ***************************************************************************/
  364. char *quotequotes(char *s)
  365. {
  366.     int i, n=0;
  367.     int len;
  368.     char *ret;
  369.     char *d;
  370.  
  371.     if (!s) return strdup("");
  372.  
  373.     len = strlen(s);
  374.  
  375.     for (i=0;i<len;i++)
  376.         if (s[i] == '"')
  377.             n++;
  378.  
  379.     ret = malloc(len + n*6 + 1);
  380.  
  381.     if (!ret) return NULL;
  382.  
  383.     d = ret;
  384.  
  385.     for (i=0;i<len;i++) {
  386.         switch (s[i]) {
  387.         case '"':
  388.             safe_strcpy(d, """, len + n*6 - (d - ret));
  389.             d += 6;
  390.             break;
  391.  
  392.         default:
  393.             *d++ = s[i];
  394.         }
  395.     }
  396.  
  397.     *d = 0;
  398.  
  399.     return ret;
  400. }
  401.  
  402.  
  403. /***************************************************************************
  404. quote spaces in a buffer
  405.   ***************************************************************************/
  406. void quote_spaces(char *buf)
  407. {
  408.     while (*buf) {
  409.         if (*buf == ' ') *buf = '+';
  410.         buf++;
  411.     }
  412. }
  413.  
  414.  
  415.  
  416. /***************************************************************************
  417. tell a browser about a fatal error in the http processing
  418.   ***************************************************************************/
  419. static void cgi_setup_error(char *err, char *header, char *info)
  420. {
  421.     printf("HTTP/1.1 %s\r\n%sConnection: close\r\nContent-Type: text/html\r\n\r\n<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>%s</H1>%s<p></BODY></HTML>\r\n", err, header, err, err, info);
  422.     exit(0);
  423. }
  424.  
  425.  
  426. /***************************************************************************
  427. decode a base64 string in-place - simple and slow algorithm
  428.   ***************************************************************************/
  429. static void base64_decode(char *s)
  430. {
  431.     char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  432.     int bit_offset, byte_offset, idx, i;
  433.     unsigned char *d = (unsigned char *)s;
  434.     char *p;
  435.  
  436.     i=0;
  437.  
  438.     while (*s && (p=strchr(b64,*s))) {
  439.         idx = (int)(p - b64);
  440.         byte_offset = (i*6)/8;
  441.         bit_offset = (i*6)%8;
  442.         d[byte_offset] &= ~((1<<(8-bit_offset))-1);
  443.         if (bit_offset < 3) {
  444.             d[byte_offset] |= (idx << (2-bit_offset));
  445.         } else {
  446.             d[byte_offset] |= (idx >> (bit_offset-2));
  447.             d[byte_offset+1] = 0;
  448.             d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
  449.         }
  450.         s++; i++;
  451.     }
  452. }
  453.  
  454.  
  455. /***************************************************************************
  456. handle a http authentication line
  457.   ***************************************************************************/
  458. static int cgi_handle_authorization(char *line)
  459. {
  460.     char *p, *user, *pass;
  461.     struct passwd *pwd;
  462.     int ret=0;
  463.  
  464.     if (strncasecmp(line,"Basic ", 6)) {
  465.         cgi_setup_error("401 Bad Authorization", "", 
  466.                 "Only basic authorization is understood");
  467.     }
  468.     line += 6;
  469.     while (line[0] == ' ') line++;
  470.     base64_decode(line);
  471.     if (!(p=strchr(line,':'))) {
  472.         cgi_setup_error("401 Bad Authorization", "", 
  473.                 "username/password must be supplied");
  474.     }
  475.     *p = 0;
  476.     user = line;
  477.     pass = p+1;
  478.  
  479.     /* currently only allow connections as root */
  480.     if (strcasecmp(user,"root")) {
  481.         cgi_setup_error("401 Bad Authorization", "", 
  482.                 "incorrect username/password");
  483.     }
  484.     
  485.     pwd = getpwnam(user);
  486.  
  487.     if (!strcmp((char *)crypt(pass, pwd->pw_passwd),pwd->pw_passwd)) {
  488.         ret = 1;
  489.     }
  490.  
  491.     memset(pass, 0, strlen(pass));
  492.  
  493.     return ret;
  494. }
  495.  
  496.  
  497. /***************************************************************************
  498. handle a file download
  499.   ***************************************************************************/
  500. static void cgi_download(char *file)
  501. {
  502.     struct stat st;
  503.     char buf[1024];
  504.     int fd, l, i;
  505.     char *p;
  506.  
  507.     /* sanitise the filename */
  508.     for (i=0;file[i];i++) {
  509.         if (!isalnum(file[i]) && !strchr("/.-_", file[i])) {
  510.             cgi_setup_error("404 File Not Found","",
  511.                     "Illegal character in filename");
  512.         }
  513.     }
  514.  
  515.     if (strstr(file,"..")) {
  516.         cgi_setup_error("404 File Not Found","",
  517.                 "Relative paths not allowed");
  518.     }
  519.  
  520.     if (!file_exist(file, &st)) {
  521.         cgi_setup_error("404 File Not Found","",
  522.                 "The requested file was not found");
  523.     }
  524.     fd = open(file,O_RDONLY);
  525.     if (fd == -1) {
  526.         cgi_setup_error("404 File Not Found","",
  527.                 "The requested file was not found");
  528.     }
  529.     printf("HTTP/1.1 200 OK\r\n");
  530.     if ((p=strrchr(file,'.'))) {
  531.         if (strcmp(p,".gif")==0 || strcmp(p,".jpg")==0) {
  532.             printf("Content-Type: image/gif\r\n");
  533.         } else {
  534.             printf("Content-Type: text/html\r\n");
  535.         }
  536.     }
  537.     printf("Content-Length: %d\r\n\r\n", (int)st.st_size);
  538.     while ((l=read(fd,buf,sizeof(buf)))>0) {
  539.         fwrite(buf, 1, l, stdout);
  540.     }
  541.     close(fd);
  542.     exit(0);
  543. }
  544.  
  545.  
  546. /***************************************************************************
  547. setup the cgi framework, handling the possability that this program is either
  548. run as a true cgi program by a web browser or is itself a mini web server
  549.   ***************************************************************************/
  550. void cgi_setup(char *rootdir)
  551. {
  552.     int authenticated = 0;
  553.     char line[1024];
  554.     char *url=NULL;
  555.     char *p;
  556.  
  557.     if (chdir(rootdir)) {
  558.         cgi_setup_error("400 Server Error", "",
  559.                 "chdir failed - the server is not configured correctly");
  560.     }
  561.  
  562.     if (getenv("CONTENT_LENGTH") || getenv("REQUEST_METHOD")) {
  563.         /* assume we are running under a real web server */
  564.         return;
  565.     }
  566.  
  567.     /* we are a mini-web server. We need to read the request from stdin
  568.        and handle authentication etc */
  569.     while (fgets(line, sizeof(line)-1, stdin)) {
  570.         if (line[0] == '\r' || line[0] == '\n') break;
  571.         if (strncasecmp(line,"GET ", 4)==0) {
  572.             request_get = 1;
  573.             url = strdup(&line[4]);
  574.         } else if (strncasecmp(line,"POST ", 5)==0) {
  575.             request_post = 1;
  576.             url = strdup(&line[5]);
  577.         } else if (strncasecmp(line,"PUT ", 4)==0) {
  578.             cgi_setup_error("400 Bad Request", "",
  579.                     "This server does not accept PUT requests");
  580.         } else if (strncasecmp(line,"Authorization: ", 15)==0) {
  581.             authenticated = cgi_handle_authorization(&line[15]);
  582.         } else if (strncasecmp(line,"Content-Length: ", 16)==0) {
  583.             content_length = atoi(&line[16]);
  584.         }
  585.         /* ignore all other requests! */
  586.     }
  587.  
  588.     if (!authenticated) {
  589.         cgi_setup_error("401 Authorization Required", 
  590.                 "WWW-Authenticate: Basic realm=\"root\"\r\n",
  591.                 "You must be authenticated to use this service");
  592.     }
  593.  
  594.     if (!url) {
  595.         cgi_setup_error("400 Bad Request", "",
  596.                 "You must specify a GET or POST request");
  597.     }
  598.  
  599.     /* trim the URL */
  600.     if ((p = strchr(url,' ')) || (p=strchr(url,'\t'))) {
  601.         *p = 0;
  602.     }
  603.     while (*url && strchr("\r\n",url[strlen(url)-1])) {
  604.         url[strlen(url)-1] = 0;
  605.     }
  606.  
  607.     /* anything following a ? in the URL is part of the query string */
  608.     if ((p=strchr(url,'?'))) {
  609.         query_string = p+1;
  610.         *p = 0;
  611.     }
  612.  
  613.     if (strcmp(url,"/")) {
  614.         cgi_download(url+1);
  615.     }
  616.  
  617.     printf("HTTP/1.1 200 OK\r\nConnection: close\r\n");
  618.     
  619. }
  620.  
  621.  
  622.