(* $Id: netcgi_types.mli,v 1.9 2002/10/24 23:47:48 stolpmann Exp $
 * ----------------------------------------------------------------------
 *
 *)

exception Resources_exceeded


(* Introduction:
 * There are two representations of CGI arguments, simple_message and
 * mime_message. The first consists only of a name and a value while the
 * second representation has additionally a MIME header.
 *
 * The simple_message arguments are used if the QUERY_STRING contains
 * the encoded arguments, or if the posted message is x-www-urlencoded.
 * The mime_message representation is used if the posted message is
 * form-encoded.
 *
 * Independently of the representation, the arguments are stored somewhere.
 * Supported stores are currently `Memory and `File. 
 * (Restriction: A simple_message must be stored in `Memory.)
 *)

type store =
  [ `Memory
  | `File of string
      (* The string is the filename of a file containing the (decoded) value
       * of the argument
       *)
  ]

(* In general, the [store] is just a container, and the interpretation
 * of the [store] depends on the [representation] of the argument. For the
 * two defined representations, the container holds the decoded value of
 * the argument. For future extensions of [representation], the [store] might
 * be interpreted differently, though.
 *)


class type simple_message = Netmime.mime_body
  (* A simple_message is one of two representations of CGI arguments. 
   * It stores a non-structured string value. It happens that Netmime.mime_body
   * is already a class type for such non-structured values, so we are
   * reusing this type here.
   *)

(* The types mime_header and mime_message are now defined in Netmime. *)


type representation =
  [ `Simple of simple_message
  | `MIME of Netmime.mime_message
  ]

(* Representations of CGI arguments:
 * `Simple representation: The argument has a name and a value, and no more
 *    properties.
 * `MIME representation: The argument has a name, a MIME header, and a value.
 *)


(* The exception Value_is_immutable has been renamed to just "Immutable",
 * and its definition has been moved to the module Netmime.
 *)

class type cgi_argument =
object
  method name : string
  method value : string
    (* This method returns always the decoded value of the argument. If the
     * value is stored in a file, the file will be loaded.
     *)
  method open_value_rd : unit -> Netchannels.in_obj_channel
    (* Opens the value for reading. *)
  method ro : bool
    (* whether this argument is read-only or not *)
  method store : store
    (* see above *)
  method content_type : string
    (* The MIME type, by default "text/plain". This string is the media
     * type without parameters.
     *)
  method content_type_params : (string * Mimestring.s_param) list
    (* The parameters of the MIME type, by default [] *)
  method charset : string
    (* The charset parameter of the MIME type, or "" if not present. *)
  method filename : string option
    (* The user filename of uploaded files *)
  method representation : representation
    (* see above *)
  method finalize : unit -> unit
    (* Arguments stored in files must be deleted when the argument is no 
     * longer used. You can call [finalize] to delete such files. The
     * method does not have any effect for [store = `Memory].
     * The method does not raise any exceptions. If the file does no longer
     * exist (e.g. because it is moved away), or if there are any problems
     * deleting the file, the error will be suppressed.
     * The [finalize] method is not registered in the garbage collector.
     * You can do that, but it is usually better to call this method manually.
     * [cgi_activation] supports this.
     *)

  method set_value : string -> unit
    (* If the [representation] supports mutable values, the value is set
     * to the passed string. The other properties of the argument are not
     * modified.
     * If the [representation] does not support this feature, the exception
     * Netmime.Immutable will be raised.
     *)
  method open_value_wr : unit -> Netchannels.out_obj_channel
    (* Opens the value for writing. The current value is overwritten. 
     * If the value is immutable, the exception Immutable will be raised.
     *)
end


type cgi_cookie = Cgi.cookie = 
    { cookie_name : string;
      cookie_value : string;           (* may contain every character *)
      cookie_expires : float option;
        (* None: the cookie expires when the browser session ends.
         * Some t: the cookie expires at the time t (seconds since the epoch)
         *)
      cookie_domain : string option;
        (* None: the domain is the hostname of the server
         * Some domain: the domain is this string
         *)
      cookie_path : string option;
        (* None: the path is script name + path_info
         * Some p: the path is p
         *)
      cookie_secure : bool;
        (* Whether SSL is necessary to set the cookie *)
    }


