SREhttp/2 || SRE2003 Procedure Library

## The SREhttp/2 Procedure Library

### Description of The SREhttp/2 functions

The following lists several useful functions contained in the SREhttp/2 procedure library. This is not a complete list, there are other functions that are only meant to be used by the SREhttp/2 filter.

#### Notes:

- These complement the SRE2003 procedure library
- SREhttp/2 also sets a number of useful environment variables (that can be read using the SREH2_VALUE procedure)

Alphabetical list of procedures
`SREH2_CFG_READ` | read several selector specific attribute files
`SREH2_CFG_BACKUP` | read & backup a CFG file
`SREH2_AUTO_CHECK_PRIVS` | check if access to resource is allowed
`SREH2_CHECK_PRIVS` | check for required privileges
`SREH2_CURRENT_SECOND` | Return the current time, as a fraction of a day, accurate to a second
`SREH2_DIGITS` | create a graphical message, using digit-GIF files
`SREH2_DATETIME_CONVERT` | Convert, using c strftime format,a time-date formatted string
`SREH2_DYNAMIC_PWD` | provide "digest" style authentication to http/1.0 browsers.
`SREH2_DYNAMIC_PRIVS` | read or write dynamic privileges
`SREH2_FIG_FILE_NAME` | determine file name, given datadir or virtual dir
`SREH2_AUTO_FIG_FILE_NAME` | simple version of SREH2_FIG_FILE_NAME
`SREH2_FILE_CACHE` | work with the SREhttp/2 file cache
`SREH2_FILE_INTREE` | search a directory tree for a file
`SREH2_FIND_SPECIAL` | find a !special directive in the selector
`SREH2_FORCE_RESPONSE` | invoke a forced-authorization request
`SREH2_GET_ATTRIBUTES` | lookup selector specific attributes
`SREH2_GET_COOKIE` | retrieve the value of a cookie
`SREH2_GET_MIMETYPE` | Return the media type of a file, based on its extension.
`SREH2_GET_USERINFO` | read privileges from the USERS.CFG file(s)
`SREH2_HOSTDATA` | update hostdata info
`SREH2_HOMEDIR` | check ~ replacement
`SREH2_HTML_COMMENT` | return string embedded within <-- -->
`SREH2_MAILIT` | send an e-mail message through an SMTP gateway
`SREH2_MULTI_SEND` | send a multipart document, or send pieces of a document
`SREH2_QUERYFILE` | return the name of a file, possibly relative to a supplied directory.
`SREH2_PRIVS_CHECK` | Compare client-privileges to required privileges
`SREH2_RESET` | Reset SREhttp/2
`SREH2_TEXT2HTML` | Converts text string to html-friendly text
`SREH2_SET_COOKIE` | add a set-cookie header
`SREH2_VALUE` | lookup a SREhttp/2 value
`SREH2_VERSION` | lookup SREhttp/2 version number
`SREH2_XBITMAP` | produce and XBITMAP image

### The SREhttp/2 functions: full descriptions

SREH2_CFG_READ:
  Update information contained in various host-specific configuration files: SREHTTP2.CFG, SSI_VARS.CFG, and MIMETYPE.CFG.

  `Syntax:
  foo=sreh2_cfg_read()`
  where `foo` contains:

  - `1' 'nf1' 'nfdone1' 'nf2' 'nf2done 'nf3' 'nf3done`
    (first two values refer to SREHTTP2.CFG files, next two SSI_VARS.CFG files, and the last two MIMETYPE.CFG files)

