746 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			746 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/*  Part of SWI-Prolog
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Author:        Jan Wielemaker
							 | 
						||
| 
								 | 
							
								    E-mail:        J.Wielemaker@uva.nl
							 | 
						||
| 
								 | 
							
								    WWW:           http://www.swi-prolog.org
							 | 
						||
| 
								 | 
							
								    Copyright (C): 2009, University of Amsterdam
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This library is free software; you can redistribute it and/or
							 | 
						||
| 
								 | 
							
								    modify it under the terms of the GNU Lesser General Public
							 | 
						||
| 
								 | 
							
								    License as published by the Free Software Foundation; either
							 | 
						||
| 
								 | 
							
								    version 2.1 of the License, or (at your option) any later version.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This library 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
							 | 
						||
| 
								 | 
							
								    Lesser General Public License for more details.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    You should have received a copy of the GNU Lesser General Public
							 | 
						||
| 
								 | 
							
								    License along with this library; if not, write to the Free Software
							 | 
						||
| 
								 | 
							
								    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <SWI-Stream.h>
							 | 
						||
| 
								 | 
							
								#include <SWI-Prolog.h>
							 | 
						||
| 
								 | 
							
								#include <stdlib.h>
							 | 
						||
| 
								 | 
							
								#include <string.h>
							 | 
						||
| 
								 | 
							
								#include <assert.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
							 | 
						||
| 
								 | 
							
								The task of cgi_stream.c is to interface between the actual wrapper code
							 | 
						||
| 
								 | 
							
								that implements an HTTP location and the   socket sending a reply to the
							 | 
						||
| 
								 | 
							
								client.  In particular, we want to deal with:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    * Separating the header from the body of the reply
							 | 
						||
| 
								 | 
							
								    * Chunked or traditional transfer encoding
							 | 
						||
| 
								 | 
							
								    * Connection management (Keep-alife)
							 | 
						||
| 
								 | 
							
								    * Thread management
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The original HTTP infrastructure has an `accept thread' that accepts new
							 | 
						||
| 
								 | 
							
								connections. The connection is handed to a   thread  that reads the HTTP
							 | 
						||
| 
								 | 
							
								header and calls a handler with the  output redirected to a memory file,
							 | 
						||
| 
								 | 
							
								processing the reply-header and reply-data   after the handler finished.
							 | 
						||
| 
								 | 
							
								This is a clean and modular design,   but it cannot deal with especially
							 | 
						||
| 
								 | 
							
								chunked encoding and  thread  management.   This  module  remedies these
							 | 
						||
| 
								 | 
							
								issues.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								To do this, the  stream  provides   a  three  call-backs. Initially, the
							 | 
						||
| 
								 | 
							
								stream is in line-buffering mode (SIO_LBUF),   waiting for the header to
							 | 
						||
| 
								 | 
							
								become complete. At that moment it calls   the  hook, passing event type
							 | 
						||
| 
								 | 
							
								'header' and the stream. This processes the   head and combines the head
							 | 
						||
| 
								 | 
							
								with the request, deciding on:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    * The final header
							 | 
						||
| 
								 | 
							
								    * The transfer encoding (chunked/none)
							 | 
						||
| 
								 | 
							
								    * The content encoding (octet/utf8)
							 | 
						||
| 
								 | 
							
								    * The connection (Keep-Alife/close)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Now, the stream is placed in  full   buffering  mode  (SIO_FBUF). If the
							 | 
						||
| 
								 | 
							
								transfer encoding is 'chunked'  it  immediately   calls  the  hook using
							 | 
						||
| 
								 | 
							
								'send_header' to emit the current header.   Output continues. In chunked
							 | 
						||
| 
								 | 
							
								mode sending the chunks, otherwisse collecting   the  data. On close, it
							 | 
						||
| 
								 | 
							
								writes an empty block (chunked mode)  or   (normal  mode) calls the hook
							 | 
						||
| 
								 | 
							
								'send_header' which now has access to   the  content-length, followed by
							 | 
						||
| 
								 | 
							
								the data.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Note that the work-flow is kept with the stream. This allows passing the
							 | 
						||
| 
								 | 
							
								cgi stream from thread to thread while keeping track of the work-flow.
							 | 
						||
| 
								 | 
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
							 | 
						||
| 
								 | 
							
								TODO
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									* Error handling (many places)
							 | 
						||
| 
								 | 
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	      CONSTANTS		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_header;		/* header */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_header_codes;	/* header_codes */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_send_header;		/* send_header */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_data;		/* data */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_discarded;		/* discarded */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_request;		/* request */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_client;		/* client */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_chunked;		/* chunked */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_none;		/* none */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_state;		/* state */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_transfer_encoding;	/* transfer_encoding */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_connection;		/* connection */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_keep_alife;		/* keep_alife */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_close;		/* close */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_content_length;	/* content_length */
							 | 
						||
