/*
 * $Id$
 *
 * Copyright (C) 2001-2003 Fhg Fokus
 *
 * This file is part of ser, a free SIP server.
 *
 * ser 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 2 of the License, or
 * (at your option) any later version
 *
 * For a license to use the ser software under conditions
 * other than those described here, or to purchase support for this
 * software, please contact iptel.org by e-mail at the following addresses:
 *    info@iptel.org
 *
 * ser 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef USE_TCP

#include <stdio.h>
#include <errno.h>
#include <string.h>

#include <sys/select.h>

#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <unistd.h>


#include "dprint.h"
#include "tcp_conn.h"
#include "pass_fd.h"
#include "globals.h"
#include "receive.h"


#define q_memchr memchr

/* reads next available bytes
 * return number of bytes read, 0 on EOF or -1 on error,
 * sets also r->error */
int tcp_read(struct tcp_req *r)
{
	int bytes_free, bytes_read;
	
	bytes_free=TCP_BUF_SIZE- (int)(r->pos - r->buf);
	
	if (bytes_free==0){
		fprintf(stderr, "buffer overrun, dropping\n");
		r->error=TCP_REQ_OVERRUN;
		return -1;
	}
again:
	bytes_read=read(r->fd, r->pos, bytes_free);

	if(bytes_read==-1){
		if (errno == EWOULDBLOCK || errno == EAGAIN){
			return 0; /* nothing has been read */
		}else if (errno == EINTR) goto again;
		else{
			fprintf(stderr, "error reading: %s\n", strerror(errno));
			r->error=TCP_READ_ERROR;
			return -1;
		}
	}
	
	r->pos+=bytes_read;
	return bytes_read;
}



/* reads all headers (until double crlf), & parses the content-length header
 * (WARNING: highly ineficient, tries to reuse receive_msg but will parse
 *  all the header names twice [once here & once in receive_msg]; a more
 *  speed eficient version will result in either major code duplication or
 *  major changes to the receive code - TODO)
 * returns number of bytes read & sets r->state & r->body
 * when either r->body!=0 or r->state==H_BODY =>
 * all headers have been read. It should be called in a while loop.
 * returns < 0 if error or 0 if EOF */
int tcp_read_headers(struct tcp_req *r)
{
	int bytes, remaining;
	char *p;
	
	#define crlf_default_skip_case \
					case '\n': \
						r->state=H_LF; \
						break; \
					default: \
						r->state=H_SKIP
	
	#define content_len_beg_case \
					case ' ': \
					case '\t': \
						if (!r->has_content_len) r->state=H_STARTWS; \
						else r->state=H_SKIP; \
							/* not interested if we already found one */ \
						break; \
					case 'C': \
					case 'c': \
						if(!r->has_content_len) r->state=H_CONT_LEN1; \
						else r->state=H_SKIP; \
						break 
						
	#define change_state(upper, lower, newstate)\
					switch(*p){ \
						case upper: \
						case lower: \
							r->state=(newstate); break; \
						crlf_default_skip_case; \
					}
	
	#define change_state_case(state0, upper, lower, newstate)\
					case state0: \
							  change_state(upper, lower, newstate); \
							  p++; \
							  break


	
	bytes=tcp_read(r);
	if (bytes<=0) return bytes;
	p=r->parsed;
	
	while(p<r->pos && r->error==TCP_REQ_OK){
		switch(r->state){
			case H_BODY: /* read the body*/
				remaining=r->pos-p;
				if (remaining>r->bytes_to_go) remaining=r->bytes_to_go;
				r->bytes_to_go-=remaining;
				p+=remaining;
				if (r->bytes_to_go==0){
					r->complete=1;
					goto skip;
				}
				break;
				
			case H_SKIP:
				/* find lf, we are in this state if we are not interested
				 * in anything till end of line*/
				p=q_memchr(p, '\n', r->pos-r->parsed);
				if (p){
					p++;
					r->state=H_LF;
				}else{
					p=r->pos;
				}
				break;
				
			case H_LF:
				/* terminate on LF CR LF or LF LF */
				switch (*p){
					case '\r':
						r->state=H_LFCR;
						break;
					case '\n':
						/* found LF LF */
						r->state=H_BODY;
						if (r->has_content_len){
							r->body=p+1;
							r->bytes_to_go=r->content_len;
							if (r->bytes_to_go==0){
								r->complete=1;
								goto skip;
							}
						}else{
							r->error=TCP_REQ_BAD_LEN;
						}
						break;
					content_len_beg_case;
					default: 
						r->state=H_SKIP;
				}
				p++;
				break;
			case H_LFCR:
				if (*p=='\n'){
					/* found LF CR LF */
					r->state=H_BODY;
					if (r->has_content_len){
						r->body=p+1;
						r->bytes_to_go=r->content_len;
						if (r->bytes_to_go==0){
							r->complete=1;
							goto skip;
						}
					}else{
						r->error=TCP_REQ_BAD_LEN;
					}
				}else r->state=H_SKIP;
				p++;
				break;
				
			case H_STARTWS:
				switch (*p){
					content_len_beg_case;
					crlf_default_skip_case;
				}
				p++;
				break;
			
			change_state_case(H_CONT_LEN1,  'O', 'o', H_CONT_LEN2);
			change_state_case(H_CONT_LEN2,  'N', 'n', H_CONT_LEN3);
			change_state_case(H_CONT_LEN3,  'T', 't', H_CONT_LEN4);
			change_state_case(H_CONT_LEN4,  'E', 'e', H_CONT_LEN5);
			change_state_case(H_CONT_LEN5,  'N', 'n', H_CONT_LEN6);
			change_state_case(H_CONT_LEN6,  'T', 't', H_CONT_LEN7);
			change_state_case(H_CONT_LEN7,  '-', '_', H_CONT_LEN8);
			change_state_case(H_CONT_LEN8,  'L', 'l', H_CONT_LEN9);
			change_state_case(H_CONT_LEN9,  'E', 'e', H_CONT_LEN10);
			change_state_case(H_CONT_LEN10, 'N', 'n', H_CONT_LEN11);
			change_state_case(H_CONT_LEN11, 'G', 'g', H_CONT_LEN12);
			change_state_case(H_CONT_LEN12, 'T', 't', H_CONT_LEN13);
			change_state_case(H_CONT_LEN13, 'H', 'h', H_L_COLON);
			
			case H_L_COLON:
				switch(*p){
					case ' ':
					case '\t':
						break; /* skip space */
					case ':':
						r->state=H_CONT_LEN_BODY;
						break;
					crlf_default_skip_case;
				};
				p++;
				break;
			
			case  H_CONT_LEN_BODY:
				switch(*p){
					case ' ':
					case '\t':
						break; /* eat space */
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9':
						r->state=H_CONT_LEN_BODY_PARSE;
						r->content_len=(*p-'0');
						break;
					/*FIXME: content lenght on different lines ! */
					crlf_default_skip_case;
				}
				p++;
				break;
				
			case H_CONT_LEN_BODY_PARSE:
				switch(*p){
					case '0':
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
						r->content_len=r->content_len*10+(*p-'0');
						break;
					case '\r':
					case ' ':
					case '\t': /* FIXME: check if line contains only WS */
						r->state=H_SKIP;
						r->has_content_len=1;
						break;
					case '\n':
						/* end of line, parse succesfull */
						r->state=H_LF;
						r->has_content_len=1;
						break;
					default:
						LOG(L_ERR, "ERROR: tcp_read_headers: bad "
								"Content-Length header value, unexpected "
								"char %c in state %d\n", *p, r->state);
						r->state=H_SKIP; /* try to find another?*/
				}
				p++;
				break;
			
			default:
				fprintf(stderr, "BUG: unexpected state %d\n", r->state);
				abort();
		}
	}
skip:
	r->parsed=p;
	return bytes;
}