SREH2_CFG_BACKUP:
  Read & backup configuration files.
  This is a 2 step procedure (it should be called twice).
  1. The first call backups the configuration file, returns the name of a temporary file to use, returns the name of the actual file, and returns the current contents of the configuration file.
  2. 2) The second step copies the "temporary file" to the "actual file", and deletes the temporary file
  In both steps, if an error condition occurs, processing stops and and an error message is returned.

  step 1 | Syntax: `stuff=sreh2_cfg_backup(cfg_type,host_nickname,nobackup)` where: cfg_type cfg_type is one of the non-host specific files: `HOSTDATA, PRELOADS, LOGS, SRE2003, BADIPS` or one of the host-specific files:`SREHTTP2, USERS, MIMETYPE, ATTRIBS, COUNTER, MANAGER, SRE_UTIL, SSI_VARS` or a generic host specific type `FILE:filename.ext` Other values return an error (although a few abbreviations of the above are recognized). host_nickname host_nickname identifies which version of the CFG file you want to backup. To backup the default version, use `DEFAULT`, or don't specify host_nickname. Note that if cfg_type is a non-host specific file, then host_nickname is ignored. nobackup to suppress creation of a backup file, set nobackup=1 stuff If success, stuff has the structure: `1 TEMPFILE_NAME CFGFILE_NAME BACKUP_NAME,contents` `tempfile_name`: use this to create new version `cfgfile_name`: this where the new version will eventually be written to `backup_name`: this is the backup of the current version. If `nobackup=1`, or if there is no current version of the cfg_type, then backup_name=`.` `contents`: may be quite long, and contains CRLFs. Note the comma immediately preceding contents! If error. stuff has the structure: `0 Error Message` or `-1 serious error mesage` A `-1` means that the old version of the cfgfile has been removed, without a replacement. The error message tells you the details (almost always, the old version is retained in a temporary file)
  Step 2 | Syntax: `status=sreh2_cfg_backup(,tempfile_name,cfgfile_name)` where: `tempfile_name` and `cfgfile_name` are from step 1 `status` is either `1 message` (for success) or `0 Error message` if an error occurred.

  #### Notes:

  - The `tempfile_name` returned in step 1 is a suggestion -- SREH2_CFG_BACKUP will not create, or otherwise `open` this file (it will check that no such file with this name currently exists).
  - Often, the `cfgfile_name` is the file currently being used (for this `cfg_type` and for this host). However, this file may not exist (say, if no attributes have yet been defined for a newly specified host-nickname).
  - To just read the contents of a given CFG file (for a specific host-nickname), just use a value of `1` for the `nobackup` parameter.
  - If there is no currently existing CFG file, `CONTENTS` will just contain a short comment-line (starting with a semi-colon)
  - `FILE:filename.ext` -- look for `filename.ext` in the the host-specific CFG directory. It is not as automatic as the presets, but it gives flexibility.

  #### Example of use:

         stuff=sreh2_cfg_backup('USER','DEFAULT')
         parse var stuff astatus stuff
         if astatus=0  then do
            ... return error message and exit
         end
         parse var stuff tempfile cfgfile backfile ',' contents
            ... parse contents, modify it, and write results to tempfile
         stuff=sreh2_cfg_backup(,tempfile,cfgfile)
         parse var stuff astatus amess
         if astatus=0 then
            ... return error message and exit
         else
            ... success!

SREH2_AUTO_CHECK_PRIVS: check if access is allowed to a resource
  Check if access to a selector is allowed.

  This is a simple frontend to the SREH2_GET_ATTRIBUTES and SREH2_CHECK_PRIVS procedures.

  `Syntax:
  astat=sreh2_auto_check_privs(seluse,privest,host_nick,id_info)`
  where:

  seluse | the selector (to be compared against `RULES` in attribs.cfg)
  privset | the client's privileges
  host_nick | optional -- host-nickname (will be looked up if not provided)
  id_info | optional, the id-info (will be looked up if not provided)
  astat | Return 1 if access is allowed, 0 if access is not allowed.

SREH2_CHECK_PRIVS: check for required privileges
  Check a `PRIVSET` for required privileges.

  `Syntax:
  astat=sreh2_check_privs(required_privs,privset)`
  where:

  required_privs | a list of privileges to be checked for. Note that there are two kinds of privileges:
  privset | the privileges granted to this user
  astat | Return: 0 for failure or 1 for success (where failure means that the required privileges were not fulfilled by the privileges contained in the privset). Note that two kinds of privileges can be specified in the required_privs: `ONE_OF` and `MUST_HAVE`. ONE_OF: the client must have one of the `ONE_OF` privileges MUST_HAVE: the client must have all of the `MUST_HAVE` privileges. MUST_HAVE privileges are identified by a preceding`&`

  See ACCESS.HTM for more details.

SREH2_CURRENT_SECOND: Return current second
  Returns the absolute time, expressed as fractions of a day since 12:00 AM 1 Jan 1991. Using the default number of digits, the answer is accurate to 1 second.

  If no argument, use current time. Otherwise, use a normal format: such as `24 Sep 2002 21:01:32`.

  Examples:

  - `aa=sreh2_current_second('07 jun 2002 10:35:03')` returns `4175.44101`
  - `baa=sreh2_current_second()` could return `4284.87433`

SREH2_DIGITS: Create a graphical message
  SREH2_DIGITS will produce a text string of URLS that point to the appropriate set of digits.

  `Syntax:
  txtstring=sreh2_DIGITS(amess,digit_name,nd,useframe,nobr,opts,imgdir)`
  See COUNTER.TXT for the details.

