/* * Boa, an http server * Copyright (C) 1995 Paul Phillips * Some changes Copyright (C) 1999 Jon Nelson * 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; }