| 
								 | 
							
								static atom_t ATOM_id;			/* id */
							 | 
						||
| 
								 | 
							
								static predicate_t PREDICATE_call3;	/* Goal, Event, Handle */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	      CONTEXT		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define BUFSIZE SIO_BUFSIZE		/* raw I/O buffer */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef enum
							 | 
						||
| 
								 | 
							
								{ CGI_HDR  = 0,
							 | 
						||
| 
								 | 
							
								  CGI_DATA,
							 | 
						||
| 
								 | 
							
								  CGI_DISCARDED
							 | 
						||
| 
								 | 
							
								} cgi_state;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define CGI_MAGIC 0xa85ce042
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								typedef struct cgi_context
							 | 
						||
| 
								 | 
							
								{ IOSTREAM	   *stream;		/* Original stream */
							 | 
						||
| 
								 | 
							
								  IOSTREAM	   *cgi_stream;		/* Stream I'm handle of */
							 | 
						||
| 
								 | 
							
								  IOENC		    parent_encoding;	/* Saved encoding of parent */
							 | 
						||
| 
								 | 
							
													/* Prolog attributes */
							 | 
						||
| 
								 | 
							
								  module_t	    module;		/* Calling module */
							 | 
						||
| 
								 | 
							
								  record_t	    hook;		/* Hook called on action */
							 | 
						||
| 
								 | 
							
								  record_t	    request;		/* Associated request term */
							 | 
						||
| 
								 | 
							
								  record_t	    header;		/* Associated reply header term */
							 | 
						||
| 
								 | 
							
								  atom_t	    transfer_encoding;	/* Current transfer encoding */
							 | 
						||
| 
								 | 
							
								  atom_t	    connection;		/* Keep alife? */
							 | 
						||
| 
								 | 
							
													/* state */
							 | 
						||
| 
								 | 
							
								  cgi_state	    state;		/* Current state */
							 | 
						||
| 
								 | 
							
													/* data buffering */
							 | 
						||
| 
								 | 
							
								  size_t	    data_offset;	/* Start of real data */
							 | 
						||
| 
								 | 
							
								  char		   *data;		/* Buffered data */
							 | 
						||
| 
								 | 
							
								  size_t	    datasize;		/* #bytes buffered */
							 | 
						||
| 
								 | 
							
								  size_t	    dataallocated;	/* #bytes allocated */
							 | 
						||
| 
								 | 
							
								  int		    id;			/* Identifier */
							 | 
						||
| 
								 | 
							
								  unsigned int	    magic;		/* CGI_MAGIC */
							 | 
						||
| 
								 | 
							
								} cgi_context;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int start_chunked_encoding(cgi_context *ctx);
							 | 
						||
| 
								 | 
							
								static ssize_t cgi_chunked_write(cgi_context *ctx, char *buf, size_t size);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	     ALLOC/FREE		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static cgi_context*
							 | 
						||
| 
								 | 
							
								alloc_cgi_context(IOSTREAM *s)
							 | 
						||