SREH2_DATETIME_CONVERT: Convert, using c strftime format,a time-date formatted string

  `Syntax:
  timestr=sreh2_datetime_convert(cvt_string,adate,atime)`
  where:

  cvt_string | a C style strftime format string
  adate | date, in REXX B format (days since 1 January 0001)
  atime | time in hh:mm:ss

  For more details, see TIMEFMT.TXT

SREH2_DYNAMIC_PWD: provide "digest" style authentication to http/1.0 browsers.
  Provide "digest" style authentication to http/1.0 browsers.

  Syntax: `
  res=sreh2_dynamic_pwd(appname,newloc,privset,duration,respfile,nonce,amess,id_info,this_flag,servername,username)`
  where:

  appname | recommended. The "application" calling this. Used to identify a "shared secret". If not specified, a value of DYNPWD is used. Note that APPNAME is also used in responses.
  newloc | optional. The uri to invoke when client hits the submit button. The default value is the Selector that invoked the call to this procedure (typically, the selector that lead to execution of the SREhttp/2 addon that uses dynamic passwords).
  privset | REQUIRED. The space delimited list of "secret" privileges. It should contain an entry of the form `APPNAME:shared_secret`
  duration | Optional. Duration of a timestamp, in days (and fractions of day) (default value is 1 day)
  respfile | Optional. The "response file" that contains javascript and HTML. The response file creates a session key, using the secret password, and sends it to NEWLOC. If not specified, will use DYNPWD.RSP in the filter directory (i.e.; in the SREHTTP2 directory).
  nonce | Optional Extra identifiacation information. This should be unique to a client. If not specified, SREH2_DYNAMIC_PWD will use the client's IP address.
  amess | Optional. A short text string. It will be displayed in emphasized format (you can `amess` for descriptive purposes). Since `amess` is used to report status information, in general we recommend leaving it undefined.
  id_info | Optional. If available, this will speed up processing a bit.
  this_flag | Optional. A flag value. `this_flag` is returned when a special action should be taken. The special action depends on the response file. For example, DYNPWD.RSP uses `this_flag` to signal client requested a relogon (when a checkbox is checked)
  servername | Optional. If not specified, it will be looked up (it is only used for informational purposes).
  username | Optional. If specified, it will be used for informational purposes.
  res | `resp=''` if a valid password was found or `resp='200 '||length(response)` if no valid password is found. sreh2_dynamic_pwd used the respfile to ask the client to supply one -- this is the response code from this response. or `resp=this_flag` if a special action was requested.

  For more details, see DYNPWD.TXT.

SREH2_DYNAMIC_PRIVS: read and write dynamic privileges
  Dynamic privileges are user specific information granted for a short duration.

  Syntax: `
  stuff=sreh2_dynamic_privs(action,aname,avalue,lifespan,id_info)`
  where:

  action | action: one of `LIST, DELETE, WRITE, AUGMENT, READ,` or `RESET`
  aname | a case-insensitive username, or other identifier, identifiying the record where the dynamic privileges are stored. Several special values can also be used: `!IP, !USER,` and `!COOKIE`.
  avalue | the dynamic privileges to store. Used only by the WRITE and AUGMENT actions.
  lifespan | the lifespan, in minutes, of this record. If not specified, a default of 15 minutes is used.
  id_info | Optional, the ID_INFO
  stuff | : Either status information, or a list of dynamic privileges.

  For more details, see DYNPRIV.TXT.

SREH2_FIG_FILE_NAME: determine file name, given datadir or virtual dir

  `Syntax:
  nname=sreh2_fig_file_name(hname,ddir,asel,avirt,arule,vtype,max_repeats,id_info)`
  where:

  hname | optional the host-nickname (or DEFAULT). If not provided, it will be determined.
  ddir | optional the default data directory. If not provided, it will be determined (host-specific value is used if available). This is used only if avirt is not available.
  asel | the selector to map to a file name
  avirt | optional virtual directory
  arule | optional the rule If`avirt=''` and`arule=1`, then avirt and arule will be looked up (in the possibly host-specific ATTRIBS.CFG).
  vtype | optional, not used if avirt and arule are not specifiedl Request type. Either HTML, ADDON,CGI, or UPLOAD. Used if `!types` are used in `avirt`
  maxrepeats | optional, not used if avirt and arule are not specified Maximum number of iterations to perform (re-reads of ATTRIBS.CFG using successive modifications of asel, as can occur due to internal redirection entries in ATTRIBS.CFG). If not specified, the default value is used (as set in SREH2INI.RXX). Note that `0` means only look once. In general, we recommend a value of no more then three.
  id_info | Optional. The ID_INFO, if it is available.
  nname | Returns a candidate fully qualified file name (does not check for its existence). This is the file name (possibly as determined by using a virtual directory) associated with this `asel`.

  Notes on avirt and arule:
  - If used, `avirt` and `arule` are typically selector-specific attributes read from a possibly host-specific `ATTRIBS.CFG` file. For example:

    > `attribs=sreh2_get_attributes(host_nickname,a_selector)
    > parse var attribs realm '01'x arule '01'x redirect '01'x .
    > parse var redirect redirect_type redirect_to
    > redirect_type=strip(translate(redirect_type))
    > avirt=''
    > if redirect_type='DIR' then avirt=redirect_to`

  - If`avirt=''` and`arule=1`, then `avirt` and `arule` will be looked up in a possibly host-specific ATTRIBS.CFG.
  - If `avirt` and `arule` are not specified, then `vtype` and `maxrepeats` are not used.

