#include "cgi.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <cx/string.h>
#include "../util/util.h"
#include "../util/pblock.h"
#include "../daemon/netsite.h"
#include "../daemon/vfs.h"
#include "../util/io.h"
#include "../daemon/event.h"
#include "cgiutils.h"
#define CGI_VARS 32
#define CGI_RESPONSE_PARSER_BUFLEN 2048
#define CGI_RESPONSE_MAX_LINE_LENGTH 512
int send_cgi(pblock *pb, Session *sn, Request *rq) {
char *path = pblock_findkeyval(pb_key_path, rq->vars);
char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers);
int64_t content_length =
0;
log_ereport(
LOG_DEBUG,
"cgi-send: path: %s req: %p content-length: %s", path, rq, ctlen);
if(ctlen) {
if(!util_strtoint(ctlen, &content_length)) {
log_ereport(
LOG_FAILURE,
"send-cgi: content-length header is not an integer");
protocol_status(sn, rq,
400,
NULL);
return REQ_ABORTED;
}
}
if(!vfs_is_sys(rq->vfs)) {
log_ereport(
LOG_WARN,
"send-cgi: VFS setting ignored");
}
struct stat s;
if(stat(path, &s)) {
int statuscode = util_errno2status(errno);
protocol_status(sn, rq, statuscode,
NULL);
return REQ_ABORTED;
}
if(
S_ISDIR(s.st_mode)) {
protocol_status(sn, rq,
403,
NULL);
return REQ_ABORTED;
}
param_free(pblock_remove(
"content-type", rq->srvhdrs));
const char *args = pblock_findval(
"query", rq->reqpb);
char **argv = cgi_create_argv(path,
NULL, args);
if(!argv) {
return REQ_ABORTED;
}
char **env = http_hdrs2env(rq->headers);
env = cgi_common_vars(sn, rq, env);
env = cgi_specific_vars(sn, rq, args, env,
1);
CGIHandler *handler = pool_malloc(sn->pool,
sizeof(CGIHandler));
if(!handler) {
return REQ_ABORTED;
}
ZERO(handler,
sizeof(CGIHandler));
handler->path = path;
int ret = cgi_start(&handler->process, path, argv, env);
if(ret !=
REQ_PROCEED) {
util_env_free(env);
cgi_free_argv(argv);
return ret;
}
log_ereport(
LOG_DEBUG,
"send-cgi: req: %p pid: %d", rq, (
int)handler->process.pid);
util_env_free(env);
cgi_free_argv(argv);
char buf[
4096];
ssize_t r;
if(content_length >
0) {
ssize_t n =
0;
while(n < content_length) {
r = netbuf_getbytes(sn->inbuf, buf,
4096);
if(r <=
0) {
log_ereport(
LOG_FAILURE,
"send-cgi: script: %s: cannot read request body",
path);
kill(handler->process.pid,
SIGTERM);
cgi_close(&handler->process);
return REQ_ABORTED;
}
ssize_t w = write(handler->process.in[
1], buf, r);
if(w <=
0) {
log_ereport(
LOG_FAILURE,
"send-cgi: script: %s: cannot send request body to cgi process",
path);
kill(handler->process.pid,
SIGTERM);
cgi_close(&handler->process);
return REQ_ABORTED;
}
n += r;
}
}
system_close(handler->process.in[
1]);
handler->process.in[
1] = -
1;
handler->parser = cgi_parser_new(sn, rq);
int flags;
if ((flags = fcntl(handler->process.err[
0],
F_GETFL,
0)) == -
1) {
flags =
0;
}
if (fcntl(handler->process.err[
0],
F_SETFL, flags |
O_NONBLOCK) !=
0) {
log_ereport(
LOG_FAILURE,
"cgi-bin: fcntl err[0] failed: %s", strerror(errno));
}
if ((flags = fcntl(handler->process.out[
0],
F_GETFL,
0)) == -
1) {
flags =
0;
}
if (fcntl(handler->process.out[
0],
F_SETFL, flags |
O_NONBLOCK) !=
0) {
log_ereport(
LOG_FAILURE,
"cgi-bin: fcntl out[0] failed: %s", strerror(errno));
}
Event *readev = pool_malloc(sn->pool,
sizeof(Event));
ZERO(readev,
sizeof(Event));
readev->cookie = handler;
readev->fn = cgi_stdout_readevent;
readev->finish = cgi_event_finish;
Event *stderr_readev = pool_malloc(sn->pool,
sizeof(Event));
ZERO(stderr_readev,
sizeof(Event));
stderr_readev->cookie = handler;
stderr_readev->fn = cgi_stderr_readevent;
stderr_readev->finish = cgi_event_finish;
Event *writeev = pool_malloc(sn->pool,
sizeof(Event));
ZERO(writeev,
sizeof(Event));
writeev->cookie = handler;
writeev->fn = cgi_writeevent;
writeev->finish = cgi_event_finish;
handler->readev = readev;
handler->writeev = writeev;
net_setnonblock(sn->csd,
1);
int error =
0;
if(ev_pollin(sn->ev, handler->process.err[
0], stderr_readev)) {
log_ereport(
LOG_FAILURE,
"send-cgi: stderr ev_pollin failed");
error =
1;
}
else {
handler->wait_read =
TRUE;
handler->events++;
}
if(!error && ev_pollin(sn->ev, handler->process.out[
0], readev)) {
log_ereport(
LOG_FAILURE,
"send-cgi: stdout ev_pollin failed");
error =
1;
}
else {
handler->events++;
}
if(error) {
log_ereport(
LOG_FAILURE,
"cgi-send: initialization error: kill script: %s", path);
kill(handler->process.pid,
SIGKILL);
cgi_parser_free(handler->parser);
cgi_close(&handler->process);
return REQ_ABORTED;
}
return REQ_PROCESSING;
}
static int cgi_try_write_flush(CGIHandler *handler, Session *sn) {
ssize_t wr =
0;
while(
handler->writebuf_size - handler->writebuf_pos >
0 &&
(wr = net_write(
sn->csd,
handler->writebuf + handler->writebuf_pos,
handler->writebuf_size - handler->writebuf_pos))
>
0)
{
handler->writebuf_pos += wr;
handler->count_write += wr;
}
if(handler->writebuf_size - handler->writebuf_pos >
0) {
if(net_errno(sn->csd) !=
EAGAIN) {
handler->result =
REQ_ABORTED;
log_ereport(
LOG_FAILURE,
"cgi pid %d %s: network error: %s",
(
int)handler->process.pid,
handler->path,
strerror(net_errno(sn->csd)));
}
return 1;
}
return 0;
}
static int cgi_try_write(CGIHandler *handler, EventHandler *ev, Session *sn,
char *buf,
size_t size) {
size_t pos =
0;
ssize_t wr =
0;
while(size - pos >
0 && (wr = net_write(sn->csd, buf + pos, size - pos)) >
0) {
pos += wr;
handler->count_write += wr;
}
if(pos < size) {
if(net_errno(sn->csd) ==
EAGAIN) {
size_t remaining = size-pos;
if(remaining > handler->writebuf_alloc) {
handler->writebuf_alloc = size >
4096 ? size :
4096;
handler->writebuf = pool_realloc(sn->pool, handler->writebuf, handler->writebuf_alloc);
if(!handler->writebuf) {
handler->result =
REQ_ABORTED;
return 1;
}
}
memcpy(handler->writebuf, buf+pos, remaining);
handler->writebuf_size = remaining;
handler->writebuf_pos =
0;
}
else {
handler->result =
REQ_ABORTED;
log_ereport(
LOG_FAILURE,
"cgi pid %d %s: network error: %s",
(
int)handler->process.pid,
handler->path,
strerror(net_errno(sn->csd)));
}
return 1;
}
return 0;
}
int cgi_stdout_readevent(EventHandler *ev, Event *event) {
CGIHandler *handler = event->cookie;
if(handler->debug_finished) {
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p debug_finished: 1 cgi_stdout_readevent events: %d", handler->parser->rq, handler->events);
}
if(handler->cgi_eof || handler->result ==
REQ_ABORTED) {
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p readevent cgi_eof = TRUE result: %d", handler->parser->rq, handler->result);
handler->wait_read =
FALSE;
event->finish =
NULL;
return 0;
}
event->finish = cgi_event_finish;
handler->writeev->finish =
NULL;
CgiIOResult ret = cgi_read_output(handler, ev,
"readevent");
switch(ret) {
case CGI_IO_COMPLETE: {
break;
}
case CGI_IO_NEED_READ: {
return 1;
}
case CGI_IO_NEED_WRITE: {
if(handler->poll_out) {
return 1;
}
if(event_pollout(ev, handler->parser->sn->csd, handler->writeev)) {
handler->result =
REQ_ABORTED;
}
else {
handler->poll_out =
TRUE;
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p enable poll out", handler->parser->rq);
return 1;
}
}
case CGI_IO_ERROR: {
break;
}
}
handler->wait_read =
FALSE;
return 0;
}
int cgi_writeevent(EventHandler *ev, Event *event) {
CGIHandler *handler = event->cookie;
if(handler->cgi_eof || handler->result ==
REQ_ABORTED) {
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p writeevent cgi_eof = TRUE result: %d", handler->parser->rq, handler->result);
handler->poll_out =
FALSE;
event->finish =
NULL;
return 0;
}
event->finish = cgi_event_finish;
handler->readev->finish =
NULL;
CgiIOResult ret = cgi_read_output(handler, ev,
"writeevent");
switch(ret) {
case CGI_IO_COMPLETE: {
break;
}
case CGI_IO_NEED_READ: {
return 1;
}
case CGI_IO_NEED_WRITE: {
return 1;
}
case CGI_IO_ERROR: {
break;
}
}
handler->poll_out =
FALSE;
return 0;
}
CgiIOResult cgi_read_output(CGIHandler *handler, EventHandler *ev,
const char *debug_log) {
CGIResponseParser *parser = handler->parser;
Session *sn = parser->sn;
Request *rq = parser->rq;
if(handler->result ==
REQ_ABORTED) {
return CGI_IO_ERROR;
}
if(cgi_try_write_flush(handler, sn)) {
if(handler->result ==
REQ_ABORTED) {
return CGI_IO_ERROR;
}
else {
return CGI_IO_NEED_WRITE;
}
}
char buf[
4096];
ssize_t r;
int ret =
CGI_IO_COMPLETE;
handler->result =
REQ_PROCEED;
while((r = read(handler->process.out[
0], buf,
4096)) >
0) {
if(parser->cgiheader) {
size_t pos;
int ret = cgi_parse_response(parser, buf, r, &pos);
if(ret == -
1) {
log_ereport(
LOG_FAILURE,
"broken cgi script response: path: %s", handler->path);
protocol_status(sn, rq,
500,
NULL);
handler->result =
REQ_ABORTED;
return CGI_IO_ERROR;
}
else if(ret ==
1) {
WS_ASSERT(pos <= r);
parser->response_length += r-pos;
parser->cgiheader =
FALSE;
if(parser->status >
0) {
protocol_status(sn, rq, parser->status, parser->msg);
}
handler->response = http_create_response(sn, rq);
if(!handler->response) {
handler->result =
REQ_ABORTED;
return CGI_IO_ERROR;
}
int send_response = http_send_response(handler->response);
if(send_response <
0) {
handler->result =
REQ_ABORTED;
ret =
CGI_IO_ERROR;
break;
}
else if(send_response ==
1) {
if(!handler->poll_out) {
if(event_pollout(ev, sn->csd, handler->writeev)) {
handler->result =
REQ_ABORTED;
return CGI_IO_ERROR;
}
handler->poll_out =
TRUE;
return CGI_IO_NEED_WRITE;
}
}
else {
handler->response =
NULL;
}
if(pos < r) {
if(cgi_try_write(handler, ev, sn, &buf[pos], r-pos)) {
return handler->result ==
REQ_ABORTED ?
CGI_IO_ERROR :
CGI_IO_NEED_WRITE;
}
}
}
}
else {
parser->response_length += r;
if(cgi_try_write(handler, ev, sn, buf, r)) {
return handler->result ==
REQ_ABORTED ?
CGI_IO_ERROR :
CGI_IO_NEED_WRITE;
}
}
}
if(r <
0 && errno ==
EAGAIN) {
return CGI_IO_NEED_READ;
}
handler->cgi_eof =
TRUE;
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p pid: %d set cgi_eof : %s", rq, handler->process.pid, debug_log);
return ret;
}
int cgi_stderr_readevent(EventHandler *ev, Event *event) {
CGIHandler *handler = event->cookie;
pool_handle_t *pool = handler->parser->sn->pool;
char buf[
4096];
char *line = buf;
int line_start;
ssize_t r;
while((r = read(handler->process.err[
0], buf,
4096)) >
0) {
line_start =
0;
int pos =
0;
for(
int i=
0;i<r;i++) {
if(buf[i] ==
'\n') {
log_ereport(
LOG_INFORM,
"cgi pid %d %s stderr: %.*s%.*s",
(
int)handler->process.pid,
handler->path,
(
int)handler->stderr_tmplen,
handler->stderr_tmp,
i - line_start,
line + line_start);
line_start = i+
1;
pos = i+
1;
if(handler->stderr_tmp) {
handler->stderr_tmplen =
0;
}
}
}
if(pos < r) {
int tmplen = r-pos;
if(handler->stderr_tmplen >
0) {
if(handler->stderr_tmplen + tmplen > handler->stderr_tmpalloc) {
handler->stderr_tmpalloc = handler->stderr_tmplen + tmplen;
handler->stderr_tmp = pool_realloc(pool, handler->stderr_tmp, handler->stderr_tmpalloc);
if(!handler->stderr_tmp) {
log_ereport(
LOG_FAILURE,
"send-cgi: cannot create tmp buffer for parsing stderr");
handler->stderr_tmpalloc =
0;
handler->stderr_tmplen =
0;
continue;
}
}
memcpy(handler->stderr_tmp + handler->stderr_tmplen, line + line_start, tmplen);
handler->stderr_tmplen += tmplen;
}
else {
if(handler->stderr_tmpalloc < tmplen) {
handler->stderr_tmpalloc = tmplen <
256 ?
256 : tmplen;
if(handler->stderr_tmp) {
pool_free(pool, handler->stderr_tmp);
}
handler->stderr_tmp = pool_malloc(pool, handler->stderr_tmpalloc);
if(!handler->stderr_tmp) {
log_ereport(
LOG_FAILURE,
"send-cgi: cannot create tmp buffer for parsing stderr");
handler->stderr_tmpalloc =
0;
handler->stderr_tmplen =
0;
continue;
}
}
memcpy(handler->stderr_tmp, line + line_start, tmplen);
handler->stderr_tmplen = tmplen;
}
}
else {
handler->stderr_tmplen =
0;
}
}
if(r <
0 && errno ==
EAGAIN) {
return 1;
}
if(handler->stderr_tmp) {
pool_free(handler->parser->sn->pool, handler->stderr_tmp);
}
return 0;
}
int cgi_event_finish(EventHandler *ev, Event *event) {
CGIHandler *handler = event->cookie;
CGIResponseParser *parser = handler->parser;
Session *sn = parser->sn;
Request *rq = parser->rq;
char *event_fn =
"";
if(event->fn == cgi_stdout_readevent) {
event_fn =
"stdout";
}
else if(event->fn == cgi_stderr_readevent) {
event_fn =
"stderr";
}
else if(event->fn == cgi_writeevent) {
event_fn =
"httpout";
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p finish: pid: %d", rq, handler->process.pid);
}
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p finish: event: %d pollout: %d wait_read: %d cgi_eof: %d fn: %s", rq, handler->events, handler->poll_out, handler->wait_read, handler->cgi_eof, event_fn);
handler->debug_finished =
TRUE;
if(event->fn != cgi_stderr_readevent) {
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p finish set cgi_eof: %s", rq, event_fn);
handler->cgi_eof =
TRUE;
}
if(handler->result ==
REQ_ABORTED && handler->process.pid !=
0 && handler->cgi_kill ==
0) {
log_ereport(
LOG_FAILURE,
"cgi-send: kill script: %s pid: %d", handler->path, (
int)handler->process.pid);
if(kill(handler->process.pid,
SIGTERM)) {
log_ereport(
LOG_FAILURE,
"cgi-send: pid: %d kill failed: %s", (
int)handler->process.pid, strerror(errno));
}
else {
log_ereport(
LOG_DEBUG,
"cgi-send: finish: req: %p kill %d successful", rq, (
int)handler->process.pid);
handler->cgi_kill =
SIGTERM;
}
}
if(--handler->events >
0) {
return 0;
}
if(handler->poll_out) {
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p finish: remove-poll write", rq);
if(event_removepoll(ev, sn->csd)) {
log_ereport(
LOG_FAILURE,
"cgi_event_finish: event_removepoll: %s", strerror(errno));
}
handler->poll_out =
FALSE;
}
if(handler->wait_read) {
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p finish: remove-poll read", rq);
if(ev_remove_poll(ev, handler->process.out[
0])) {
log_ereport(
LOG_FAILURE,
"cgi_event_finish: req: %p ev_remove_poll: %s", rq, strerror(errno));
}
handler->wait_read =
FALSE;
}
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p cgi_close", rq);
int exit_code = cgi_close(&handler->process);
if(exit_code !=
0) {
log_ereport(
LOG_FAILURE,
"send-cgi: script: %s exited with code %d", handler->path, exit_code);
handler->result =
REQ_ABORTED;
}
cgi_parser_free(parser);
WSBool response_length_error =
FALSE;
char *ctlen_header = pblock_findkeyval(pb_key_content_length, rq->srvhdrs);
if(ctlen_header) {
int64_t ctlenhdr;
if(util_strtoint(ctlen_header, &ctlenhdr)) {
if(ctlenhdr != parser->response_length) {
log_ereport(
LOG_FAILURE,
"cgi-send: script: %s: content length mismatch",
handler->path);
response_length_error =
TRUE;
}
}
}
if(handler->result !=
REQ_ABORTED && handler->parser->response_length != handler->count_write) {
log_ereport(
LOG_FAILURE,
"cgi-send: script: %s: IO error: cgi response length != http response length",
handler->path);
response_length_error =
TRUE;
}
if(response_length_error) {
rq->rq_attr.keep_alive =
0;
handler->result =
REQ_ABORTED;
}
net_setnonblock(sn->csd,
0);
log_ereport(
LOG_DEBUG,
"cgi-send: req: %p event-finish nsapi return", rq);
nsapi_function_return(sn, rq, handler->result);
return 0;
}
int cgi_start(CGIProcess *p,
char *path,
char *
const argv[],
char *
const envp[]) {
if(pipe(p->in) || pipe(p->out) || pipe(p->err)) {
log_ereport(
LOG_FAILURE,
"send-cgi: cannot create pipe: %s",
strerror(errno));
return REQ_ABORTED;
}
p->pid = fork();
if(p->pid ==
0) {
cxstring script = cx_str(path);
cxmutstr parent;
int len = strlen(path);
for(
int i=len-
1;i>=
0;i--) {
if(path[i] ==
'/') {
script = cx_strn(path + i +
1, len - i);
parent = cx_strdup(cx_strn(path, i));
if(chdir(parent.ptr)) {
perror(
"cgi_start: chdir");
free(parent.ptr);
exit(-
1);
}
free(parent.ptr);
break;
}
}
if(dup2(p->in[
0],
STDIN_FILENO) == -
1) {
perror(
"cgi_start: dup2");
exit(
EXIT_FAILURE);
}
if(dup2(p->out[
1],
STDOUT_FILENO) == -
1) {
perror(
"cgi_start: dup2");
exit(
EXIT_FAILURE);
}
if(dup2(p->err[
1],
STDERR_FILENO) == -
1) {
perror(
"cgi_start: dup2");
exit(
EXIT_FAILURE);
}
system_close(p->in[
1]);
exit(execve(script.ptr, argv, envp));
}
else {
system_close(p->out[
1]);
system_close(p->err[
1]);
p->out[
1] = -
1;
p->err[
1] = -
1;
}
return REQ_PROCEED;
}
int cgi_close(CGIProcess *p) {
if(p->in[
0] != -
1) {
system_close(p->in[
0]);
}
if(p->in[
1] != -
1) {
system_close(p->in[
1]);
}
if(p->out[
0] != -
1) {
system_close(p->out[
0]);
}
if(p->out[
1] != -
1) {
system_close(p->out[
1]);
}
if(p->err[
0] != -
1) {
system_close(p->err[
0]);
}
if(p->err[
1] != -
1) {
system_close(p->err[
1]);
}
int status = -
1;
if(waitpid(p->pid, &status,
WNOHANG) ==
0) {
log_ereport(
LOG_DEBUG,
"cgi_close: waitpid returned 0: pid: %d", (
int)p->pid);
sleep(
1);
if(waitpid(p->pid, &status,
WNOHANG) ==
0) {
log_ereport(
LOG_DEBUG,
"cgi_close: waitpid returned 0 again: pid: %d", (
int)p->pid);
}
}
return status;
}
CGIResponseParser* cgi_parser_new(Session *sn, Request *rq) {
CGIResponseParser* parser = pool_malloc(sn->pool,
sizeof(CGIResponseParser));
parser->sn = sn;
parser->rq = rq;
parser->status =
0;
parser->msg =
NULL;
parser->response_length =
0;
parser->cgiheader =
TRUE;
cxBufferInit(&parser->tmp,
NULL,
64, pool_allocator(sn->pool),
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS);
return parser;
}
void cgi_parser_free(CGIResponseParser *parser) {
if(parser->tmp.space) {
cxBufferDestroy(&parser->tmp);
}
pool_free(parser->sn->pool, parser);
}
static int parse_lines(CGIResponseParser *parser,
char *buf,
size_t len,
int *pos) {
CxAllocator *a = pool_allocator(parser->sn->pool);
cxmutstr name;
cxmutstr value;
WSBool space =
TRUE;
int i;
int line_begin =
0;
int value_begin =
0;
for(i=
0;i<len;i++) {
char c = buf[i];
if(value_begin == line_begin && c ==
':') {
name = cx_mutstrn(buf + line_begin, i - line_begin);
value_begin = i +
1;
}
else if(c ==
'\n') {
if(value_begin == line_begin) {
if(space) {
*pos = i +
1;
return 2;
}
else {
return -
1;
}
}
value = cx_mutstrn(buf + value_begin, i - value_begin);
cx_strlower(name);
name = cx_strdup_a(a, cx_strtrim((cxstring){name.ptr, name.length}));
value = cx_strtrim_m(value);
if(name.length ==
0 || value.length ==
0) {
return -
1;
}
if(!cx_strcmp((cxstring){name.ptr, name.length}, (cxstring)
CX_STR(
"status"))) {
cxmutstr codestr = value;
int j;
for(j=
0;j<codestr.length;j++) {
if(!isdigit(codestr.ptr[j])) {
break;
}
if(j >
2) {
break;
}
}
codestr.ptr[j] =
'\0';
int64_t s =
0;
util_strtoint(codestr.ptr, &s);
parser->status = (
int)s;
cxmutstr msg = cx_strtrim_m(cx_strsubs_m(value, j +
1));
if(msg.length >
0) {
parser->msg = cx_strdup_pool(parser->sn->pool, msg).ptr;
}
}
else {
pblock_nvlinsert(
name.ptr,
name.length,
value.ptr,
value.length,
parser->rq->srvhdrs);
}
line_begin = i+
1;
value_begin = line_begin;
space =
TRUE;
}
else if(!isspace(c)) {
space =
FALSE;
}
}
if(i < len) {
*pos = i;
return 0;
}
return 1;
}
int cgi_parse_response(CGIResponseParser *parser,
char *buf,
size_t len,
size_t *bpos) {
*bpos =
0;
int pos =
0;
if(parser->tmp.pos >
0) {
WSBool nb =
FALSE;
for(pos=
0;pos<len;pos++) {
if(buf[pos] ==
'\n') {
nb =
TRUE;
break;
}
}
cxBufferWrite(buf,
1, pos, &parser->tmp);
if(nb) {
int npos;
int r = parse_lines(parser, parser->tmp.space, parser->tmp.pos, &npos);
switch(r) {
case -
1:
return -
1;
case 0:
return -
1;
case 1:
break;
case 2: {
*bpos = pos +
1;
return 1;
}
}
parser->tmp.pos =
0;
}
else {
if(parser->tmp.pos >
CGI_RESPONSE_MAX_LINE_LENGTH) {
return -
1;
}
}
}
int npos =
0;
int r = parse_lines(parser, buf + pos, len - pos, &npos);
switch(r) {
default:
return -
1;
case 0:
case 1: {
int newlen = len - npos;
if(npos >
0) {
cxBufferWrite(buf + npos,
1, newlen, &parser->tmp);
}
return 0;
}
case 2: {
*bpos = pos + npos;
return 1;
}
}
}