260 lines
7.4 KiB
C
260 lines
7.4 KiB
C
/*
|
|
* Boa, an http server
|
|
* Copyright (C) 1995 Paul Phillips <paulp@go2net.com>
|
|
* Some changes Copyright (C) 1999 Jon Nelson <jnelson@boa.org>
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 1, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
/* $Id: buffer.c,v 1.10.2.2 2002/07/26 03:03:44 jnelson Exp $ */
|
|
|
|
#include "boa.h"
|
|
#include "escape.h"
|
|
|
|
#define INT_TO_HEX(x) \
|
|
((((x)-10)>=0)?('A'+((x)-10)):('0'+(x)))
|
|
|
|
/*
|
|
* Name: req_write
|
|
*
|
|
* Description: Buffers data before sending to client.
|
|
* Returns: -1 for error, otherwise how much is stored
|
|
*/
|
|
|
|
int req_write(request * req, char *msg)
|
|
{
|
|
int msg_len;
|
|
|
|
msg_len = strlen(msg);
|
|
|
|
if (!msg_len || req->status == DEAD)
|
|
return req->buffer_end;
|
|
|
|
if (req->buffer_end + msg_len > BUFFER_SIZE) {
|
|
log_error_time();
|
|
fprintf(stderr, "Ran out of Buffer space!\n");
|
|
req->status = DEAD;
|
|
return -1;
|
|
}
|
|
memcpy(req->buffer + req->buffer_end, msg, msg_len);
|
|
req->buffer_end += msg_len;
|
|
return req->buffer_end;
|
|
}
|
|
|
|
void reset_output_buffer(request *req)
|
|
{
|
|
req->buffer_end = 0;
|
|
}
|
|
|
|
/*
|
|
* Name: req_write_escape_http
|
|
* Description: Buffers and "escapes" data before sending to client.
|
|
* as above, but translates as it copies, into a form suitably
|
|
* encoded for URLs in HTTP headers.
|
|
* Returns: -1 for error, otherwise how much is stored
|
|
*/
|
|
int req_write_escape_http(request * req, char *msg)
|
|
{
|
|
char c, *inp, *dest;
|
|
int left;
|
|
inp = msg;
|
|
dest = req->buffer + req->buffer_end;
|
|
/* 3 is a guard band, since we don't check the destination pointer
|
|
* in the middle of a transfer of up to 3 bytes */
|
|
left = BUFFER_SIZE - req->buffer_end - 3;
|
|
while ((c = *inp++) && left > 0) {
|
|
if (needs_escape((unsigned int) c)) {
|
|
*dest++ = '%';
|
|
*dest++ = INT_TO_HEX(c >> 4);
|
|
*dest++ = INT_TO_HEX(c & 15);
|
|
left -= 3;
|
|
} else {
|
|
*dest++ = c;
|
|
left--;
|
|
}
|
|
}
|
|
req->buffer_end = dest - req->buffer;
|
|
if (left == 0) {
|
|
log_error_time();
|
|
fprintf(stderr, "Ran out of Buffer space!\n");
|
|
req->status = DEAD;
|
|
return -1;
|
|
}
|
|
return req->buffer_end;
|
|
}
|
|
|
|
/*
|
|
* Name: req_write_escape_html
|
|
* Description: Buffers and "escapes" data before sending to client.
|
|
* as above, but translates as it copies, into a form suitably
|
|
* encoded for HTML bodies.
|
|
* Returns: -1 for error, otherwise how much is stored
|
|
*/
|
|
int req_write_escape_html(request * req, char *msg)
|
|
{
|
|
char c, *inp, *dest;
|
|
int left;
|
|
inp = msg;
|
|
dest = req->buffer + req->buffer_end;
|
|
/* 5 is a guard band, since we don't check the destination pointer
|
|
* in the middle of a transfer of up to 5 bytes */
|
|
left = BUFFER_SIZE - req->buffer_end - 5;
|
|
while ((c = *inp++) && left > 0) {
|
|
switch (c) {
|
|
case '>':
|
|
*dest++ = '&';
|
|
*dest++ = 'g';
|
|
*dest++ = 't';
|
|
*dest++ = ';';
|
|
left -= 4;
|
|
break;
|
|
case '<':
|
|
*dest++ = '&';
|
|
*dest++ = 'l';
|
|
*dest++ = 't';
|
|
*dest++ = ';';
|
|
left -= 4;
|
|
break;
|
|
case '&':
|
|
*dest++ = '&';
|
|
*dest++ = 'a';
|
|
*dest++ = 'm';
|
|
*dest++ = 'p';
|
|
*dest++ = ';';
|
|
left -= 5;
|
|
break;
|
|
case '\"':
|
|
*dest++ = '&';
|
|
*dest++ = 'q';
|
|
*dest++ = 'u';
|
|
*dest++ = 'o';
|
|
*dest++ = 't';
|
|
*dest++ = ';';
|
|
left -= 6;
|
|
break;
|
|
default:
|
|
*dest++ = c;
|
|
left--;
|
|
}
|
|
}
|
|
req->buffer_end = dest - req->buffer;
|
|
if (left == 0) {
|
|
log_error_time();
|
|
fprintf(stderr, "Ran out of Buffer space!\n");
|
|
req->status = DEAD;
|
|
return -1;
|
|
}
|
|
return req->buffer_end;
|
|
}
|
|
|
|
|
|
/*
|
|
* Name: flush_req
|
|
*
|
|
* Description: Sends any backlogged buffer to client.
|
|
*
|
|
* Returns: -2 for error, -1 for blocked, otherwise how much is stored
|
|
*/
|
|
|
|
int req_flush(request * req)
|
|
{
|
|
int bytes_to_write;
|
|
|
|
bytes_to_write = req->buffer_end - req->buffer_start;
|
|
if (req->status == DEAD)
|
|
return -2;
|
|
|
|
if (bytes_to_write) {
|
|
int bytes_written;
|
|
|
|
bytes_written = write(req->fd, req->buffer + req->buffer_start,
|
|
bytes_to_write);
|
|
|
|
if (bytes_written < 0) {
|
|
if (errno == EWOULDBLOCK || errno == EAGAIN)
|
|
return -1; /* request blocked at the pipe level, but keep going */
|
|
else {
|
|
req->buffer_start = req->buffer_end = 0;
|
|
if (errno != EPIPE)
|
|
perror("buffer flush"); /* OK to disable if your logs get too big */
|
|
req->status = DEAD;
|
|
req->buffer_end = 0;
|
|
return -2;
|
|
}
|
|
}
|
|
#ifdef FASCIST_LOGGING
|
|
log_error_time();
|
|
fprintf(stderr, "%s:%d - Wrote \"", __FILE__, __LINE__);
|
|
fwrite(req->buffer + req->buffer_start, sizeof (char),
|
|
bytes_written, stderr);
|
|
fprintf(stderr, "\" (%d bytes)\n", bytes_written);
|
|
#endif
|
|
req->buffer_start += bytes_written;
|
|
}
|
|
if (req->buffer_start == req->buffer_end)
|
|
req->buffer_start = req->buffer_end = 0;
|
|
return req->buffer_end; /* successful */
|
|
}
|
|
|
|
/*
|
|
* Name: escape_string
|
|
*
|
|
* Description: escapes the string inp. Uses variable buf. If buf is
|
|
* NULL when the program starts, it will attempt to dynamically allocate
|
|
* the space that it needs, otherwise it will assume that the user
|
|
* has already allocated enough space for the variable buf, which
|
|
* could be up to 3 times the size of inp. If the routine dynamically
|
|
* allocates the space, the user is responsible for freeing it afterwords
|
|
* Returns: NULL on error, pointer to string otherwise.
|
|
* Note: this function doesn't really belong here, I plopped it here to
|
|
* work around a "bug" in escape.h (it defines a global, so can't be
|
|
* used in multiple source files). Actually, this routine shouldn't
|
|
* exist anywhere, it's only usage is in get.c's handling of on-the-fly
|
|
* directory generation, which would be better configured to use a combination
|
|
* of req_write_escape_http and req_write_escape_html. That would involve
|
|
* more work than I'm willing to put in right now, though, so here we are.
|
|
*/
|
|
|
|
char *escape_string(char *inp, char *buf)
|
|
{
|
|
int max;
|
|
char *index;
|
|
unsigned char c;
|
|
|
|
max = strlen(inp) * 3;
|
|
|
|
if (buf == NULL && max)
|
|
buf = malloc(sizeof (char) * max + 1);
|
|
|
|
if (buf == NULL) {
|
|
log_error_time();
|
|
perror("malloc");
|
|
return NULL;
|
|
}
|
|
|
|
index = buf;
|
|
while ((c = *inp++) && max > 0) {
|
|
if (needs_escape((unsigned int) c)) {
|
|
*index++ = '%';
|
|
*index++ = INT_TO_HEX(c >> 4);
|
|
*index++ = INT_TO_HEX(c & 15);
|
|
} else
|
|
*index++ = c;
|
|
}
|
|
*index = '\0';
|
|
return buf;
|
|
}
|