SREH2_AUTO_FIG_FILE_NAME: determine file name, under datadir or virtual dir
  This is a front-end to SREH2_FIG_FILE_NAME.

  `Syntax:
  fname=sreh2_auto_fig_file_name(asel,ahost,ddir,id_info)
  ` where:

  asel: | selector to map to a file; using possibly host-specific default data directories and virtual directories.
  ahost: | Optional: the host-nickname. If not available, it will be looked up.
  ddir: | Optional, the default data directory. If not available, a possibly host-specific value will be looked up.
  id_info | Optional. The ID_INFO, if it is available.

  Limitations (use sreh2_fig_file_name if these are a problem):

  - looks up static files (uses VTYPE="HTML")
  - three iterations (uses max_repeats=3)

SREH2_FILE_CACHE: Work with the SREhttp/2 file cache

  `Syntax:
  astat=sreh2_file_cache(id_name,params,msec_wait,ownid,action)`
  where:

  id_name | A string that identifies a cache entry. This is often a selector, or a host_nickname:/selector, or a directory name. Note that spaces are stripped from this string before it is used.
  params | Parameters that control how to cache id_name
  msec_wait | Wait time, in milli seconds. Optional -- the default value is 30 seconds (30000). After this many seconds, the procedure gives up (it stops waiting on the cache daemon), and returns an error code.
  ownid | optional. The calling procedure's Own_ID. If you have it, supplying the OWN_ID can speed up processing a bit.
  action | An action to take. Valid actions are:`GET PUT INFO RESET REMOVE` Note that if `action` is not specified, then: if `params` not specified, then a GET is done if `params` is specified: a PUT is done Hence, for GET and PUT (which are usually the only ACTIONS you bother with), you do NOT need to specify ACTION.

  For futher details, see FILE_CSH.HTM

SREH2_FILE_INTREE: search a directory tree for a file

  `Syntax:
  full_file=sreh2_file_intree(filename,topdir,botdir)`
  where:

  filename | the file name to search for (with NO path information) if filename not specified, check for existence of a subdirectory
  topdir | fully qualified directory to start search in it topdir not specified, use current default
  bottom_dir | fully qualified directory to stop searching in. topdir should be a subdirectory of bottom_dir if bottom_dir not specified, search down to the root (x:\) directory

  Example:

        filename='PERL.TXT'
        topdir='f:\www\sre2k\srehttp2\demos'
        botdir='f:\www'
        file_use=sreh2_file_intree(filename,topdir,botdir)
        /* File_use might be F:\WWW\SRE2K\SREHTTP2\PERL.TXT (assuming
           that PERL.TXT is not in f:\www\sre2k\srehttp2\demo). */

SREH2_FIND_SPECIAL: find a !special directive in selector

  `Syntax:
  sp_val=sreh2_find_special(selector_original,lookfor)`
  where:

  selector_original | a selector, typically as it is extracted from the request string.
  lookfor | the `!SPECIAL directive` (or !special command) to lookfor. Note that you do not have to preface `lookfor` with a `!`. Alternatively, `0` == return the `selector` (after stripping off all `!special directives`) `not specified` == if you do not specify the `lookfor` argument, then a CRLF delimited list of the `!SPECIAL` directives (and commands) will be returned. Each line of this lists has the form: `special_directive value` where `value` may be 1 (if the special_directive did not include a `=`)
  sp_val | the value of the `lookfor` !special directive. Returns a blank (a ' ') if there is no such `lookfor` special directive (or command). Alternatively, returns the selector, or the list of !special directives (and commands).

  Example: `sp_val=sreh2_find_special(seloriginal,'ENCRYPT')`