| 
								 | 
							
								{ cgi_context *ctx = PL_malloc(sizeof(*ctx));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  memset(ctx, 0, sizeof(*ctx));
							 | 
						||
| 
								 | 
							
								  ctx->magic  = CGI_MAGIC;
							 | 
						||
| 
								 | 
							
								  ctx->stream = s;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return ctx;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								free_cgi_context(cgi_context *ctx)
							 | 
						||
| 
								 | 
							
								{ if ( ctx->stream->upstream )
							 | 
						||
| 
								 | 
							
								    Sset_filter(ctx->stream, NULL);
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    PL_release_stream(ctx->stream);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( ctx->data )       free(ctx->data);
							 | 
						||
| 
								 | 
							
								  if ( ctx->hook )       PL_erase(ctx->hook);
							 | 
						||
| 
								 | 
							
								  if ( ctx->request )    PL_erase(ctx->request);
							 | 
						||
| 
								 | 
							
								  if ( ctx->header )     PL_erase(ctx->header);
							 | 
						||
| 
								 | 
							
								  if ( ctx->connection ) PL_unregister_atom(ctx->connection);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ctx->magic = 0;
							 | 
						||
| 
								 | 
							
								  PL_free(ctx);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int
							 | 
						||
| 
								 | 
							
								grow_data_buffer(cgi_context *ctx, size_t size)
							 | 
						||
| 
								 | 
							
								{ size_t newsize;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( ctx->dataallocated == 0 )
							 | 
						||
| 
								 | 
							
								    newsize = SIO_BUFSIZE;
							 | 
						||
| 
								 | 
							
								  else
							 | 
						||
| 
								 | 
							
								    newsize = ctx->dataallocated;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while(newsize < size)
							 | 
						||
| 
								 | 
							
								    newsize *= 2;
							 | 
						||
| 
								 | 
							
								  if ( ctx->data )
							 | 
						||
| 
								 | 
							
								  { void *p;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ( !(p=realloc(ctx->data, newsize)) )
							 | 
						||
| 
								 | 
							
								      return -1;
							 | 
						||
| 
								 | 
							
								    ctx->data = p;
							 | 
						||
| 
								 | 
							
								    ctx->dataallocated = newsize;
							 | 
						||
| 
								 | 
							
								  } else
							 | 
						||
| 
								 | 
							
								  { if ( !(ctx->data = malloc(newsize)) )
							 | 
						||
| 
								 | 
							
								      return -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    ctx->dataallocated = newsize;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	     PROPERTIES		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static IOFUNCTIONS cgi_functions;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int
							 | 
						||
| 
								 | 
							
								get_cgi_stream(term_t t, IOSTREAM **sp, cgi_context **ctx)
							 | 
						||
| 
								 | 
							
								{ IOSTREAM *s;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !PL_get_stream_handle(t, &s) )
							 | 
						||
| 
								 | 
							
								    return FALSE;
							 | 
						||
| 
								 | 
							
								  if ( s->functions != &cgi_functions )
							 | 
						||
| 
								 | 
							
								  { PL_release_stream(s);
							 | 
						||
| 
								 | 
							
								    return type_error(t, "cgi_stream");
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  *sp = s;
							 | 
						||
| 
								 | 
							
								  *ctx = s->handle;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return TRUE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int
							 | 
						||
| 
								 | 
							
								unify_record(term_t t, record_t r)
							 | 
						||
| 
								 | 
							
								{ if ( r )
							 | 
						||
| 
								 | 
							
								  { term_t t2 = PL_new_term_ref();
							 | 
						||
| 
								 | 
							
								    PL_recorded(r, t2);
							 | 
						||
| 
								 | 
							
								    return PL_unify(t, t2);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return FALSE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static foreign_t
							 | 
						||
| 
								 | 
							
								is_cgi_stream(term_t cgi)
							 | 
						||
| 
								 | 
							
								{ IOSTREAM *s;
							 | 
						||
| 
								 | 
							
								  int rc;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !PL_get_stream_handle(cgi, &s) )
							 | 
						||
| 
								 | 
							
								    return FALSE;
							 | 
						||
| 
								 | 
							
								  rc = (s->functions == &cgi_functions);
							 | 
						||
| 
								 | 
							
								  PL_release_stream(s);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return rc;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static foreign_t
							 | 
						||
| 
								 | 
							
								cgi_property(term_t cgi, term_t prop)
							 | 
						||
| 
								 | 
							
								{ IOSTREAM *s;
							 | 
						||
| 
								 | 
							
								  cgi_context *ctx;
							 | 
						||
| 
								 | 
							
								  term_t arg = PL_new_term_ref();
							 | 
						||
| 
								 | 
							
								  atom_t name;
							 | 
						||
| 
								 | 
							
								  int arity;
							 | 
						||
| 
								 | 
							
								  int rc = TRUE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !get_cgi_stream(cgi, &s, &ctx) )
							 | 
						||
| 
								 | 
							
								    return FALSE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !PL_get_name_arity(prop, &name, &arity) || arity != 1 )
							 | 
						||
| 
								 | 
							
								  { rc = type_error(prop, "cgi_property");
							 | 
						||
| 
								 | 
							
								    goto out;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  _PL_get_arg(1, prop, arg);
							 | 
						||
| 
								 | 
							
								  if ( name == ATOM_request )
							 | 
						||
| 
								 | 
							
								  { if ( ctx->request )
							 | 
						||
| 
								 | 
							
								      rc = unify_record(arg, ctx->request);
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								      rc = PL_unify_nil(arg);
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_header )
							 | 
						||
| 
								 | 
							
								  { if ( ctx->header )
							 | 
						||
| 
								 | 
							
								      rc = unify_record(arg, ctx->header);
							 | 
						||
| 
								 | 
							
								     else
							 | 
						||
| 
								 | 
							
								      rc = PL_unify_nil(arg);
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_id )
							 | 
						||
| 
								 | 
							
								  { rc = PL_unify_integer(arg, ctx->id);
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_client )
							 | 
						||
| 
								 | 
							
								  { rc = PL_unify_stream(arg, ctx->stream);
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_transfer_encoding )
							 | 
						||
| 
								 | 
							
								  { rc = PL_unify_atom(arg, ctx->transfer_encoding);
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_connection )
							 | 
						||
| 
								 | 
							
								  { rc = PL_unify_atom(arg, ctx->connection ? ctx->connection : ATOM_close);
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_content_length )
							 | 
						||
| 
								 | 
							
								  { rc = PL_unify_int64(arg, ctx->datasize - ctx->data_offset);
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_header_codes )
							 | 
						||
| 
								 | 
							
								  { if ( ctx->data_offset > 0 )
							 | 
						||
| 
								 | 
							
								      rc = PL_unify_chars(arg, PL_CODE_LIST, ctx->data_offset, ctx->data);
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								      rc = existence_error(cgi, "header");
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_state )
							 | 
						||
| 
								 | 
							
								  { atom_t state;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    switch(ctx->state)
							 | 
						||
| 
								 | 
							
								    { case CGI_HDR:       state = ATOM_header; break;
							 | 
						||
| 
								 | 
							
								      case CGI_DATA:      state = ATOM_data; break;
							 | 
						||
| 
								 | 
							
								      case CGI_DISCARDED: state = ATOM_discarded; break;
							 | 
						||
| 
								 | 
							
								      default:
							 | 
						||
| 
								 | 
							
									assert(0);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    rc = PL_unify_atom(arg, state);
							 | 
						||
| 
								 | 
							
								  } else
							 | 
						||
| 
								 | 
							
								  { rc = existence_error(prop, "cgi_property");
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								out:
							 | 
						||
| 
								 | 
							
								  PL_release_stream(s);
							 | 
						||
| 
								 | 
							
								  return rc;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int
							 | 
						||
| 
								 | 
							
								set_term(record_t *r, term_t t)
							 | 
						||
| 
								 | 
							
								{ if ( *r )
							 | 
						||
| 
								 | 
							
								    PL_erase(*r);
							 | 
						||
| 
								 | 
							
								  *r = PL_record(t);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return TRUE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int
							 | 
						||
| 
								 | 
							
								set_atom(atom_t *a, term_t t)
							 | 
						||
| 
								 | 
							
								{ atom_t new;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !PL_get_atom(t, &new) )
							 | 
						||
| 
								 | 
							
								    return type_error(t, "atom");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( *a != new )
							 | 
						||
| 
								 | 
							
								  { if ( *a )
							 | 
						||
| 
								 | 
							
								      PL_unregister_atom(*a);
							 | 
						||
| 
								 | 
							
								    *a = new;
							 | 
						||
| 
								 | 
							
								    PL_register_atom(new);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return TRUE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static foreign_t
							 | 
						||
| 
								 | 
							
								cgi_set(term_t cgi, term_t prop)
							 | 
						||
| 
								 | 
							
								{ IOSTREAM *s;
							 | 
						||
| 
								 | 
							
								  cgi_context *ctx;
							 | 
						||
| 
								 | 
							
								  term_t arg = PL_new_term_ref();
							 | 
						||
| 
								 | 
							
								  atom_t name;
							 | 
						||
| 
								 | 
							
								  int arity;
							 | 
						||
| 
								 | 
							
								  int rc = TRUE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !get_cgi_stream(cgi, &s, &ctx) )
							 | 
						||
| 
								 | 
							
								    return FALSE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !PL_get_name_arity(prop, &name, &arity) || arity != 1 )
							 | 
						||
| 
								 | 
							
								  { rc = type_error(prop, "cgi_property");
							 | 
						||
| 
								 | 
							
								    goto out;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  _PL_get_arg(1, prop, arg);
							 | 
						||
| 
								 | 
							
								  if ( name == ATOM_request )
							 | 
						||
| 
								 | 
							
								  { rc = set_term(&ctx->request, arg);
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_header )
							 | 
						||
| 
								 | 
							
								  { rc = set_term(&ctx->header, arg);
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_connection )
							 | 
						||
| 
								 | 
							
								  { rc = set_atom(&ctx->connection, arg);
							 | 
						||
| 
								 | 
							
								  } else if ( name == ATOM_transfer_encoding )
							 | 
						||
| 
								 | 
							
								  { atom_t enc;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ( !PL_get_atom(arg, &enc) )
							 | 
						||
| 
								 | 
							
								      return type_error(arg, "atom");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ( ctx->transfer_encoding != enc )
							 | 
						||
| 
								 | 
							
								    { if ( enc == ATOM_chunked )
							 | 
						||
| 
								 | 
							
								      { ctx->transfer_encoding = enc;
							 | 
						||
| 
								 | 
							
									rc = start_chunked_encoding(ctx);
							 | 
						||
| 
								 | 
							
								      } else
							 | 
						||
| 
								 | 
							
								      { rc = domain_error(arg, "transfer_encoding");
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else
							 | 
						||
| 
								 | 
							
								  { rc = existence_error(prop, "cgi_property");
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								out:
							 | 
						||
| 
								 | 
							
								  PL_release_stream(s);
							 | 
						||
| 
								 | 
							
								  return rc;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static foreign_t
							 | 
						||
| 
								 | 
							
								cgi_discard(term_t cgi)
							 | 
						||
| 
								 | 
							
								{ IOSTREAM *s;
							 | 
						||
| 
								 | 
							
								  cgi_context *ctx;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !get_cgi_stream(cgi, &s, &ctx) )
							 | 
						||
| 
								 | 
							
								    return FALSE;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ctx->state = CGI_DISCARDED;
							 | 
						||
| 
								 | 
							
													/* empty buffer to avoid write */
							 | 
						||
| 
								 | 
							
								  ctx->cgi_stream->bufp = ctx->cgi_stream->buffer;
							 | 
						||
| 
								 | 
							
								  PL_release_stream(s);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return TRUE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	      HOOKS		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
							 | 
						||
| 
								 | 
							
								Call hook on the data we collected sofar.   The  hook is called with the
							 | 
						||
| 
								 | 
							
								following additional arguments:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    * Event-type (header, data)
							 | 
						||
| 
								 | 
							
								    * An input stream pointing to the collected data
							 | 
						||
| 
								 | 
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int
							 | 
						||
| 
								 | 
							
								call_hook(cgi_context *ctx, atom_t event)
							 | 
						||
| 
								 | 
							
								{ fid_t fid = PL_open_foreign_frame();
							 | 
						||
| 
								 | 
							
								  term_t av = PL_new_term_refs(3);
							 | 
						||
| 
								 | 
							
								  qid_t qid;
							 | 
						||
| 
								 | 
							
								  int rc;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  PL_recorded(ctx->hook, av+0);
							 | 
						||
| 
								 | 
							
								  PL_put_atom(av+1, event);
							 | 
						||
| 
								 | 
							
								  PL_unify_stream(av+2, ctx->cgi_stream);
							 | 
						||
| 
								 | 
							
								  qid = PL_open_query(ctx->module, PL_Q_CATCH_EXCEPTION, PREDICATE_call3, av);
							 | 
						||
| 
								 | 
							
								  rc = PL_next_solution(qid);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !rc )
							 | 
						||
| 
								 | 
							
								  { term_t ex;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ( (ex = PL_exception(qid)) )
							 | 
						||
| 
								 | 
							
								    { Sset_exception(ctx->cgi_stream, ex);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    } else
							 | 
						||
| 
								 | 
							
								    { char buf[256];
							 | 
						||
| 
								 | 
							
								      Ssprintf(buf, "CGI Hook %s failed", PL_atom_chars(event));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Sseterr(ctx->cgi_stream, SIO_WARN, buf);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    PL_cut_query(qid);
							 | 
						||
| 
								 | 
							
								    PL_close_foreign_frame(fid);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return FALSE;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  PL_close_query(qid);
							 | 
						||
| 
								 | 
							
								  PL_discard_foreign_frame(fid);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return TRUE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int
							 | 
						||
| 
								 | 
							
								start_chunked_encoding(cgi_context *ctx)
							 | 
						||
| 
								 | 
							
								{ if ( call_hook(ctx, ATOM_send_header) )
							 | 
						||
| 
								 | 
							
								  { if ( ctx->datasize > ctx->data_offset )
							 | 
						||
| 
								 | 
							
								    { int rc = cgi_chunked_write(ctx,
							 | 
						||
| 
								 | 
							
												 &ctx->data[ctx->data_offset],
							 | 
						||
| 
								 | 
							
												 ctx->datasize - ctx->data_offset);
							 | 
						||
| 
								 | 
							
								      if ( rc == -1 )
							 | 
						||
| 
								 | 
							
									return FALSE;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return TRUE;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return FALSE;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static size_t
							 | 
						||
| 
								 | 
							
								find_data(cgi_context *ctx, size_t start)
							 | 
						||
| 
								 | 
							
								{ const char *s = &ctx->data[start];
							 | 
						||
| 
								 | 
							
								  const char *e = &ctx->data[ctx->datasize-2];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for(; s<=e; s++)
							 | 
						||
| 
								 | 
							
								  { if ( s[0] == '\r' && s[1] == '\n' &&
							 | 
						||
| 
								 | 
							
									 s <= e-2 &&
							 | 
						||
| 
								 | 
							
									 s[2] == '\r' && s[3] == '\n' )
							 | 
						||
| 
								 | 
							
								      return &s[4] - ctx->data;
							 | 
						||
| 
								 | 
							
								    if ( s[0] == '\n' && s[1] == '\n' )
							 | 
						||
| 
								 | 
							
								      return &s[2] - ctx->data;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return (size_t)-1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	   IO FUNCTIONS		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static ssize_t				/* encode */
							 | 
						||
| 
								 | 
							
								cgi_chunked_write(cgi_context *ctx, char *buf, size_t size)
							 | 
						||
| 
								 | 
							
								{ if ( Sfprintf(ctx->stream, "%x\r\n", size) < 0 )
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								  if ( size > 0 &&
							 | 
						||
| 
								 | 
							
								       Sfwrite(buf, sizeof(char), size, ctx->stream) != size )
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								  if ( Sfprintf(ctx->stream, "\r\n") < 0 )
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								  if ( Sflush(ctx->stream) < 0 )
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return size;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static ssize_t
							 | 
						||
| 
								 | 
							
								cgi_write(void *handle, char *buf, size_t size)
							 | 
						||
| 
								 | 
							
								{ cgi_context *ctx = handle;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  DEBUG(1, Sdprintf("cgi_write(%ld bytes)\n", (long)size));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( ctx->state == CGI_DISCARDED )
							 | 
						||
| 
								 | 
							
								  { Sseterr(ctx->cgi_stream, SIO_FERR, "CGI stream was discarded");
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( ctx->transfer_encoding == ATOM_chunked )
							 | 
						||
| 
								 | 
							
								  { return cgi_chunked_write(ctx, buf, size);
							 | 
						||
| 
								 | 
							
								  } else
							 | 
						||
| 
								 | 
							
								  { size_t osize = ctx->datasize;
							 | 
						||
| 
								 | 
							
								    size_t dstart;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ( osize+size > ctx->dataallocated )
							 | 
						||
| 
								 | 
							
								    { if ( grow_data_buffer(ctx, osize+size) < 0 )
							 | 
						||
| 
								 | 
							
									return -1;			/* no memory */
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    memcpy(&ctx->data[osize], buf, size);
							 | 
						||
| 
								 | 
							
								    ctx->datasize = osize+size;
							 | 
						||
| 
								 | 
							
								    osize = (osize > 4 ? osize-4 : 0);	/* 4 is max size of the separator */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ( ctx->state == CGI_HDR &&
							 | 
						||
| 
								 | 
							
									 (dstart=find_data(ctx, osize)) != ((size_t)-1) )
							 | 
						||
| 
								 | 
							
								    { assert(dstart <= ctx->datasize);
							 | 
						||
| 
								 | 
							
								      ctx->data_offset = dstart;
							 | 
						||
| 
								 | 
							
								      ctx->state = CGI_DATA;
							 | 
						||
| 
								 | 
							
								      if ( !call_hook(ctx, ATOM_header) )
							 | 
						||
| 
								 | 
							
								      { ctx->state = CGI_DISCARDED;
							 | 
						||
| 
								 | 
							
									return -1;			/* TBD: pass error kindly */
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      ctx->cgi_stream->flags &= ~(SIO_FBUF|SIO_LBUF|SIO_NBUF);
							 | 
						||
| 
								 | 
							
								      ctx->cgi_stream->flags |= SIO_FBUF;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return size;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int
							 | 
						||
| 
								 | 
							
								cgi_control(void *handle, int op, void *data)
							 | 
						||
| 
								 | 
							
								{ cgi_context *ctx = handle;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( ctx->magic != CGI_MAGIC )
							 | 
						||
| 
								 | 
							
								  { DEBUG(0, Sdprintf("OOPS: cgi_control(%d): invalid handle\n", op));
							 | 
						||
| 
								 | 
							
								    errno = EINVAL;
							 | 
						||
| 
								 | 
							
								    return -1;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  switch(op)
							 | 
						||
| 
								 | 
							
								  { case SIO_FLUSHOUTPUT:
							 | 
						||
| 
								 | 
							
								    case SIO_SETENCODING:
							 | 
						||
| 
								 | 
							
								      return 0;				/* allow switching encoding */
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      if ( ctx->stream->functions->control )
							 | 
						||
| 
								 | 
							
									return (*ctx->stream->functions->control)(ctx->stream->handle, op, data);
							 | 
						||
| 
								 | 
							
								      return -1;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int
							 | 
						||
| 
								 | 
							
								cgi_close(void *handle)
							 | 
						||
| 
								 | 
							
								{ cgi_context *ctx = handle;
							 | 
						||
| 
								 | 
							
								  int rc = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  DEBUG(1, Sdprintf("cgi_close()\n"));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  switch( ctx->state )
							 | 
						||
| 
								 | 
							
								  { case CGI_DATA:
							 | 
						||
| 
								 | 
							
								    { if ( ctx->transfer_encoding == ATOM_chunked )
							 | 
						||
| 
								 | 
							
								      { if ( cgi_chunked_write(ctx, NULL, 0) < 0 )
							 | 
						||
| 
								 | 
							
									{ rc = -1;
							 | 
						||
| 
								 | 
							
									  goto out;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								      } else
							 | 
						||
| 
								 | 
							
								      { size_t clen = ctx->datasize - ctx->data_offset;
							 | 
						||
| 
								 | 
							
									const char *dstart = &ctx->data[ctx->data_offset];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if ( !call_hook(ctx, ATOM_send_header) )
							 | 
						||
| 
								 | 
							
									{ rc = -1;
							 | 
						||
| 
								 | 
							
									  goto out;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if ( Sfwrite(dstart, sizeof(char), clen, ctx->stream) != clen ||
							 | 
						||
| 
								 | 
							
									     Sflush(ctx->stream) < 0 )
							 | 
						||
| 
								 | 
							
									{ rc = -1;
							 | 
						||
| 
								 | 
							
									  goto out;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    case CGI_HDR:
							 | 
						||
| 
								 | 
							
								      break;
							 | 
						||
| 
								 | 
							
								    case CGI_DISCARDED:
							 | 
						||
| 
								 | 
							
								      goto out;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !call_hook(ctx, ATOM_close) )	/* what if we had no header sofar? */
							 | 
						||
| 
								 | 
							
								    rc = -1;				/* TBD: pass error kindly */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								out:
							 | 
						||
| 
								 | 
							
								  ctx->stream->encoding = ctx->parent_encoding;
							 | 
						||
| 
								 | 
							
								  free_cgi_context(ctx);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return rc;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static IOFUNCTIONS cgi_functions =
							 | 
						||
| 
								 | 
							
								{ NULL,					/* read */
							 | 
						||
| 
								 | 
							
								  cgi_write,
							 | 
						||
| 
								 | 
							
								  NULL,					/* seek */
							 | 
						||
| 
								 | 
							
								  cgi_close,
							 | 
						||
| 
								 | 
							
								  cgi_control,				/* control */
							 | 
						||
| 
								 | 
							
								  NULL,					/* seek64 */
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       OPEN		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int current_id = 0;		/* TBD: MT: lock */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define CGI_COPY_FLAGS (SIO_OUTPUT| \
							 | 
						||
| 
								 | 
							
											SIO_TEXT| \
							 | 
						||
| 
								 | 
							
											SIO_REPXML|SIO_REPPL|\
							 | 
						||
| 
								 | 
							
											SIO_RECORDPOS)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static foreign_t
							 | 
						||
| 
								 | 
							
								pl_cgi_open(term_t org, term_t new, term_t closure, term_t options)
							 | 
						||
| 
								 | 
							
								{ term_t tail = PL_copy_term_ref(options);
							 | 
						||
| 
								 | 
							
								  term_t head = PL_new_term_ref();
							 | 
						||
| 
								 | 
							
								  cgi_context *ctx;
							 | 
						||
| 
								 | 
							
								  IOSTREAM *s, *s2;
							 | 
						||
| 
								 | 
							
								  module_t module = NULL;
							 | 
						||
| 
								 | 
							
								  term_t hook = PL_new_term_ref();
							 | 
						||
| 
								 | 
							
								  record_t request = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  PL_strip_module(closure, &module, hook);
							 | 
						||
| 
								 | 
							
								  if ( !PL_is_callable(hook) )
							 | 
						||
| 
								 | 
							
								    return type_error(closure, "callable");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while(PL_get_list(tail, head, tail))
							 | 
						||
| 
								 | 
							
								  { atom_t name;
							 | 
						||
| 
								 | 
							
								    int arity;
							 | 
						||
| 
								 | 
							
								    term_t arg = PL_new_term_ref();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ( !PL_get_name_arity(head, &name, &arity) || arity != 1 )
							 | 
						||
| 
								 | 
							
								      return type_error(head, "option");
							 | 
						||
| 
								 | 
							
								    _PL_get_arg(1, head, arg);
							 | 
						||
| 
								 | 
							
								    if ( name == ATOM_request )
							 | 
						||
| 
								 | 
							
								    { request = PL_record(arg);
							 | 
						||
| 
								 | 
							
								    } else
							 | 
						||
| 
								 | 
							
								      return existence_error(head, "cgi_open_option");
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if ( !PL_get_nil(tail) )
							 | 
						||
| 
								 | 
							
								    return type_error(tail, "list");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if ( !PL_get_stream_handle(org, &s) )
							 | 
						||
| 
								 | 
							
								    return FALSE;			/* Error */
							 | 
						||
| 
								 | 
							
								  if ( !(s->flags&SIO_OUTPUT) )		/* only allow output stream */
							 | 
						||
| 
								 | 
							
								  { PL_release_stream(s);
							 | 
						||
| 
								 | 
							
								    return permission_error("stream", "write", org);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ctx = alloc_cgi_context(s);
							 | 
						||
| 
								 | 
							
								  ctx->hook = PL_record(hook);
							 | 
						||
| 
								 | 
							
								  ctx->module = module;
							 | 
						||
| 
								 | 
							
								  ctx->request = request;
							 | 
						||
| 
								 | 
							
								  ctx->transfer_encoding = ATOM_none;
							 | 
						||
| 
								 | 
							
								  if ( !(s2 = Snew(ctx,
							 | 
						||
| 
								 | 
							
										   (s->flags&CGI_COPY_FLAGS)|SIO_LBUF,
							 | 
						||
| 
								 | 
							
										   &cgi_functions)) )
							 | 
						||
| 
								 | 
							
								  { free_cgi_context(ctx);			/* no memory */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return FALSE;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  s2->encoding = ENC_ASCII;		/* Header is ASCII only */
							 | 
						||
| 
								 | 
							
								  ctx->parent_encoding = s->encoding;
							 | 
						||
| 
								 | 
							
								  s->encoding = ENC_OCTET;
							 | 
						||
| 
								 | 
							
								  ctx->cgi_stream = s2;
							 | 
						||
| 
								 | 
							
								  if ( PL_unify_stream(new, s2) )
							 | 
						||
| 
								 | 
							
								  { Sset_filter(s, s2);
							 | 
						||
| 
								 | 
							
								    PL_release_stream(s);
							 | 
						||
| 
								 | 
							
								    ctx->id = ++current_id;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return TRUE;
							 | 
						||
| 
								 | 
							
								  } else
							 | 
						||
| 
								 | 
							
								  { return instantiation_error();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void
							 | 
						||
| 
								 | 
							
								install_cgi_stream()
							 | 
						||
| 
								 | 
							
								{ ATOM_header		 = PL_new_atom("header");
							 | 
						||
| 
								 | 
							
								  ATOM_header_codes	 = PL_new_atom("header_codes");
							 | 
						||
| 
								 | 
							
								  ATOM_send_header	 = PL_new_atom("send_header");
							 | 
						||
| 
								 | 
							
								  ATOM_data		 = PL_new_atom("data");
							 | 
						||
| 
								 | 
							
								  ATOM_discarded	 = PL_new_atom("discarded");
							 | 
						||
| 
								 | 
							
								  ATOM_request		 = PL_new_atom("request");
							 | 
						||
| 
								 | 
							
								  ATOM_header		 = PL_new_atom("header");
							 | 
						||
| 
								 | 
							
								  ATOM_client		 = PL_new_atom("client");
							 | 
						||
| 
								 | 
							
								  ATOM_chunked		 = PL_new_atom("chunked");
							 | 
						||
| 
								 | 
							
								  ATOM_state		 = PL_new_atom("state");
							 | 
						||
| 
								 | 
							
								  ATOM_none		 = PL_new_atom("none");
							 | 
						||
| 
								 | 
							
								  ATOM_transfer_encoding = PL_new_atom("transfer_encoding");
							 | 
						||
| 
								 | 
							
								  ATOM_close             = PL_new_atom("close");
							 | 
						||
| 
								 | 
							
								  ATOM_keep_alife        = PL_new_atom("keep_alife");
							 | 
						||
| 
								 | 
							
								  ATOM_connection        = PL_new_atom("connection");
							 | 
						||
| 
								 | 
							
								  ATOM_content_length    = PL_new_atom("content_length");
							 | 
						||
| 
								 | 
							
								  ATOM_id  	         = PL_new_atom("id");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  PREDICATE_call3   = PL_predicate("call", 3, "system");
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  PL_register_foreign("cgi_open",      4, pl_cgi_open,	 PL_FA_TRANSPARENT);
							 | 
						||
| 
								 | 
							
								  PL_register_foreign("is_cgi_stream", 1, is_cgi_stream, 0);
							 | 
						||
| 
								 | 
							
								  PL_register_foreign("cgi_property",  2, cgi_property,	 0);
							 | 
						||
| 
								 | 
							
								  PL_register_foreign("cgi_set",       2, cgi_set,	 0);
							 | 
						||
| 
								 | 
							
								  PL_register_foreign("cgi_discard",   1, cgi_discard,	 0);
							 | 
						||
| 
								 | 
							
								}
							 |