(index ("make-entry" 0) ("make-info-entry" 0) ("make-error-entry" 0) ("make-url-entry" 0) ("sgm->entry" 1137) ("sgm-rules" 1752) ("send-entry" 2623) ("send-entries" 2893) ("handle-request" 3354) ("request" 3354) ("handlers" 3354) ("handle-open-dir" 4386) ("handle-file" 4930) ("handle-sgm" 5906) ("sgm-filename" 5906) ("handle-url" 6727) ("url-redirect-time" 6727) ("match-selector" 7155) ("match-resource" 7543) ("match-url" 8056) ("extension-type" 8271) ("extension-type-map" 8540) ("path->entry" 8874) ("path->entry" 8874) ("filenames->entries" 9753) ("any-handler" 10309) ("bind-fs" 10598) ("sanitize-filename" 11124) ("selector->filename" 11314) ("send-line" 11577) ("send-lastline" 11705) ("utc-seconds->string" 11871) ("logger" 12113) ("logger-impl" 12113) ("logger-port" 12113) ("host" 13136) ("port" 13136) ("listen-address" 13136) ("client-ip" 13136) ("phricken-user" 13906) ("phricken-group" 13906) ("start-server!" 14304))
(def (sig (procedure "(make-entry type name sel #!optional (host h) (port p))" (id make-entry)) (procedure "(make-info-entry . msg)" (id make-info-entry)) (procedure "(make-error-entry . msg)" (id make-error-entry)) (procedure "(make-url-entry name url)" (id make-url-entry))) (p (tt "make-entry") " creates a record consisting of the five main fields in RFC 1436, in the fashion of " (int-link "gopher#make-entry") ".  Here, the " (tt "host") " and " (tt "port") " fields are optional and will be filled in from the " (tt "(host)") " and " (tt "(port)") " module parameters.  Fields may be of any type, as they are converted to strings via " (tt "->string") " before sending.") (p "The other three procedures are convenience functions creating entries of type " (tt "i") ", " (tt "3") " and " (tt "h") " respectively.  As in " (tt "pygopherd") ", info and error entries have their selectors set to " (tt "\"fake\"") ", hosts to " (tt "\"(NULL)\"") ", and ports to " (tt "\"0\"") ".") (p "Examples:") (pre "(make-entry 'I \"Picture of me\" \"/me.jpg\")\n(make-info-entry \"There are \" time-til-boom \" seconds until self-destruct\")"))
(def (sig (procedure "(sgm->entry expr)" (id sgm->entry))) (p (tt "sgm->entry") " converts a Scheme gophermap entry (s-expression) to a Gopher entry record, using the " (tt "sgm-rules") " alist to transform it. Here are a couple example SGM entries:") (pre "(i \"Blog entry for \" y \"-\" m \"-\" d)\n(1 \"Public directory\" \"/pub\")\n(h \"3e8.org hypertext service\" \"http://3e8.org\")") (p "Entries are meant to look exactly like you'd expect a gophermap to look if implemented using s-expressions, instead of the more typical flat file.  However, the exact behavior is dictated by the " (tt "sgm-rules") "."))
(def (sig (parameter "sgm-rules" (id sgm-rules))) (p (tt "sgm-rules") " is an alist mapping a type symbol to a transformer procedure.  To transform, the appropriate procedure is looked up using the first field (type), and then the entire entry is passed to the procedure via " (tt "apply") ".  It is therefore natural to use the existing " (int-link "#make-entry" "make-entry") " family of procedures here.") (p "Here's the default definition of " (tt "sgm-rules") ":") (pre "(define sgm-rules\n  (make-parameter\n   `((*default* . ,make-entry)\n     (i . ,(lambda (type . msg)    (apply make-info-entry msg)))\n     (3 . ,(lambda (type . msg)    (apply make-error-entry msg)))\n     (h . ,(lambda (type name url) (make-url-entry name url))) )))") (p "If the entry type is not found, the rule " (tt "*default*") " is consulted; an error is signaled if no rules match."))
(def (sig (procedure "(send-entry e)" (id send-entry))) (p "Sends one entry to the client.  " (tt "e") " may be an entry record or an sgm entry (s-expr), which is automatically converted into an entry record.") (pre "(send-entry `(3 \"Invalid selector \" ,selector))"))
(def (sig (procedure "(send-entries L)" (id send-entries))) (p "Sends multiple entries to the client, using send-entry.") (pre "(send-entries\n `((i \"Chat log\")\n   (i \"--------\")\n   (i)\n   ,@(map (lambda (x)\n            `(i ,(utc-seconds->string (car x))\n                \" | \" ,(cadr x)))\n          (read-file (chatfile)))\n   (i)\n   (7 \"Say\"     ,(request-selector req))\n   (1 \"Refresh\" ,(request-selector req))\n   (1 \"Go home\" \"\")))"))
(def (sig (procedure "(handle-request selector extra)" (id handle-request)) (record "(request selector matches extra)" (id request)) (parameter "handlers" (id handlers))) (p (tt "handle-request") " is the primary handler procedure, suitable for passing to " (int-link "gopher#accept") ".") (p "It executes the handlers in " (tt "(handlers)") " in order until one returns a true value.  If a handler throws an exception, processing terminates immediately, a generic internal server error is sent to the user, the error is logged, and the exception is re-signaled.") (p "Each handler is passed a " (tt "request") " record.  The " (tt "selector") " and " (tt "extra") " fields are taken directly from the arguments to " (tt "handle-request") " (see " (int-link "gopher#accept") " for an explanation).  The " (tt "matches") " field is initially the empty list, but may be modified by the " (int-link "#Matchers" "matcher procedures") ", which perform a regex match on the selector and set " (tt "matches") " to the submatch results."))
(def (sig (procedure "(handle-open-dir root)" (id handle-open-dir))) (p "Returns a handler which generates a directory listing for any directory under document root " (tt "ROOT") ", using " (int-link "#filenames->entries" "filenames->entries") " to determine how to generate an entry for each filename.") (p "Expects to be attached to a resource (path is second submatch).") (pre "(match-resource \"/pub/www\" (handle-dir \"/var/www/myhost/pub\"))\n; Now selector /pub/www/files sends an index of the \n; directory /var/www/myhost/pub/files"))
(def (sig (procedure "(handle-file root)" (id handle-file))) (p "Returns a file handler for the document root at " (tt "ROOT") ".  The filename path will be taken from the request's second submatch and so is generally wrapped in a " (int-link "#match-resource" "match-resource") ".") (p "This handler sends every file via " (int-link "gopher#send-binary-file") ", even type 0 text files.  This seems to be okay with modern (ahem) Gopher clients, which are less stringent about a terminating full-stop and don't require lines end in CRLF.  Clients therefore receive a verbatim representation of the text file instead of a transformed one, as you would over the web.  If you wish to treat text files separately, you might define a similar handler which uses " (tt "extension-type") " to distinguish between text and binary files.") (pre "(match-resource \"/pub/www\" (handle-file \"/var/www/myhost/pub\"))\n; Now selector /pub/www/todo.txt sends /var/www/myhost/pub/todo.txt"))
(def (sig (procedure "(handle-sgm root)" (id handle-sgm)) (parameter "(sgm-filename \"index.sgm\")" (id sgm-filename))) (p "Serves up Scheme gophermaps.  If " (tt "sgm-filename") " exists in the directory indicated by the selector (relative to " (tt "ROOT") "), read the file contents as a Scheme Gophermap and send the results.  The file is read with " (tt "read-file") ".") (p "Expects to be attached to a resource (path is second submatch).") (p "Example:") (pre "(match-resource \"\" (handle-sgm \"/var/phricken/root\"))\n; An access to /pix/index.sgm will now render the\n; contents of /var/phricken/root/pix/index.sgm") (p "where " (tt "index.sgm") " might contain:") (pre "(i \"My pictures\")\n(i \"-----------\")\n(I \"Me at the Apollo\" \"/pix/apollo.jpg\")\n(I \"Me at Carnegie Hall\" \"/pix/carnegie.jpg\")"))
(def (sig (procedure "handle-url" (id handle-url)) (parameter "(url-redirect-time 0)" (id url-redirect-time))) (p "A handler which sends a meta redirect HTML page to the user.  The destination URL comes from the request's first submatch, so this is usually used with " (int-link "#match-url" "match-url") ".") (p (tt "url-redirect-time") " can be parameterized to set the content refresh time.") (pre "(match-url handle-url)"))
(def (sig (procedure "(match-selector rx handler)" (id match-selector))) (p "Returns a new handler that matches the incoming request selector against regex " (tt "RX") " using " (tt "string-match") ", and calls " (tt "HANDLER") " with the request object.  Any submatches will be added to the " (tt "matches") " field of the request (i.e., it is the CDR of the result of string-match)."))
(def (sig (procedure "(match-resource resource handler)" (id match-resource))) (p "Returns a handler that matches a selector \"resource\" -- this is just a shortcut for match-selector, matching the directory (posix-string or SRE) you provide as \"resource\", plus optional subdirectory path.") (p "For example, " (tt "\"/wiki\"") " will match " (tt "\"(/wiki)($|/*)\"") " and provide those two submatches in the request.  The " (tt "handle-file") " and " (tt "handle-open-dir") " handlers expect exactly this."))
(def (sig (procedure "(match-url handler)" (id match-url))) (p "Convenience matcher for " (tt "URL:xxx") " selectors; the first submatch will be the URL, as expected by " (int-link "#handle-url" "handle-url") "."))
(def (sig (procedure "(extension-type ext)" (id extension-type))) (p "Convert a filename extension EXT (a case-insensitive string) to a Gopher entry type (a symbol) using " (tt "extension-type-map") ".") (pre "(extension-type (pathname-extension \"me.jpg\")) ; => I"))
(def (sig (parameter "extension-type-map" (id extension-type-map))) (p "Case-insensitive map of file extension (as symbol) to 1-character Gopher entry type (as symbol).") (pre "(define extension-type-map\n (make-parameter\n  `((txt . 0) (log . 0) (scm . 0) (sgm . 0) (c . 0) (h . 0)\n    (png . I) (gif . g) (jpg . I) (svg . I))))"))
(def (sig (parameter "path->entry" (id path->entry)) (procedure "((path->entry) dir fn dir-sel)" (id path->entry))) (p "Convert pathname into a Gopher entry.  " (tt "DIR") " is the directory on disk; " (tt "FN") " is the file's basename; " (tt "DIR-SEL") " is the selector corresponding to " (tt "DIR") ".") (p "Returns an entry object or an SGM entry; either is permissible. Generated entries need not be file entries; they might be, for example, info entries!") (p "This is a parameter used by " (tt "filenames->entries") " and ultimately by " (int-link "#handle-dir" "handle-dir") ", so override this if you would like to change how directory contents are presented to the user.") (p "The default value is a procedure that maps directories to type 1, other files based on " (tt "extension-type-map") ", and defaults to binary type 9.  Symbolic links are currently ignored."))
(def (sig (procedure "(filenames->entries dir basenames dir-sel)" (id filenames->entries))) (p "Invokes " (tt "path->entry") " on a list of basenames instead of just one. If " (tt "path->entry") " returns " (tt "#f") " for any entry, it is omitted from the resulting list.") (p (tt "DIR") " is the containing directory on disk; " (tt "BASENAMES") " are the basenames of the files, such as those provided via the " (tt "(directory dir)") " call; " (tt "DIR-SEL") " is the absolute selector corresponding to this directory (not relative to any resource)."))
(def (sig (procedure "(any-handler . handlers)" (id any-handler))) (p "Returns a handler which executes " (tt "HANDLERS") " in order and returns the first true value, or " (tt "#f") ".  Useful when you have more than one handler you'd like to try against a particular matched selector."))
(def (sig (procedure "(bind-fs sel root)" (id bind-fs))) (p "Utility function which 'mounts' fs " (tt "ROOT") " on resource selector " (tt "SEL") " with default filesystem handlers.") (p "Handlers used are " (int-link "#handle-sgm" "handle-sgm") ", " (int-link "#handle-open-dir" "handle-open-dir") ", " (int-link "#handle-file" "handle-file") ".") (pre "(define (bind-fs sel root)\n  (match-resource\n   sel\n   (any-handler (handle-sgm root)\n                (handle-open-dir root)\n                (handle-file root))))"))
(def (sig (procedure "(sanitize-filename fn)" (id sanitize-filename))) (p "Sanitize filename " (tt "FN") "; currently just removes any references to a parent directory " (tt "\"..\"") "."))
(def (sig (procedure "(selector->filename s root)" (id selector->filename))) (p "Converts a selector string into a filename string by prepending the " (tt "ROOT") " path.  Also confirms the file exists and the user has read permission.  Returns #f on failure."))
(def (sig (procedure "(send-line line)" (id send-line))) (p "Send a single line to the client, and terminate it with a CRLF."))
(def (sig (procedure "(send-lastline)" (id send-lastline))) (p "Send an end-of-transmission indicator to the client, which is simply a period on a line by itself."))
(def (sig (procedure "(utc-seconds->string seconds)" (id utc-seconds->string))) (p "Convert seconds since UNIX epoch into a UTC time string suitable for logging.") (pre "(utc-seconds->string (current-seconds))\n;=> \"2009-02-13 21:32:18\""))
(def (sig (parameter "logger" (id logger)) (procedure "(logger-impl type req . msg)" (id logger-impl)) (parameter "logger-port" (id logger-port))) (p "The " (tt "logger") " parameter specifies a logging procedure conforming to the " (tt "logger-impl") " interface.") (p "The default logger implementation logs a formatted message to " (tt "(logger-port)") ", by default " (tt "(current-error-port)") ", or skips logging if the port is " (tt "#f") ".  No locking is performed.  Seek to EOF is not done on write because some ports do not support seeking; therefore, files should be opened in append mode, as in " (tt "(open-output-file file #:append)") ".") (p "It is legal for " (tt "req") " to be " (tt "#f") " if a request has not yet been created--for example, upon early error, or initial connect.") (p (tt "type") " can be any symbol; current types are " (tt "'connect") ", " (tt "'access") ", " (tt "'error") ", " (tt "'redirect") ".  By default, types are not treated specially, just displayed in the log message."))
(def (sig (parameter "host" (id host)) (parameter "(port 70)" (id port)) (parameter "(listen-address #f)" (id listen-address)) (parameter "client-ip" (id client-ip))) (p (tt "host") " and " (tt "port") " are used in " (tt "make-entry") " as the default hostname and port, and port is used in " (tt "start-server!") " to determine which port to listen on.  Note that " (tt "host") " must be a DNS name which clients can resolve to reach your machine. " (tt "host") " defaults to the value of " (tt "(get-host-name)") ".") (p (tt "listen-address") " is the IP address to listen on (bind to), as a string, or " (tt "#f") " for the unspecified address.") (p (tt "client-ip") " is read-only; you can read it inside a handler to determine the IP address of the remote end."))
(def (sig (parameter "(phricken-user #!optional user)" (id phricken-user)) (parameter "(phricken-group #!optional group)" (id phricken-group))) (p (tt "start-server!") " will switch to the specified " (tt "user") " and " (tt "group") " after opening the listen socket, allowing you to run unprivileged while listening on a privileged port. If " (tt "#f") ", the default, then no switch is done."))
(def (sig (procedure "(start-server! #!optional (bg #f))" (id start-server!))) (p "Starts a new threaded server on " (tt "(port)") " using the " (tt "tcp-server") " extension.  Upon connection, control is passed to " (int-link "gopher#accept") ", which will then dispatch back to our own " (int-link "#handle-request" "handle-request") ".") (p "If optional " (tt "BG") " is #t, the server will itself be started in a new thread, allowing you to debug at the REPL.") (p "The " (int-link "/egg/tcp6" "tcp6") " extension is used to permit IPv6 support, so tcp6 parameters such as " (tt "tcp-bind-ipv6-only") " and " (tt "tcp-buffer-size") " are applicable."))