SREH2_FORCE_RESPONSE: invoke a forced-authoriztion request
  Direct the client to re-ask for this resource, with a leading `!FORCE` directive. This is used to force SREhttp/2 to lookup privileges, and to not use cached entries.

  `Syntax:
  newname=sreh2_force_response(seloriginal,servername,port,id_info)`
  where all parameters are optional. If not provided, they will be looked up...

  seloriginal | the original selector.
  servername | the servername to which this request was directed.
  port | the server port to which this request was directed
  id_info | supplying this can speed things up a bit

  Reiterating, if you don't know the values for these three parameters, SREH2_FORCE_RESPONSE will look them up for you (providing you call this as part of an addon, or from an EXEC selector-specific procedure.

  > As an alternative to SREH2_FORCE_RESPONSE, SREhttp/2 addons can return `'PRIVS'`. This causes SREhttp/2 to immediately lookup privileges for this client, and then recall the addon.

SREH2_GET_ATTRIBUTES: Lookup selector-specific attributes
  The fields returned are (with each field seperated by a `'01'x`):

            REALM RULES REDIRECT OPTIONS FAILURE PERMISSIONS REQUIRES

  Note that fields may contain CRLFS.

  Alternatively, you can ask for just one of the above fields; or you can get just the description of the field.

  `Syntax:
  stuff=sreh2_get_attributes(ahost,asel,afield,id_info,attribs)`
  where...

  ahost | The host-nickname to look under. If not specified, the `DEFAULT` host is used.
  asel | The selector to look for.
  afield | The field to return (see above for valid fields). If not specified, then all fields (seperated by a '01'x) are returned.
  id_info | Optional. If available, it can speed up lookups.
  attribs | If specified, should contain attribute information (say as supplied by a prior call to SREH2_GET_ATTRIBUTES). If `attribs` is supplied, then `ahost` and `asel` are not used.

  Note: if ahost and asel do not match a specified selector, then a '' is returned.

  Special features: |
  To return a list of all virtual directories currently defined for a host, you can use: `dirlist=sreh2_get_attributes(host_nickname,'*DIR',,id_info)` A space delimited, URL-encoded (embedded spaces are converted to `%20`) list will be returned. The list can have two parts, seperated by a comma. The first part are the virtual directories defined for this host, the second part (after the comma) are virtual directories defined for the `DEFAULT` host. Each virtual directory has the form: `realm:x:\dir\subdir`. Note that for strictly-superceding hosts, and for the default host, there will never be this second part (since strictly-superceding hosts do not use `DEFAULT` attributes).
  Variant: | Use `dirlist=sreh2_get_attributes(host_nickname,'*DIR2',,id_info)` and each portion of the returned list will be preceded by `host_nickname/` For example, the following could be returned: `JOEHOST/ RLM1:d:\personal\joe1 RLM2:d:\personal\kathy , DEFAULT DRM1:c:\archive`
  To lookup attributes by realm (rather then by selector), use `'*REALM:realmName'` as the `asel` argument, where `realmName` is the realm (from the host with an `ahost` host-nickname) whose attributes you need. For example... `my_attribs=sreh2_get_attributes(host_nickname,'*REALM:FLOOR10',,id_info)`
  To get the description -- use `'DESC'` as the value of `afield` -- just the description is returned. Note that if a `desc` line was not used in the entry describing the the selector-specific attributes, then the description will be ' '.

SREH2_GET_COOKIE: get the value of a cookie
  Check for a cookie (in the request headers).

  Syntax:
  `cookie_value=sref_get_cookie(cookie_name,nth)`
  where:

  - `cookie_name`: the name of the cookie to retrieve
  - `nth`: optional, the nth occurrence of a cookie with this name (default value is 1).
  SREH2_GET_COOKIE will check the request headers for the `nth` cookie with the name given in `cookie_name`. If found, its value will be returned.
  If not found, an empty string will be returned.

  Note that if `nth` is not included, the first matching cookie is used.

  Example | If the request headers contain: `cookie: customer=wiley_coyote` then `sreh2_get_cookie('customer')` will return `wiley_coyote` Alternatively: `cookie: customer=wiley_coyote ; item=bazooka ; customer=roadrunner` then `sreh2_get_cookie('customer',2)` will return `roadrunner`

  #### Notes:

  - cookie_name is case insensitive, but cookie_value is not
  - Restictions: can only be called from an addon. In cgi-bin scripts, use `value('http_cookie',,'os2environment')` instead.