type status =   (* Status codes from RFC 2616 *)
  (* 2xx: (successful) *)
  [ `Ok
  | `Created
  | `Accepted
  | `Non_authoritative
  | `No_content
  | `Reset_content
  | `Partial_content
  (* 3xx: (redirection) *)
  | `Multiple_choices
  | `Moved_permanently
  | `Found
  | `See_other
  | `Not_modified
  | `Use_proxy
  | `Temporary_redirect
  (* 4xx: (client error) *)
  | `Bad_request
  | `Unauthorized
  | `Payment_required
  | `Forbidden
  | `Not_found
  | `Method_not_allowed
  | `Not_acceptable
  | `Proxy_auth_required
  | `Request_timeout
  | `Conflict
  | `Gone
  | `Length_required
  | `Precondition_failed
  | `Request_entity_too_large
  | `Request_uri_too_long
  | `Unsupported_media_type
  | `Requested_range_not_satisfiable
  | `Expectation_failed
  (* 5xx: (server error) *)
  | `Internal_server_error
  | `Not_implemented
  | `Bad_gateway
  | `Service_unavailable
  | `Gateway_timeout
  | `Http_version_not_supported 
  ]
    (* The type "status" may be moved to another, HTTP-specific module *)


type request_method =
  [ `GET
  | `HEAD
  | `POST
  | `DELETE
  | `PUT of cgi_argument
  ]
  (* Note that there is also a configuration option in the environment
   * that specifies which methods are allowed at all.
   *)    

type cache_control =
    [ `No_cache
    | `Max_age of int
    | `Unspecified
    ]
  (* This is only a small subset of the HTTP 1.1 cache control features,
   * but they are usually sufficient, and they work for HTTP/1.0 as well.
   * The directives mean:
   * - `No_cache:
   *   Caches are disabled. The following headers are sent:
   *   Cache-control: no-cache, Pragma: no-cache, Expires: (now - 1 second)
   * - `Max_age n:
   *   Caches are allowed to store a copy of the response for n seconds.
   *   After that, the response must be revalidated.
   *   Cache-control: max-age n, Cache-control: must-revalidate,
   *   Expires: (now + n seconds)
   * - `Unspecified:
   *   No cache control header is added to the response.
   *
   * Notes:
   * - Cache control directives only apply to GET requests; POST requests
   *   are never cached
   * - Not only proxies are considered as cache, but also the local disk
   *   cache of the browser
   * - HTTP/1.0 did not specify cache behaviour as strictly as HTTP/1.1
   *   does. Because of this the "Pragma" and "Expires" headers are sent, too.
   *   These fields are not interpreted by HTTP/1.1 clients because 
   *   "Cache-control" has higher precedence.
   *)

type query_string_spec =
    [ `Initial | `Current | `Args of cgi_argument list | `None ]

 (* `Initial: The query string is created from the initial
  *    CGI arguments
  * `Current: The query string is created from the current
  *    CGI arguments
  * `Args: The query string is created from the specified argument list
  * `None: The query string is omitted
  *)

type other_url_spec =
    [ `Env | `This of string | `None ]

  (* `Env: Take the value from the environment
   * `This: Use this value. It must already be URL-encoded.
   * `None: Do not include this part of the URL
   *)


class type cgi_activation =
object
  method environment : Netcgi_env.cgi_environment

  method request_method : request_method

  method initial_arguments : (string * cgi_argument) list
  method initial_argument : string -> cgi_argument
  method initial_argument_value : ?default:string -> string -> string
  method initial_multiple_argument : string -> cgi_argument list
    (* The arguments at the time the CGI program was started *)

  method arguments : (string * cgi_argument) list
  method argument : string -> cgi_argument
  method argument_value : ?default:string -> string -> string
  method multiple_argument : string -> cgi_argument list
    (* The current CGI arguments *)

  (* argument: raises Not_found if the argument is unknown.
   * argument_value: returns the ~default if the argument is unknown;
   *    ~default defaults to "".
   * multiple_argument: returns all argument with the passed name, not only
   *    the first
   * Same conventions for the initial_* methods.
   *)

  method set_arguments : ?fin:bool -> cgi_argument list -> unit
  method update_argument : ?fin:bool -> cgi_argument -> unit
  method update_multiple_argument : ?fin:bool -> cgi_argument list -> unit
  method delete_argument : ?fin:bool -> string -> unit
    (* Modify the current CGI arguments:
     *
     * set_arguments: The passed list becomes the new list of current args
     * update_argument: The passed argument replaces the current argument
     *    (or multiple argument) with the same name; if there is no such
     *    argument, the passed argument is added to the list of current args
     * update_multiple_argument: It is required that all passed arguments
     *    have the same name. This method replaces all existing arguments
     *    with this name by the passed list.
     * delete_argument: Deletes all arguments with the passed name.
     *    Nothing happens if no such argument exists.
     *
     * The behaviour is undefined if the same argument object occurs several
     * times in the list of current arguments.
     *
     * ~fin: If true, the arguments will be finalized that are no longer
     *   member of the current argument set. Default: true.
     *
     * RESTRICTION: Arguments stored in files are read-only, and cannot
     * be modified using these methods.
     *)

    (* DISCUSS: We are not sure whether it is good or bad to make the
     * arguments mutable here.
     * If mutability is only for the "url" method, this can be solved
     * in a purely functional way (pass the changed arguments to "url"
     * directly).
     *)

  method url : ?protocol:Netcgi_env.protocol ->   
                                            (* default: from environment *)
               ?with_authority:other_url_spec ->        (* default: `Env *) 
               ?with_script_name:other_url_spec ->      (* default: `Env *)
               ?with_path_info:other_url_spec ->        (* default: `Env *)
               ?with_query_string:query_string_spec ->  (* default: `None *)
               unit ->
		 string

  method output : Netchannels.trans_out_obj_channel
                                              (* The current output channel *)

  (* THIS IS MEANT AS FOLLOWS:
   * 
   * method output : trans_out_obj_channel
   *
   * If an error happens, it is possible to roll the channel back, and
   * to write the error message:
   * try
   *   cgi # set_header ... ();
   *   cgi # output # output_string "Hello World!"; ...
   *   cgi # output # commit_work();
   * with
   *   err ->
   *     cgi # output # rollback_work();
   *     cgi # set_header ... ();
   *     cgi # output # output_string "Software error!"; ...
   *
   * This has also the advantage that there is a clear moment when the 
   * header is sent: When the output channel is committed.
   *
   * The rollback works only if the class bases on a transactional channel.
   * If not, the implementation is free to do nothing, to raise an exception,
   * or to do something else.
   *)

  method set_header :
           ?status:status -> 
	   ?content_type:string ->
           ?cache:cache_control ->
           ?filename:string ->
           ?language:string ->
           ?script_type:string ->
           ?style_type:string ->
           ?set_cookie:cgi_cookie list ->
           ?fields:(string * string list) list ->
	   unit ->
	     unit
    (* Sets the header (see also comment below).
     * ~status: Sets the HTTP status of the reply. Defaults to "no status",
     *   but the server normally returns `Ok in this case
     * ~content_type: Sets the content type. Defaults to "text/html" if the
     *   content type is not yet set.
     * ~cache: Sets the cache behavior for replies to GET requests. The
     *   default is `Unspecified. IT IS STRONGLY RECOMMENDED TO SPECIFY
     *   THE CACHE BEHAVIOR!!! You are on the safe side with `No_cache,
     *   forcing every page to be regenerated. If your data do not change
     *   frequently, `Max_age n tells the caches to store the data at most
     *   n seconds.
     * ~filename: Sets the filename associated with the page. This filename
     *   is taken for the "save as..." dialog. Default: no filename.
     *   Note: It is bad practice if the filename contains:
     *     - The characters: backslash, double quote, space. These characters
     *       cause incorrect behaviour of many browsers (unless the RFC822
     *       quoting mechanism is correctly implemented)
     *     - Names of directories
     * ~script_type: Sets the language of the script tag (for HTML replies).
     *   It is recommended to use this field if there are ONXXX attributes
     *   containing scripts before the first <SCRIPT> element, because you
     *   cannot specify the script language for the ONXXX attributes.
     *   ~script_type must be a MIME type, e.g. "text/javascript".
     *   Default: no language is specified.
     * ~style_type: Sets the language of the style tag (for HTML replies).
     *   It is recommended to use this field if there are STYLE attributes
     *   containing scripts before the first <STYLE> element, because you
     *   cannot specify the style language for the STYLE attributes.
     *   ~style_type must be a MIME type, e.g. "text/css". 
     *   Default: no language is specified.
     * ~set_cookie: Sets a number of cookies.
     *   Default: []
     *   You can query the cookies using environment#cookies.
     * ~fields: Sets other fields of the header.
     *
     * If set_header is called several times, _all_ of the header fields
     * are overwritten.
     *)

  method set_redirection_header :
           string ->
	     unit
    (* Sets the header such that a redirection to the specified URL
     * is performed. If the URL begins with "http:" the redirection directive is
     * passed back to the client, and the client will repeat the request for
     * the new location. If the URL begins with "/", the server performs the
     * redirection, and it is invisible for the client.
     *)

    (* GENERAL NOTES ABOUT SETTING THE HEADER: *)

    (* The header is automatically prepended to the selected output channel.
     * You must call set_header or set_redirection_header (at least) once.
     * If the class supports transactions, it is sufficient to call one of
     * the methods until commit_work is invoked, and it is possible to call
     * them several times.
     * If the class does not support transactions, only one of the methods
     * must be called, and it must be called before any other output is
     * generated.
     *
     * The header is treated in a special way. It is passed to the environment
     * object, and the send_output_header method of this object is called at
     * the right moment, usually when the output data are committed. This
     * means that the environment object is responsible for sending the
     * header to the client, and not the implementation of cgi_activation.
     *
     * To avoid confusion: There are two output channels, and they are
     * layered. In cgi_environment, the output channel is usually a 
     * wrapped stdout, so writing to this channel writes directly to
     * stdout. In cgi_activation, the output channel is usually a
     * transactional channel (a channel that buffers output until it is
     * committed). In the current design, the header is _not_ written
     * to the transactional channel, but directly into variables of
     * cgi_environment, and cgi_environment is told when the header is
     * valid and should be sent to stdout (usually when the data of the
     * transactional channel is committed). 
     *
     * This looks complicated, but I think there are several advantages:
     * - It is possible to change header fields at every moment before
     *   the commitment happens. For example, it is possible to set
     *   the content-length field which is normally only known just
     *   at the time of the commit operation.
     * - The cgi_environment object can process the header; for example
     *   it can fix header fields.
     * - It is simpler to connect to environments which transport the
     *   header in non-standard ways. Example: Assume that the environment
     *   is the web server process (e.g. we are an Apache module). Typically
     *   the header must be stored in different structures than the 
     *   body of the message.
     *)

  (* later: 
   * method send_mime_message : mime_message -> unit
   * method begin_multipart_message : XXX -> unit
   * method end_multipart_message : XXX -> unit
   *)

  method finalize : unit -> unit
    (* This method calls [finalize] for every CGI argument to ensure that
     * all files are deleted.
     * It does not close the in/out channels.
     * This method is not registered in the garbage collector.
     *)

end



(* ======================================================================
 * History:
 * 
 * $Log: netcgi_types.mli,v $
 * Revision 1.9  2002/10/24 23:47:48  stolpmann
 * 	Support for the HEAD method.
 * 	Workaround for a bug in MSIE: Empty cookies are represented
 * in the wrong way
 *
 * Revision 1.8  2002/01/14 01:10:50  stolpmann
 * 	cgi_argument bases now on Netmime.mime_message.
 *
 * Revision 1.7  2001/11/17 23:27:07  stolpmann
 * 	Changed options for [url] method: The with_XXX options
 * have the values [ `Env | `This of string | `None ] instead of
 * just bool.
 *
 * Revision 1.6  2001/10/14 15:45:52  stolpmann
 * 	Added a comment.
 *
 * Revision 1.5  2001/10/07 19:46:17  stolpmann
 * 	New comments, esp. set_header.
 *
 * Revision 1.4  2001/10/04 01:07:15  stolpmann
 * 	Moved from directory /src/netstring to /src/cgi
 *
 * Revision 1.3  2001/09/30 00:02:52  stolpmann
 * 	Documentation only.
 *
 * Revision 1.2  2001/09/28 21:21:53  stolpmann
 * 	Enhanced MIME objects.
 * 	Improved handling of finalization.
 *
 * Revision 1.1  2001/09/27 21:59:00  stolpmann
 * 	Initial revision
 *
 * 
 *)
