1
0
mirror of https://github.com/samba-team/samba.git synced 2025-02-28 01:58:17 +03:00

this new cgi code includes the ability to act as a mini web server,

allowing people to use web configuration of Samba without installing a
web server
(This used to be commit b4e05c360e77cbf27a95920b613bfe6bc874ea1b)
This commit is contained in:
Andrew Tridgell 1997-11-22 07:51:23 +00:00
parent a5d239e873
commit 8d971f1db1
2 changed files with 1032 additions and 146 deletions

View File

@ -1,6 +1,4 @@
/*
Unix SMB/Netbios implementation.
Version 1.9.
some simple CGI helper routines
Copyright (C) Andrew Tridgell 1997
@ -20,9 +18,19 @@
*/
#include "includes.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#define MAX_VARIABLES 512
#define MAX_VARIABLES 10000
#ifdef DEBUG_COMMENTS
extern void print_title(char *fmt, ...);
#endif
struct var {
char *name;
@ -31,14 +39,60 @@ struct var {
static struct var variables[MAX_VARIABLES];
static int num_variables;
static int content_length;
static int request_post;
static int request_get;
static char *query_string;
static int grab_line(int *cl, char *line, int maxsize)
static void unescape(char *buf)
{
char *p=buf;
while ((p=strchr(p,'+')))
*p = ' ';
p = buf;
while (p && *p && (p=strchr(p,'%'))) {
int c1 = p[1];
int c2 = p[2];
if (c1 >= '0' && c1 <= '9')
c1 = c1 - '0';
else if (c1 >= 'A' && c1 <= 'F')
c1 = 10 + c1 - 'A';
else if (c1 >= 'a' && c1 <= 'f')
c1 = 10 + c1 - 'a';
else {p++; continue;}
if (c2 >= '0' && c2 <= '9')
c2 = c2 - '0';
else if (c2 >= 'A' && c2 <= 'F')
c2 = 10 + c2 - 'A';
else if (c2 >= 'a' && c2 <= 'f')
c2 = 10 + c2 - 'a';
else {p++; continue;}
*p = (c1<<4) | c2;
memcpy(p+1, p+3, strlen(p+3)+1);
p++;
}
}
static char *grab_line(FILE *f, int *cl)
{
char *ret;
int i = 0;
int len = 1024;
ret = (char *)malloc(len);
if (!ret) return NULL;
while ((*cl)) {
int c = fgetc(stdin);
int c = fgetc(f);
(*cl)--;
if (c == EOF) {
@ -46,98 +100,140 @@ static int grab_line(int *cl, char *line, int maxsize)
break;
}
if (c == '+') {
c = ' ';
}
if (c == '\r') continue;
if (strchr("\n&", c)) break;
if (c == '%' && (*cl) >= 2) {
int c1, c2;
c1 = fgetc(stdin);
c2 = fgetc(stdin);
(*cl) -= 2;
if (c1 == EOF || c2 == EOF) break;
if (c1 >= '0' && c1 <= '9')
c1 = c1 - '0';
else if (c1 >= 'A' && c1 <= 'F')
c1 = 10 + c1 - 'A';
else if (c1 >= 'a' && c1 <= 'f')
c1 = 10 + c1 - 'a';
else break;
ret[i++] = c;
if (c2 >= '0' && c2 <= '9')
c2 = c2 - '0';
else if (c2 >= 'A' && c2 <= 'F')
c2 = 10 + c2 - 'A';
else if (c2 >= 'a' && c2 <= 'f')
c2 = 10 + c2 - 'a';
else break;
c = (c1<<4) | c2;
if (i == len-1) {
char *ret2;
ret2 = (char *)realloc(ret, len*2);
if (!ret2) return ret;
len *= 2;
ret = ret2;
}
line[i++] = c;
if (i == maxsize) break;
}
/* now unescape the line */
line[i] = 0;
return 1;
ret[i] = 0;
return ret;
}
/***************************************************************************
load all the variables passed to the CGI program
load all the variables passed to the CGI program. May have multiple variables
with the same name and the same or different values. Takes a file parameter
for simulating CGI invocation eg loading saved preferences.
***************************************************************************/
void cgi_load_variables(void)
void cgi_load_variables(FILE *f1)
{
static pstring line;
char *p;
FILE *f = f1;
static char *line;
char *p, *s, *tok;
int len;
if (!(p=getenv("CONTENT_LENGTH"))) return;
len = atoi(p);
if (len <= 0) return;
while (len && grab_line(&len, line, sizeof(line)-1)) {
p = strchr(line,'=');
if (!p) continue;
*p = 0;
variables[num_variables].name = strdup(line);
variables[num_variables].value = strdup(p+1);
if (!variables[num_variables].name ||
!variables[num_variables].value)
continue;
#if 0
printf("%s=%s<br>\n",
variables[num_variables].name,
variables[num_variables].value);
#ifdef DEBUG_COMMENTS
char dummy[100]="";
print_title(dummy);
printf("<!== Start dump in cgi_load_variables() %s ==>\n",__FILE__);
#endif
num_variables++;
if (num_variables == MAX_VARIABLES) break;
if (!f1) {
f = stdin;
if (!content_length) {
p = getenv("CONTENT_LENGTH");
len = p?atoi(p):0;
} else {
len = content_length;
}
} else {
fseek(f, 0, SEEK_END);
len = ftell(f);
fseek(f, 0, SEEK_SET);
}
if (len > 0 &&
(f1 || request_post ||
((s=getenv("REQUEST_METHOD")) &&
strcasecmp(s,"POST")==0))) {
while (len && (line=grab_line(f, &len))) {
p = strchr(line,'=');
if (!p) continue;
*p = 0;
variables[num_variables].name = strdup(line);
variables[num_variables].value = strdup(p+1);
free(line);
if (!variables[num_variables].name ||
!variables[num_variables].value)
continue;
unescape(variables[num_variables].value);
unescape(variables[num_variables].name);
#ifdef DEBUG_COMMENTS
printf("<!== POST var %s has value \"%s\" ==>\n",
variables[num_variables].name,
variables[num_variables].value);
#endif
num_variables++;
if (num_variables == MAX_VARIABLES) break;
}
}
if (f1) {
#ifdef DEBUG_COMMENTS
printf("<!== End dump in cgi_load_variables() ==>\n");
#endif
return;
}
fclose(stdin);
if ((s=query_string) || (s=getenv("QUERY_STRING"))) {
for (tok=strtok(s,"&;");tok;tok=strtok(NULL,"&;")) {
p = strchr(tok,'=');
if (!p) continue;
*p = 0;
variables[num_variables].name = strdup(tok);
variables[num_variables].value = strdup(p+1);
if (!variables[num_variables].name ||
!variables[num_variables].value)
continue;
unescape(variables[num_variables].value);
unescape(variables[num_variables].name);
#ifdef DEBUG_COMMENTS
printf("<!== Commandline var %s has value \"%s\" ==>\n",
variables[num_variables].name,
variables[num_variables].value);
#endif
num_variables++;
if (num_variables == MAX_VARIABLES) break;
}
}
#ifdef DEBUG_COMMENTS
printf("<!== End dump in cgi_load_variables() ==>\n");
#endif
}
/***************************************************************************
find a variable passed via CGI
Doesn't quite do what you think in the case of POST text variables, because
if they exist they might have a value of "" or even " ", depending on the
browser. Also doesn't allow for variables[] containing multiple variables
with the same name and the same or different values.
***************************************************************************/
char *cgi_variable(char *name)
{
@ -149,6 +245,15 @@ char *cgi_variable(char *name)
return NULL;
}
/***************************************************************************
return a particular cgi variable
***************************************************************************/
char *cgi_vnum(int i, char **name)
{
if (i < 0 || i >= num_variables) return NULL;
*name = variables[i].name;
return variables[i].value;
}
/***************************************************************************
return the value of a CGI boolean variable.
@ -161,3 +266,341 @@ int cgi_boolean(char *name, int def)
return strcmp(p, "1") == 0;
}
/***************************************************************************
like strdup() but quotes < > and &
***************************************************************************/
char *quotedup(char *s)
{
int i, n=0;
int len;
char *ret;
char *d;
if (!s) return strdup("");
len = strlen(s);
for (i=0;i<len;i++)
if (s[i] == '<' || s[i] == '>' || s[i] == '&')
n++;
ret = malloc(len + n*6 + 1);
if (!ret) return NULL;
d = ret;
for (i=0;i<len;i++) {
switch (s[i]) {
case '<':
strcpy(d, "&lt;");
d += 4;
break;
case '>':
strcpy(d, "&gt;");
d += 4;
break;
case '&':
strcpy(d, "&amp;");
d += 5;
break;
default:
*d++ = s[i];
}
}
*d = 0;
return ret;
}
/***************************************************************************
like strdup() but quotes a wide range of characters
***************************************************************************/
char *urlquote(char *s)
{
int i, n=0;
int len;
char *ret;
char *d;
char *qlist = "\"\n\r'&<> \t+;";
if (!s) return strdup("");
len = strlen(s);
for (i=0;i<len;i++)
if (strchr(qlist, s[i])) n++;
ret = malloc(len + n*2 + 1);
if (!ret) return NULL;
d = ret;
for (i=0;i<len;i++) {
if (strchr(qlist,s[i])) {
sprintf(d, "%%%02X", (int)s[i]);
d += 3;
} else {
*d++ = s[i];
}
}
*d = 0;
return ret;
}
/***************************************************************************
like strdup() but quotes " characters
***************************************************************************/
char *quotequotes(char *s)
{
int i, n=0;
int len;
char *ret;
char *d;
if (!s) return strdup("");
len = strlen(s);
for (i=0;i<len;i++)
if (s[i] == '"')
n++;
ret = malloc(len + n*6 + 1);
if (!ret) return NULL;
d = ret;
for (i=0;i<len;i++) {
switch (s[i]) {
case '"':
strcpy(d, "&quot;");
d += 6;
break;
default:
*d++ = s[i];
}
}
*d = 0;
return ret;
}
/***************************************************************************
quote spaces in a buffer
***************************************************************************/
void quote_spaces(char *buf)
{
while (*buf) {
if (*buf == ' ') *buf = '+';
buf++;
}
}
/***************************************************************************
tell a browser about a fatal error in the http processing
***************************************************************************/
static void cgi_setup_error(char *err, char *header, char *info)
{
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);
exit(0);
}
/***************************************************************************
decode a base64 string in-place - simple and slow algorithm
***************************************************************************/
static void base64_decode(char *s)
{
char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int bit_offset, byte_offset, idx, i;
unsigned char *d = (unsigned char *)s;
char *p;
i=0;
while (*s && (p=strchr(b64,*s))) {
idx = (int)(p - b64);
byte_offset = (i*6)/8;
bit_offset = (i*6)%8;
d[byte_offset] &= ~((1<<(8-bit_offset))-1);
if (bit_offset < 3) {
d[byte_offset] |= (idx << (2-bit_offset));
} else {
d[byte_offset] |= (idx >> (bit_offset-2));
d[byte_offset+1] = 0;
d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
}
s++; i++;
}
}
/***************************************************************************
handle a http authentication line
***************************************************************************/
static int cgi_handle_authorization(char *line)
{
char *p, *user, *pass;
struct passwd *pwd;
int ret=0;
if (strncasecmp(line,"Basic ", 6)) {
cgi_setup_error("401 Bad Authorization", "",
"Only basic authorization is understood");
}
line += 6;
while (line[0] == ' ') line++;
base64_decode(line);
if (!(p=strchr(line,':'))) {
cgi_setup_error("401 Bad Authorization", "",
"username/password must be supplied");
}
*p = 0;
user = line;
pass = p+1;
/* currently only allow connections as root */
if (strcasecmp(user,"root")) {
cgi_setup_error("401 Bad Authorization", "",
"incorrect username/password");
}
pwd = getpwnam(user);
if (!strcmp((char *)crypt(pass, pwd->pw_passwd),pwd->pw_passwd)) {
ret = 1;
}
memset(pass, 0, strlen(pass));
return ret;
}
/***************************************************************************
handle a file download
***************************************************************************/
static void cgi_download(char *file)
{
struct stat st;
char buf[1024];
int fd, l;
char *p;
if (!file_exist(file, &st)) {
cgi_setup_error("404 File Not Found","",
"The requested file was not found");
}
fd = open(file,O_RDONLY);
if (fd == -1) {
cgi_setup_error("404 File Not Found","",
"The requested file was not found");
}
printf("HTTP/1.1 200 OK\r\n");
if ((p=strrchr(file,'.'))) {
if (strcmp(p,".gif")==0 || strcmp(p,".jpg")==0) {
printf("Content-Type: image/gif\r\n");
} else {
printf("Content-Type: text/html\r\n");
}
}
printf("Content-Length: %d\r\n\r\n", (int)st.st_size);
while ((l=read(fd,buf,sizeof(buf)))>0) {
fwrite(buf, 1, l, stdout);
}
close(fd);
exit(0);
}
/***************************************************************************
setup the cgi framework, handling the possability that this program is either
run as a true cgi program by a web browser or is itself a mini web server
***************************************************************************/
void cgi_setup(char *rootdir)
{
int authenticated = 0;
char line[1024];
char *url=NULL;
char *p;
if (chdir(rootdir)) {
cgi_setup_error("400 Server Error", "",
"chdir failed - the server is not configured correctly");
}
if (getenv("CONTENT_LENGTH") || getenv("REQUEST_METHOD")) {
/* assume we are running under a real web server */
return;
}
/* we are a mini-web server. We need to read the request from stdin
and handle authentication etc */
while (fgets(line, sizeof(line)-1, stdin)) {
if (line[0] == '\r' || line[0] == '\n') break;
if (strncasecmp(line,"GET ", 4)==0) {
request_get = 1;
url = strdup(&line[4]);
} else if (strncasecmp(line,"POST ", 5)==0) {
request_post = 1;
url = strdup(&line[5]);
} else if (strncasecmp(line,"PUT ", 4)==0) {
cgi_setup_error("400 Bad Request", "",
"This server does not accept PUT requests");
} else if (strncasecmp(line,"Authorization: ", 15)==0) {
authenticated = cgi_handle_authorization(&line[15]);
} else if (strncasecmp(line,"Content-Length: ", 16)==0) {
content_length = atoi(&line[16]);
}
/* ignore all other requests! */
}
if (!authenticated) {
cgi_setup_error("401 Authorization Required",
"WWW-Authenticate: Basic realm=\"samba\"\r\n",
"You must be authenticated to use this service");
}
if (!url) {
cgi_setup_error("400 Bad Request", "",
"You must specify a GET or POST request");
}
/* trim the URL */
if ((p = strchr(url,' ')) || (p=strchr(url,'\t'))) {
*p = 0;
}
while (*url && strchr("\r\n",url[strlen(url)-1])) {
url[strlen(url)-1] = 0;
}
/* anything following a ? in the URL is part of the query string */
if ((p=strchr(url,'?'))) {
query_string = p+1;
*p = 0;
}
if (strcmp(url,"/")) {
cgi_download(url+1);
}
printf("HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n");
}

View File

@ -1,6 +1,4 @@
/*
Unix SMB/Netbios implementation.
Version 1.9.
some simple CGI helper routines
Copyright (C) Andrew Tridgell 1997
@ -20,9 +18,19 @@
*/
#include "includes.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#define MAX_VARIABLES 512
#define MAX_VARIABLES 10000
#ifdef DEBUG_COMMENTS
extern void print_title(char *fmt, ...);
#endif
struct var {
char *name;
@ -31,14 +39,60 @@ struct var {
static struct var variables[MAX_VARIABLES];
static int num_variables;
static int content_length;
static int request_post;
static int request_get;
static char *query_string;
static int grab_line(int *cl, char *line, int maxsize)
static void unescape(char *buf)
{
char *p=buf;
while ((p=strchr(p,'+')))
*p = ' ';
p = buf;
while (p && *p && (p=strchr(p,'%'))) {
int c1 = p[1];
int c2 = p[2];
if (c1 >= '0' && c1 <= '9')
c1 = c1 - '0';
else if (c1 >= 'A' && c1 <= 'F')
c1 = 10 + c1 - 'A';
else if (c1 >= 'a' && c1 <= 'f')
c1 = 10 + c1 - 'a';
else {p++; continue;}
if (c2 >= '0' && c2 <= '9')
c2 = c2 - '0';
else if (c2 >= 'A' && c2 <= 'F')
c2 = 10 + c2 - 'A';
else if (c2 >= 'a' && c2 <= 'f')
c2 = 10 + c2 - 'a';
else {p++; continue;}
*p = (c1<<4) | c2;
memcpy(p+1, p+3, strlen(p+3)+1);
p++;
}
}
static char *grab_line(FILE *f, int *cl)
{
char *ret;
int i = 0;
int len = 1024;
ret = (char *)malloc(len);
if (!ret) return NULL;
while ((*cl)) {
int c = fgetc(stdin);
int c = fgetc(f);
(*cl)--;
if (c == EOF) {
@ -46,98 +100,140 @@ static int grab_line(int *cl, char *line, int maxsize)
break;
}
if (c == '+') {
c = ' ';
}
if (c == '\r') continue;
if (strchr("\n&", c)) break;
if (c == '%' && (*cl) >= 2) {
int c1, c2;
c1 = fgetc(stdin);
c2 = fgetc(stdin);
(*cl) -= 2;
if (c1 == EOF || c2 == EOF) break;
if (c1 >= '0' && c1 <= '9')
c1 = c1 - '0';
else if (c1 >= 'A' && c1 <= 'F')
c1 = 10 + c1 - 'A';
else if (c1 >= 'a' && c1 <= 'f')
c1 = 10 + c1 - 'a';
else break;
ret[i++] = c;
if (c2 >= '0' && c2 <= '9')
c2 = c2 - '0';
else if (c2 >= 'A' && c2 <= 'F')
c2 = 10 + c2 - 'A';
else if (c2 >= 'a' && c2 <= 'f')
c2 = 10 + c2 - 'a';
else break;
c = (c1<<4) | c2;
if (i == len-1) {
char *ret2;
ret2 = (char *)realloc(ret, len*2);
if (!ret2) return ret;
len *= 2;
ret = ret2;
}
line[i++] = c;
if (i == maxsize) break;
}
/* now unescape the line */
line[i] = 0;
return 1;
ret[i] = 0;
return ret;
}
/***************************************************************************
load all the variables passed to the CGI program
load all the variables passed to the CGI program. May have multiple variables
with the same name and the same or different values. Takes a file parameter
for simulating CGI invocation eg loading saved preferences.
***************************************************************************/
void cgi_load_variables(void)
void cgi_load_variables(FILE *f1)
{
static pstring line;
char *p;
FILE *f = f1;
static char *line;
char *p, *s, *tok;
int len;
if (!(p=getenv("CONTENT_LENGTH"))) return;
len = atoi(p);
if (len <= 0) return;
while (len && grab_line(&len, line, sizeof(line)-1)) {
p = strchr(line,'=');
if (!p) continue;
*p = 0;
variables[num_variables].name = strdup(line);
variables[num_variables].value = strdup(p+1);
if (!variables[num_variables].name ||
!variables[num_variables].value)
continue;
#if 0
printf("%s=%s<br>\n",
variables[num_variables].name,
variables[num_variables].value);
#ifdef DEBUG_COMMENTS
char dummy[100]="";
print_title(dummy);
printf("<!== Start dump in cgi_load_variables() %s ==>\n",__FILE__);
#endif
num_variables++;
if (num_variables == MAX_VARIABLES) break;
if (!f1) {
f = stdin;
if (!content_length) {
p = getenv("CONTENT_LENGTH");
len = p?atoi(p):0;
} else {
len = content_length;
}
} else {
fseek(f, 0, SEEK_END);
len = ftell(f);
fseek(f, 0, SEEK_SET);
}
if (len > 0 &&
(f1 || request_post ||
((s=getenv("REQUEST_METHOD")) &&
strcasecmp(s,"POST")==0))) {
while (len && (line=grab_line(f, &len))) {
p = strchr(line,'=');
if (!p) continue;
*p = 0;
variables[num_variables].name = strdup(line);
variables[num_variables].value = strdup(p+1);
free(line);
if (!variables[num_variables].name ||
!variables[num_variables].value)
continue;
unescape(variables[num_variables].value);
unescape(variables[num_variables].name);
#ifdef DEBUG_COMMENTS
printf("<!== POST var %s has value \"%s\" ==>\n",
variables[num_variables].name,
variables[num_variables].value);
#endif
num_variables++;
if (num_variables == MAX_VARIABLES) break;
}
}
if (f1) {
#ifdef DEBUG_COMMENTS
printf("<!== End dump in cgi_load_variables() ==>\n");
#endif
return;
}
fclose(stdin);
if ((s=query_string) || (s=getenv("QUERY_STRING"))) {
for (tok=strtok(s,"&;");tok;tok=strtok(NULL,"&;")) {
p = strchr(tok,'=');
if (!p) continue;
*p = 0;
variables[num_variables].name = strdup(tok);
variables[num_variables].value = strdup(p+1);
if (!variables[num_variables].name ||
!variables[num_variables].value)
continue;
unescape(variables[num_variables].value);
unescape(variables[num_variables].name);
#ifdef DEBUG_COMMENTS
printf("<!== Commandline var %s has value \"%s\" ==>\n",
variables[num_variables].name,
variables[num_variables].value);
#endif
num_variables++;
if (num_variables == MAX_VARIABLES) break;
}
}
#ifdef DEBUG_COMMENTS
printf("<!== End dump in cgi_load_variables() ==>\n");
#endif
}
/***************************************************************************
find a variable passed via CGI
Doesn't quite do what you think in the case of POST text variables, because
if they exist they might have a value of "" or even " ", depending on the
browser. Also doesn't allow for variables[] containing multiple variables
with the same name and the same or different values.
***************************************************************************/
char *cgi_variable(char *name)
{
@ -149,6 +245,15 @@ char *cgi_variable(char *name)
return NULL;
}
/***************************************************************************
return a particular cgi variable
***************************************************************************/
char *cgi_vnum(int i, char **name)
{
if (i < 0 || i >= num_variables) return NULL;
*name = variables[i].name;
return variables[i].value;
}
/***************************************************************************
return the value of a CGI boolean variable.
@ -161,3 +266,341 @@ int cgi_boolean(char *name, int def)
return strcmp(p, "1") == 0;
}
/***************************************************************************
like strdup() but quotes < > and &
***************************************************************************/
char *quotedup(char *s)
{
int i, n=0;
int len;
char *ret;
char *d;
if (!s) return strdup("");
len = strlen(s);
for (i=0;i<len;i++)
if (s[i] == '<' || s[i] == '>' || s[i] == '&')
n++;
ret = malloc(len + n*6 + 1);
if (!ret) return NULL;
d = ret;
for (i=0;i<len;i++) {
switch (s[i]) {
case '<':
strcpy(d, "&lt;");
d += 4;
break;
case '>':
strcpy(d, "&gt;");
d += 4;
break;
case '&':
strcpy(d, "&amp;");
d += 5;
break;
default:
*d++ = s[i];
}
}
*d = 0;
return ret;
}
/***************************************************************************
like strdup() but quotes a wide range of characters
***************************************************************************/
char *urlquote(char *s)
{
int i, n=0;
int len;
char *ret;
char *d;
char *qlist = "\"\n\r'&<> \t+;";
if (!s) return strdup("");
len = strlen(s);
for (i=0;i<len;i++)
if (strchr(qlist, s[i])) n++;
ret = malloc(len + n*2 + 1);
if (!ret) return NULL;
d = ret;
for (i=0;i<len;i++) {
if (strchr(qlist,s[i])) {
sprintf(d, "%%%02X", (int)s[i]);
d += 3;
} else {
*d++ = s[i];
}
}
*d = 0;
return ret;
}
/***************************************************************************
like strdup() but quotes " characters
***************************************************************************/
char *quotequotes(char *s)
{
int i, n=0;
int len;
char *ret;
char *d;
if (!s) return strdup("");
len = strlen(s);
for (i=0;i<len;i++)
if (s[i] == '"')
n++;
ret = malloc(len + n*6 + 1);
if (!ret) return NULL;
d = ret;
for (i=0;i<len;i++) {
switch (s[i]) {
case '"':
strcpy(d, "&quot;");
d += 6;
break;
default:
*d++ = s[i];
}
}
*d = 0;
return ret;
}
/***************************************************************************
quote spaces in a buffer
***************************************************************************/
void quote_spaces(char *buf)
{
while (*buf) {
if (*buf == ' ') *buf = '+';
buf++;
}
}
/***************************************************************************
tell a browser about a fatal error in the http processing
***************************************************************************/
static void cgi_setup_error(char *err, char *header, char *info)
{
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);
exit(0);
}
/***************************************************************************
decode a base64 string in-place - simple and slow algorithm
***************************************************************************/
static void base64_decode(char *s)
{
char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int bit_offset, byte_offset, idx, i;
unsigned char *d = (unsigned char *)s;
char *p;
i=0;
while (*s && (p=strchr(b64,*s))) {
idx = (int)(p - b64);
byte_offset = (i*6)/8;
bit_offset = (i*6)%8;
d[byte_offset] &= ~((1<<(8-bit_offset))-1);
if (bit_offset < 3) {
d[byte_offset] |= (idx << (2-bit_offset));
} else {
d[byte_offset] |= (idx >> (bit_offset-2));
d[byte_offset+1] = 0;
d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
}
s++; i++;
}
}
/***************************************************************************
handle a http authentication line
***************************************************************************/
static int cgi_handle_authorization(char *line)
{
char *p, *user, *pass;
struct passwd *pwd;
int ret=0;
if (strncasecmp(line,"Basic ", 6)) {
cgi_setup_error("401 Bad Authorization", "",
"Only basic authorization is understood");
}
line += 6;
while (line[0] == ' ') line++;
base64_decode(line);
if (!(p=strchr(line,':'))) {
cgi_setup_error("401 Bad Authorization", "",
"username/password must be supplied");
}
*p = 0;
user = line;
pass = p+1;
/* currently only allow connections as root */
if (strcasecmp(user,"root")) {
cgi_setup_error("401 Bad Authorization", "",
"incorrect username/password");
}
pwd = getpwnam(user);
if (!strcmp((char *)crypt(pass, pwd->pw_passwd),pwd->pw_passwd)) {
ret = 1;
}
memset(pass, 0, strlen(pass));
return ret;
}
/***************************************************************************
handle a file download
***************************************************************************/
static void cgi_download(char *file)
{
struct stat st;
char buf[1024];
int fd, l;
char *p;
if (!file_exist(file, &st)) {
cgi_setup_error("404 File Not Found","",
"The requested file was not found");
}
fd = open(file,O_RDONLY);
if (fd == -1) {
cgi_setup_error("404 File Not Found","",
"The requested file was not found");
}
printf("HTTP/1.1 200 OK\r\n");
if ((p=strrchr(file,'.'))) {
if (strcmp(p,".gif")==0 || strcmp(p,".jpg")==0) {
printf("Content-Type: image/gif\r\n");
} else {
printf("Content-Type: text/html\r\n");
}
}
printf("Content-Length: %d\r\n\r\n", (int)st.st_size);
while ((l=read(fd,buf,sizeof(buf)))>0) {
fwrite(buf, 1, l, stdout);
}
close(fd);
exit(0);
}
/***************************************************************************
setup the cgi framework, handling the possability that this program is either
run as a true cgi program by a web browser or is itself a mini web server
***************************************************************************/
void cgi_setup(char *rootdir)
{
int authenticated = 0;
char line[1024];
char *url=NULL;
char *p;
if (chdir(rootdir)) {
cgi_setup_error("400 Server Error", "",
"chdir failed - the server is not configured correctly");
}
if (getenv("CONTENT_LENGTH") || getenv("REQUEST_METHOD")) {
/* assume we are running under a real web server */
return;
}
/* we are a mini-web server. We need to read the request from stdin
and handle authentication etc */
while (fgets(line, sizeof(line)-1, stdin)) {
if (line[0] == '\r' || line[0] == '\n') break;
if (strncasecmp(line,"GET ", 4)==0) {
request_get = 1;
url = strdup(&line[4]);
} else if (strncasecmp(line,"POST ", 5)==0) {
request_post = 1;
url = strdup(&line[5]);
} else if (strncasecmp(line,"PUT ", 4)==0) {
cgi_setup_error("400 Bad Request", "",
"This server does not accept PUT requests");
} else if (strncasecmp(line,"Authorization: ", 15)==0) {
authenticated = cgi_handle_authorization(&line[15]);
} else if (strncasecmp(line,"Content-Length: ", 16)==0) {
content_length = atoi(&line[16]);
}
/* ignore all other requests! */
}
if (!authenticated) {
cgi_setup_error("401 Authorization Required",
"WWW-Authenticate: Basic realm=\"samba\"\r\n",
"You must be authenticated to use this service");
}
if (!url) {
cgi_setup_error("400 Bad Request", "",
"You must specify a GET or POST request");
}
/* trim the URL */
if ((p = strchr(url,' ')) || (p=strchr(url,'\t'))) {
*p = 0;
}
while (*url && strchr("\r\n",url[strlen(url)-1])) {
url[strlen(url)-1] = 0;
}
/* anything following a ? in the URL is part of the query string */
if ((p=strchr(url,'?'))) {
query_string = p+1;
*p = 0;
}
if (strcmp(url,"/")) {
cgi_download(url+1);
}
printf("HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n");
}