SREH2_GET_MIMETYPE: Return the media type of a file, based on its extension

  `Syntax:
  mtype=sreh2_get_mimetype(asel,ahost,custfirst)`
  where:

  asel | The selector, or file, to determine the mimetype of
  ahost | optional host-nickname, for checking host-specific mimetype mappings. If not specified, check default mimetype mappings.
  custfirst | optional if 1, then check the custom mimetypes first. These are specified in the possibly host specific MIMETYPE.CFG files.

SREH2_GET_USERINFO: read attributes from USERS database
  SREH2_GET_USERINFO returns username-specific information (such as client privileges) that is stored in the (possibly host-specific) USERS.CFG file. It can also do password checking.

  For example ...
  Given that an `Authorization` request header was provided by the client, SREH2_GET_USERINFO can:

  1. extract the username from the `Authorization:` request header
  2. see if an entry exists for this username in a USERS.CFG database
  3. if so, extract the password and privileges for this username
  4. compare the password to the password contained in the `Authorization:` request header (using either the `Basic` or `Digest` method)
  In addition, SREH2_GET_USERINFO supports the CHECK_401 deny access to probing requests test.

  `Syntax:
  stuff=sreh2_get_userinfo(authh,host_nickname,id_info,useronly,chk401,lookfor)`
  where:

  authh | `authh` is typically the value of the `Authorization:` request header (say, as read using SRE_REQFIELD procedure). If `authh` contains an `Authorization:` request header, `SREH2_GET_USERINFO` will compare the username and password (in this request header) to usernames & passwords stored in the USERS.CFG database. If `authh` is not specified, SREH2_GET_USERINFO will try to use the current `Authorization:` request header. If there is no such header, SREH2_GET_USERINFO returns ' '. If `authh` equals `SIMPLE a_username a_password`, SREH2_GET_USERINFO will compare `ausername` and `apassword` to usernames & passwords stored in the USERS.CFG database.
  host_nickname | The host-nickname. If not provided, the current host will be used.
  id_info | optional can speed things up a bit
  useronly | optional If `useronly` equals `1`, SREH2_GET_USERINFO will compare the `username` to usernames stored in the USERS.CFG database. That is, this suppresses password checking.
  chk401 and lookfor | optional If included, SREH2_GET_USERINFO will first check to see that the client does not have several recent access failures. This is meant to impede hackers trying to discover usernames & passwords by repetitively hitting your site. `CHK401 =0` or `CHK401 not specified` means do not perform this test. Otherwise, CHK401 must be two numbers, seperated by a space: `ntimes last_secs` where access is denied (without any attempt at looking up a username and password) if requests for `lookfor` have resulted in 401 responses (access denied) `ntimes` (or more) in the last `last_secs` seconds. Example: `5 300` Note: in general, `lookfor` can be any string that uniquely identifies the user, or the user and the resource. Basically, SREhttp/2 maintains a database using `lookfor` as the record identifier. Thus, if you use a value of `lookfor` that is liable to be used in conjunction with requests by other users (for this or for a similar resource), then CHK401 will be biased (it will accumulate failures over multiple resources and clients).
  stuff | what is returned depends on what happens, and on the value of `CHK401`: If no `Authorization:` header: returns ' ' If no username in USERS database: returns ' ' If password does not match: returns ' ' Otherwise (legitimate match): returns `username password list_of_privileges` If CHK401 is enabled, then the above will always be preceded by: `chk401_status ,` (note the trailing comma) `CHK401_STATUS` will be: `$CHECK_401$` : This means too many recent attempts. ' ' : Nothing before the comma signifies no recent failures `t1 t2 ...` : If there have been `n` recent failures, then n `times of failure` (`day.fraction_of_day`) are returned in space delimited list.

  #### Notes

  - If `authh='!RESET'`, then reread entries from all USERS.CFG files

SREH2_HOSTDATA: update the HOSTDATA information.
  Update the HOSTDATA information.

  HOSTDATA information is stored in the HOSTDATA.CFG file (in the SREHTTP2\CFG directory). HOSTDATA info consists of definitions of hosts, and specifications for "host-specific" parameter files.

  `Syntax:
  foo=sreh2_hostdata()`
  where...
  foo=`1` : ok
  foo= `0 error_message` : a problem occurred

SREH2_HOMEDIR:
  Do a home directory for ~ replacement.

  `Syntax:
  ares=sreh2_homedir(aselector,homedir,host_nickname)`
  where:

  aselector | The string to perform ~ replacement on
  homedir | The home directory. If not provided its value (possibly host-specific) will be looked up
  host-nickname | The host-nickname. Only used to lookup host-specific home directory (if homedir not provided)
  ares | The result, after replacing ~ with the home directory. Note that homedir replacement can involve subdirectories.