void tcp_receive_loop(int unix_sock)
{
	struct tcp_req req;
	int bytes;
	long size;
	int n;
	long id;
	int s;
	long state;
	long response[2];
	
	
	
	
	/* listen on the unix socket for the fd */
	for(;;){
			n=receive_fd(unix_sock, &id, sizeof(id), &s);
			if (n<0){
				if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR){
					continue;
				}else{
					fprintf(stderr, "ERROR: tcp_receive_loop: read_fd: %s\n",
							strerror(errno));
					abort(); /* big error*/
				}
			}
			if (n==0){
					fprintf(stderr, 
							"WARNING: tcp_receive_loop: 0 bytes read\n");
					continue;
			}
			fprintf(stderr, "received n=%d id=%ld, fd=%d\n", n, id, s);
			if (s==-1) {
					fprintf(stderr, "ERROR: tcp_receive_loop: read_fd:"
									"no fd read\n");
					state=-1;
					goto end_req; /* ?*/
			}
		
		init_tcp_req(&req, s);
		
		
	again:
		while(req.complete==0 && req.error==TCP_REQ_OK){
			bytes=tcp_read_headers(&req);
			/* if timeout state=0; goto end__req; */
			fprintf(stderr, "read= %d bytes, parsed=%d, state=%d, error=%d\n",
					bytes, req.parsed-req.buf, req.state, req.error );
			if (bytes==-1){
				fprintf(stderr, "ERROR!\n");
				state=-1;
				goto end_req;
			}
			if (bytes==0){
				fprintf(stderr, "EOF!\n");
				state=-1;
				goto end_req;
			}

		}
		if (req.error!=TCP_REQ_OK){
			fprintf(stderr, "bad request, state=%d, error=%d\n",
					req.state, req.error);
			state=-1;
			goto end_req;
		}
		fprintf(stderr, "end of header part\n");
		fprintf(stderr, "headers:\n%.*s.\n",req.body-req.buf, req.buf);
		if (req.has_content_len){
			fprintf(stderr, "content-length= %d\n", req.content_len);
			fprintf(stderr, "body:\n%.*s\n", req.content_len, req.body);
		}else{
			req.error=TCP_REQ_BAD_LEN;
			fprintf(stderr, "content length not present or unparsable\n");
			state=-1;
			goto end_req;
		}

		/* if we are here everything is nice and ok*/
		state=0;
		/* just for debugging use sendipv4 as receiving socket */
		DBG("calling receive_msg(%p, %d, %p)\n",
				req.buf, (int)(req.parsed-req.buf), &sendipv4->su);
		bind_address=sendipv4;
		receive_msg(req.buf, req.parsed-req.buf, &sendipv4->su);

		/* prepare for next request */
		size=req.pos-req.body;
		if (size) memmove(req.buf, req.body, size);
		fprintf(stderr, "\npreparing for new request, kept %ld bytes\n", size);
		req.pos=req.buf+size;
		req.parsed=req.buf;
		req.body=0;
		req.error=TCP_REQ_OK;
		req.state=H_STARTWS;
		req.complete=req.content_len=req.has_content_len=0;
		req.bytes_to_go=0;
	
		/* process last req. */
		
		goto again;
		
	end_req:
			fprintf(stderr, "end req\n");
		/* release req & signal the parent */
		if (s!=-1) close(s);
		/* errno==EINTR, EWOULDBLOCK a.s.o todo */
		response[0]=id;
		response[1]=state;
		write(unix_sock, response, sizeof(response));
		
	
	}
}


#if 0
int main(int argv, char** argc )
{
	printf("starting tests\n");
	tcp_receive_loop();
}

#endif

#endif