RFC822(2)                                               RFC822(2)

     NAME
          rfc822 - RFC822 mail format helpers

     SYNOPSIS
          include "bufio.m";
          include "rfc822.m";
          rfc822 := load RFC822 RFC822->PATH;
          Content, Rfclex: import rfc822;

          Word, QString: con ...;
          Maxrequest: con 16*1024;   # more than enough for anything sensible

          init:         fn(b: Bufio);

          Rfclex: adt {
             tok:     int;
             wordval: string;

             mk:      fn(a: array of byte): ref Rfclex;
             getc:    fn(p: self ref Rfclex): int;
             ungetc:  fn(p: self ref Rfclex);
             lex:     fn(p: self ref Rfclex): int;
             unlex:   fn(p: self ref Rfclex);
             skipws:  fn(p: self ref Rfclex): int;

             line:    fn(p: self ref Rfclex): string;
          };

          readheaders:  fn(fd: ref Bufio->Iobuf, limit: int):
                            array of (string, array of byte);
          parseparams:  fn(ps: ref Rfclex): list of (string, string);
          parsecontent: fn(ps: ref Rfclex, multipart: int,
                            head: list of ref Content): list of ref Content;
          mimefields:   fn(ps: ref Rfclex):
                            list of (string, list of (string, string));

          quotable:     fn(s: string): int;
          quote:        fn(s: string): string;

          sec2date:     fn(secs: int): string;
          date2sec:     fn(s: string): int;
          now:          fn(): int;
          time:         fn(): string;

          Content: adt{
             generic:   string;
             specific:  string;
             params:    list of (string, string);

             mk:        fn(specific: string, generic: string,

     Page 1                       Plan 9             (printed 4/16/24)

     RFC822(2)                                               RFC822(2)

                           params: list of (string, string)): ref Content;
             check:     fn(c: self ref Content, oks: list of ref Content): int;
             text:      fn(c: self ref Content): string;
          };

          suffixclass:  fn(name: string): (ref Content, ref Content);
          dataclass:    fn(a: array of byte): (ref Content, ref Content);

     DESCRIPTION
          RFC822 provides types and functions to help read and parse
          RFC822 e-mail headers (following the more streamlined rules
          of RFC2822, which has replaced RFC822), including some MIME
          extensions.  Currently the focus is on operations that sup-
          port HTTP and other W3C protocols, rather than mail reading.

          Init must be called before any other operation in the mod-
          ule.  It must be given an instance of the Bufio module (see
          bufio(2)).

          Readheaders reads a set of RFC822 header lines from fd,
          ended by an empty line.  It returns an array of tuples
          (fieldname, value), one per header line.  The string
          fieldname is the header line's field name, in lower case.
          The value gives the rest of the line, after removing any
          initial white space and appending any continuation lines,
          uninterpreted, as an array of bytes (not a string).  Limit
          is the maximum allowed size of the header in bytes; usually
          that is Maxrequest.  Readheaders returns the tuple
          (nil, nil) on end of file or if the header size limit is
          exceeded.

          Rfclex takes a header line's value and produces a sequence
          of tokens.  It provides the following operations:

          Rfclex.mk(a)
               Return a reference to a new Rfclex value that will pro-
               duce tokens from the array of byte a, which is normally
               the value of one of the header lines returned by
               readheaders.

          rfl.getc()
               Return the next character from the input stream, in the
               Latin-1 (ISO 8859-1) character set.  Return a negative
               value (Bufio->EOF) on end-of-file.

          rfl.ungetc()
               Put back the last character read, which will be
               returned again by the next call to rfl.getc.

          rfl.lex()
               Return the next token from the input stream, ignoring
               any leading white space.  Most tokens are single

     Page 2                       Plan 9             (printed 4/16/24)

     RFC822(2)                                               RFC822(2)

               characters representing themselves.  The token value is
               also assigned to rfl.tok.  There are two special token
               values: Word, representing an unquoted word in the
               RFC2822 grammar; and QString, representing a quoted
               string.  In both cases rfl.wordval will contain the
               text of the word or string (without quotes).

          rfl.unlex()
               Put back the last token read; the next call to rfl.lex
               will return it again.

          rfl.skipws()
               Skip over any white space at the current position;
               return the initial character of the next token (which
               is not consumed).

          rfl.line()
               Return a string giving the remainder of the input line.

          Several functions take an Rfclex referring to a header
          line's value, parse it, and return a higher-level represen-
          tation of its value.

          Parseparams parses a sequence of parameter settings of the
          form (;attribute=value)* and returns a corresponding list of
          tuples (attribute, value).  It returns nil if no parameters
          are found.

          Parsecontent parses the values of fields such as
          Content-Type and Accept.  The syntax is (loosely) a sequence
          of comma-separated elements of the form type, type/*, or
          type/subtype followed by an optional sequence of parameters
          of the form (;attribute=value )*.  The type/subtype form is
          allowed only if multipart is true (non-zero).  It returns a
          corresponding list of Content values followed by the initial
          list head.

          Mimefields parses a sequence of comma-separated elements of
          the form
          h.gnidoc-refsnartdnefernamlmth.gnidoc-refsnarttratsfernamlmt

             h.)2(emityaddnefernamlmth.)2(emityadtratsfernamlmt

               h.))2(emityaddnefernamlmth.))2(emityadtratsfernamlmt

                       h.kodnefernamlmth.kotratsfernamlmt

                    h.cdnefernamlmth.ctratsfernamlmt
                       h,cdnefernamlmth,ctratsfernamlmt

               h.)cdnefernamlmth.)ctratsfernamlmt

                    h:cdnefernamlmth:ctratsfernamlmt

     h&lmtlt;&4Hgt;    h&lmtlt;&4H/gt;

     h&lmtlt;&4Hgt;    h&lmtlt;&4H/gt;