SREH2_HTML_COMMENT:bracket a string with HTML comment tokens

  `Syntax:
  newstring=sreh2_html_comment(a_comment)`
  newstring will contain a_comment bracketed by <!-- and -->

  > SREH2_HTML_COMMENT can be useful when used by INTERPRET SSI keyphrases -- it makes it easy to dynamically insert an HTML comment.

SREH2_MAILIT: send e-mail through an SMTP gateway

  `Syntax:
  status=mailit(address_list,message,gateway,sender_address,verbose)`
  where:

  address_list | A space delimited list of addresses
  message | A message string, that may contain CRLFs (and may be rather long)
  gateway | The address of your SMTP gateway server
  sender_address | The sender's address (optional). If sender's address is not given, a name will be generated, using this sites IP address or domain name.
  verbose | Status message verbosity. 0=none, 1=some, 2=many
  status | If success: `1` If a problem: `0 error-message`

  Examples:
  - `status=mailit("BOB@HIS.NET" , "This is my message ", "MAIL.MY.ORG" )`
  - `status= mailit("BOB@HIS.NET JILL@HER.COM " , "This is my message ","MAIL.MY.ORG", "HARRY@MY.ORG")`

  #### Notes:

  SMTP gateway address | If you are using an INTERNET provider, use their POP or SMTP gateway address. If no such address is available, you can try running the OS/2 SENDMAIL program, and use your hostname as the SMTP gateway address. For example, assuming that SENDMAIL.UML is in `c:\mptn\etc`, before starting SRE2003 you should start sendmail with: `C:\>start sendmail -bd -q30m -cc:\mptn\etc\sendmail.uml` or `C:\>start sendmail -bd -q30m -cc:\mptn\etc\sendmail.cf` Alternatively, you can AUTOSTART sendmail using the TCPCFG program.
  recipient addresses | The message will be sent to each address. Since the addresses are sent and then the message, this allows for fairly rapid mailing to multiple individuals.
  SENDER ADDRESS | Some SMTP gateways only accept messages from known users, or from other gateways. Often, they will examine the FROM: field to determine this. So, if you are having problems with message acceptance, you might need to adjust your SENDER_ADDRESS.
  SNDXMAIL.CMD | The `SNDXMAIL.CMD` program (located in the `SREHTTP2\CGI-BIN` directory) demonstrates how SREH2_MAILIT can be used in a standalone program.

SREH2_MULTI_SEND: send a multipart document, or send pieces of a document

  `Syntax:
  res=SREH2_multi_send(message,options,howsnd,fflag,verbose,opthdr,id_info,cprot)`
  where:

  message | a message, or filename, to send
  options | optional header and connection-close options
  howsnd | specifies whether this is a part, or a piece, of a document
  fflag | flag that means "message is a filename"
  verbose | optional verbose status messages
  opthdr | optional additional headers
  id_info | optional the transaction daemon-id, it's use can speed up processing a bit
  cprot | optional clientprotocol. Optional, using it can speed up processing a bit.
  res `(the returned value)` | If blank, no error. Otherwise, a message that starts with 'ERROR '

  For greater details on how to use SREH2_MULTI_SEND, see MULTSEND.TXT

SREH2_QUERYFILE: return the name of a file, possibly relative to a supplied directory

  `Syntax:
  newname=sreh2_queryfile(adir,aname)`
  where:

  adir | optional directory to search for file in. If not specified, search under the `filter directory`
  aname | file name. If fully qualified, `adir` is ignored. Otherwise, look for a file of this name relative to adir (or to the filter directory if adir is not specified).
  newname | The fully qualified file name, or ' ' if such a file does not exist.

  Note: if `adir` specified and `aname` is not specified, then check for the existence of `adir`.

SREH2_PRIVS_CHECK: Compare client-privileges to required privileges
  This will check a list of client-privileges against a list of required privileges. If the client-privileges satisfy the resource privileges, a `1` is returned, Otherwise, a `0` is returned.
  - typically, `client privileges` are provided by the `privset` argument sent to SREhttp/2 addons
  - `resource privileges` are typically specified in a selector-specific `realm` entry in the (possibly host-specific) ATTRIBS.CFG file.

  `Syntax:
  astat=sreh2_check_privs(requires,client_privs)`
  where ...

  - `requires` is a space delimted list of required resource privileges. Note that these required privileges may be `one_of` or `must_have` required privileges.
  - `client_privs` is a list of client privileges. Note that these may contain secret privileges.

