(* $Id: netmime.mli 1003 2006-09-24 15:17:15Z gerd $
* ----------------------------------------------------------------------
*
*)
(** Netmime contains high-level classes and functions to process
* mail and MIME messages.
*
* {b Contents}
*
* - {!Netmime.types}
* - {!Netmime.classes}
* - {!Netmime.parsing}
* - {!Netmime.printing}
*
* The tutorial has been moved to {!Netmime_tut}.
*)
(* ***************************** Types ******************************** *)
(** {1:types Types} *)
open Netchannels
type store =
[ `Memory
| `File of string
(* The string is the filename of a file containing the (decoded) value
* of the body
*)
]
(** Specifies where to store the body of a mail message. [`Memory]
* means in-memory, [`File name] means in the file [name]. The body
* is stored in decoded form (i.e. without transfer encoding).
*)
exception Immutable of string
(** Raised if it is tried to modify a read-only value. The string denotes
* the function or method where the incident happened.
*)
(** MIME headers and bodies are defined in two steps. First the subtype
* describing read access is defined ([mime_header_ro], and [mime_body_ro]),
* and after that the full class type including write access is defined
* ([mime_header], and [mime_body]).
*
* The idea is that you can write functions that take an ro value as
* input to indicate that they do not modify the value. For example:
*
* {[
* let number_of_fields (h:#mime_header_ro) =
* List.length (h#fields) ]}
*
* This function accepts both [mime_header], and [mime_header_ro] values as
* input, but the typing ensures that the function cannot mutate anything.
*
* There is another way to ensure that a header or body is not modified.
* The read-only flag can be set when creating the object, and this flag
* causes that all trials to modify the value will raise the exception
* [Immutable]. Of course, such trials of mutation are only detected at
* run-time.
*
* The advantage of the read-only flag is that it even works if
* mutation depends on a condition, but it can be ensured that this
* condition is never true. Furthermore, typing is much simpler (getting
* subtyping correct can be annoying).
*)
(** This is the read-only version of a MIME header. There are only methods
* to read the header fields.
*)
class type mime_header_ro =
object
(* read-only view of a mime_header *)
method fields : (string * string) list
method field : string -> string
method multiple_field : string -> string list
(** The current fields of the header. [fields] returns the complete
* header. [field name] returns the value of the field, or raises
* [Not_found]. [multiple_field name] returns all fields with the same
* name.
*
* Note that field names are case-insensitive; [field "content-length"],
* and [field "CONTENT-LENGTH"] will return the same field. However,
* the method [fields] returns the original field names, without
* adjustment of the case.
*
* The order of the fields is preserved.
*)
(* --------------------- Standard fields ----------------------- *)
(** Access methods for frequent standard fields.
*
* These methods will raise [Not_found] if the fields are not
* present.
*)
method content_length : unit -> int
(** Returns the Content-length field as integer *)
method content_type :
unit -> (string * (string * Mimestring.s_param)list)
(** Returns the Content-type as parsed value. The left value of the
* pair is the main type, and the right value is the list of
* parameters. For example, for the field value
* ["text/plain; charset=utf-8"] this method returns
* [("text/plain", ["charset", p])] where [p] is an opaque value
* with [Mimestring.param_value p = "utf-8"].
*)
method content_disposition :
unit -> (string * (string * Mimestring.s_param)list)
(** Returns the Content-disposition field as parsed value. The
* left value is the main disposition, and the right value is the
* list of parameters. For example, for the field value
* ["attachment; filename=xy.dat"] this method returns
* [("attachment", ["filename", p])] where [p] is an opaque value
* with [Mimestring.param_value p = "xy.dat"].
*)
method content_transfer_encoding : unit -> string
(** Returns the Content-transfer-encoding as string *)
end
(** A MIME header with both read and write method. It is still possible,
* however, to set the read-only flag to make this kind of header
* immutable, too.
*)
class type mime_header =
object
(* A mutable or immutable mime_header *)
inherit mime_header_ro
(** Supports all these read access method, too *)
method ro : bool
(** whether the header is read-only or not *)
method set_fields : (string * string) list -> unit
method update_field : string -> string -> unit
method update_multiple_field : string -> string list -> unit
method delete_field : string -> unit
(** These methods modify the fields of the header. If the header is
* read-only, the exception [Immutable] will be raised.
*
* [set_fields] replaces the current fields with a new list of
* (name,value) pairs. [update_field name value] replaces all fields
* of the passed name with the single setting (name,value), or
* adds this setting to the list. [update_multiple_field name values]
* replaces all fields of the passed name with the list of values,
* or adds this list. Finally, [delete_field name] deletes all
* fields of the passed name. Nothing happens if there is no such
* field.
*
* Both [update_field] and [update_multiple_field] first replace
* existing values by the new ones without changing the order
* of the fields in the header. Additional values are inserted
* after the last existing value, or at the end of the header.
*)
end
(** This is the read-only version of a MIME body. There are only methods
* to read the body contents.
*
* The value of the body can be returned either as [string], or as
* object channel. Both ways are possible independently of where
* the value is stored, in-memory, or as external file.
*)
class type mime_body_ro =
object
(* a read-only view of a mime_body *)
method value : string
(** The [value] method returns the _decoded_ body,
* i.e. transfer encodings are removed before the value is passed
* back.
*
* When the body is stored in an external file, this method
* reads the complete file into memory.
*)
method store : store
(** Where the body is actually stored. *)
method open_value_rd : unit -> in_obj_channel
(** Opens the value for reading. This works independently of where
* the body is stored. For example, to read the body line by line:
* {[
* let ch = body # open_value_rd () in
* try
* while true do
* let line = ch # input_line() in
* ... (* do something *)
* done;
* assert false; (* never reached *)
* with
* End_of_file ->
* ch # close_in()
* ]}
*
* As [value], this method returns the value in decoded form.
* This method is quite economical with the resources, and takes
* only as much memory as needed for the channel operations.
*)
method finalize : unit -> unit
(** After the body has been finalized, it cannot be accessed any longer.
* External resources (files) are deallocated, if they are seen as
* temporary.
*)
end
(** A MIME body with both read and write method. It is still possible,
* however, to set the read-only flag to make this kind of body
* immutable, too.
*
* The value of the body can be set either by a [string], or by writing
* to an object channel. Both ways are possible independently of where
* the value is stored, in-memory, or as external file.
*)
class type mime_body =
object
(* A mutable or immutable mime_body *)
inherit mime_body_ro
(** Supports all these read access method, too *)
method ro : bool
(** whether this body is read-only or not *)
method set_value : string -> unit
(** Sets the value. If the value is immutable, the exception
* [Immutable] will be raised.
*
* The passed string must be in decoded form. When the body is
* stored in an external file, the file is overwritten.
*)
method