SREH2_RESET: Reset SREhttp/2
  SREH2_RESET will reset SREhttp/2 -- all configuration files will be re-read.

  `Syntax:
  astat=sreh2_check_privs(id_info,mode)`
  where both arguments are optional...

  - `ID_INFO` is the ID_INFO parameter
  - `mode` controls output:
    - not specified : After resetting, SREH2_RESET will return a short response to the client. A two word message (containing # bytes sent and response status) is returned.
    - 1 : SREH2_RESET will not respond to the client. A several line message, noting what was reset, is returned.
    - 2 : Quiet mode: no response to the client, a '' is returned

SREH2_Text2HTML: Convert text into HTML friendly text.
  This will convert plain-vanilla text into it's equivalent HTML format.

  `Syntax:`
  `htm_string=sreh2_text2html(txt_string,dobr,docurly)`
  Where:
  `if dobr=1, add a <BR> after every line
  if docurly=1, convert { and } to < and >`

  SREH2_Text2HTML will ...

  - convert &,>,<, and " to their &xx; html entity equivalents
  - convert tabs to three &nbsp; 's
  - add <P> (to replace blank lines),
  - add <A href=".." > elements whenever a URL is detected (a word with more then one period),
  - add characters when tabs (or leading spaces) are found.
  - if line begins with n > 3 spaces, replace them with n-2 &nbsp; 's
  - optionally add <BR> to end of lines, and convert { and } to < and >

SREH2_SET_COOKIE: Add a Set-Cookie: response header

  The Set-Cookie: response header sets a cookie that the browser should return (on later requests) to this site.

  `Syntax:
  astat=sreh2_set_cookie(cname,avalue,duration,path,domain,replaceit)`
  where ...

  - `cname`: (required) the name of the cookie.
  - `avalue`: (required) the value of the cookie
  - `duration`: (optional) the duration. By default, in fractions of a day. Or, as with SRE_GMT, you can specify seconds, minutes, or hours (i.e.; `10s, 30m, 2h`).
  - `path`: (optional) path is used to specify the subset of URLs in a domain for which the cookie is valid
  - `domain`: (optional) when deciding whether to send a cookie, the browser matches domain against the tail of the fully qualified domain name of the host.
  - `replaceit`: If 1, then replace any existing `Set-Cookie` header. Otherwise, retain any existing `Set-Cookie` header.

    For the cookie spec, see http://wp.netscape.com/newsref/std/cookie_spec.html

SREH2_VALUE: lookup a SREhttp/2 value
  Read (or write) srehttp/2 environment variables -- these can be host-specific.

  `Syntax:
  retval=sreh2_value(varname,newval,hostname,id_info,force_host)`
  where ...

  varname | the variable name (to be read or written)
  newval | the value to write. If empty, then read the current value of `varname`
  hostname | Optional. look for host-specific value, using this host nickname
  id_info | Optional. id_info. `id_info` is used only if you do not specify hostname
  force_host | Optional. if 1, then force a lookup for a host-specific parameter. If `varname` has not been defined, then return ' '. In a sense, `FORCE_HOST` means treat the host as if it were strictly-superceding
  retval | the current value (if newval specified, retval will equal newval)

  #### Notes:

  - A hostname of `DEFAULT` means check default parameters, and do not check host-specific parameters
  - SREH2_VALUE uses SRE_VALUE and the SRE environment, using variable names that start with `H2_`
  - Hint: `SSI REPLACEment` strings are also stored using SREH2_VALUE -- using the convention that their names start with REP_ (so, the SRE value starts with `H2_REP_`)

SREH2_VERSION: The SREhttp/2 version number
  SREH2_VERSION returns either the SREhttp/2's version number, or name.

  `Syntax:
  a_version=sreh2_version()`
  This returns the version number. Version numbers have the format:
  `Nmmbbb`
  where `N` is the major version number, `mm` is the minor version number, and `bbb` is the build number.

  Example: Major version 1, minor version 11, build 423: `112103`

  Or, to return the version name, you can use: `version_name=sreh2_version('NAME')`.
  This returns the same value as SRE_SERVER('F').
  `version_name` will have the format:`SREhttp/2 ver 1.12d`.
  Note that the `d` corresponds to the `4` in the build number (you can think of it as the "major build number").

SREH2_XBITMAP: Create a XBITMAP image
  SREH2_XBITMAP will produce a "xbitmap" (black and white) image of a string of digits, and return it to the client.

  `Syntax:
  rcode=sreh2_XBITMAP(amess,digit_name,nd,useframe,nobr,opts,imgdir)`
  Note that `rcode` is a response code (as returned by SRE_COMMAND).

  See COUNTER.TXT for the details.

Updated 27 September 2002