Server Settings Backend plugin for SquirrelMail
===============================================
Ver 2.0, 2010/06/11


Copyright (c) 2008-2010 Paul Lesniewski <paul@squirrelmail.org>



Description
===========

This plugin provides a highly configurable way for other
plugins to retrieve and store settings outside of SquirrelMail.
Values stored in a SQL database, in LDAP, on a server accessed
by FTP, or on the local file system (usually accessed via a
set-uid wrapper that allows read and write access to files not
owned or normally accessible by the web server) are all supported.
Even the credentials used for accessing the settings (for example,
the FTP login username and password) can be recursively passed
back to this plugin so that they themselves can be looked up in
any one of the supported data stores.

This plugin serves as a library for other plugins (not
necessarily just the Server Settings plugin), thus it never
needs to be activated itself.  Merely download and unpack it
in the SquirrelMail plugins directory and no more.



License
=======

This plugin is released under the GNU General Public 
License (see the file COPYING for details).



Donations
=========

If you or your company make regular use of this software,
please consider supporting Open Source development by
donating to the authors or inquire about hiring them to
consult on other projects.  Donation/wish list links for
the author(s) are as follows:

Paul Lesniewski: https://squirrelmail.org/donate_paul_lesniewski.php



Requirements
============

  * SquirrelMail version 1.4.0+
  * When not using SquirrelMail 1.5.2+, Compatibility
    plugin, version 2.0.14+
  * Pear DB and/or MDB2 package if using the SQL database backend
  * FTP support in PHP if using the FTP backend
  * unixODBC and unixODBC-devel if using the Local File backend
    with the set-UID (SUID) access type to access account data
    stored in a database (these packages are commonly found in
    most *nix flavor operating system package repositories such
    as yum, apt-get, etc.) (if making lookups to a MySQL database,
    the mysql-connector-odbc package is also needed)



Help Requests
=============

Before looking for help elsewhere, please try to help yourself:

  * Read the Troubleshooting section herein.

  * Look to see if others have already asked about the same issue.
    There are tips and links for the best places to do this in
    the SquirrelMail mailing list posting guidelines:
    http://squirrelmail.org/wiki/MailingListPostingGuidelines
    You should also try Google or some other search engine.

  * If you cannot find any information about your issue, please
    first mail your help request to the squirrelmail-plugins
    mailing list.  Information about it can be found here:
    http://lists.sourceforge.net/mailman/listinfo/squirrelmail-plugins
    You MUST read the mailing list posting guidelines (see above)
    and include as much information about your issue (and your
    system) as possible.  Including configtest output, any debug
    output, the plugin configuration settings you've made and
    anything else you can think of to make it easier to diagnose
    your problem will get you the most useful responses.  Inquiries
    that do not comply with the posting guidelines are liable to
    be ignored.

  * If you don't get any replies on the mailing list, you are
    welcome to send a help request to the authors' personal
    address(es), but please be patient with the mailing list.



Troubleshooting
===============

  * Note that if any recursive configuration items (that you may
    use a separate lookup to retrieve if needed) are misconfigured
    (such as the DSN for a SQL database-backed setting), they may
    produce seemingly unrelated error messages, such as "lookup
    unknown".  Make sure to double-check all syntax when configuring
    this plugin.

  * If you are getting errors due to the lack of FTP support in
    your PHP build (which is required if you use any FTP-backed
    settings), you'll need to recompile PHP with the --enable-ftp
    option.  For more information, see:

       http://www.php.net/manual/ref.ftp.php

  * If you are getting errors due to the lack of a database
    abstraction layer, make sure you have either NOT configured
    one in your configuration or that if you have, you have
    not mistyped it.  Currently, only DB and MDB2 are suppored.
    See the SQL configuration section, DATABASE_ABSTRACTION_LAYER
    item.

  * If you are getting errors due to the lack of Pear DB or MDB2
    library on your system (one of which is required if you use
    any SQL database-backed settings), try typing "pear install DB"
    or "pear install MDB2" on your command line (only one is
    necessary depending on which abstraction layer you have
    chosen - if you have NOT chosen one specifically, the plugin
    defaults to using DB).  For more information, see:

       http://pear.php.net/manual/en/installation.php

  * If you are using MDB2 as your database interface API and 
    are getting an error such as "MDB2 Error: not found" (make
    sure to turn on debugging to see more error details), then
    make sure you have the correct driver installed for your
    database as well.  For example, when using MySQL, you'll
    need to do a "pear install MDB2#mysql" or
    "pear install MDB2_Driver_mysql".  If errors persist,
    (such as "pear/MDB2_Driver_XXX requires php extension XXX"),
    please consult the MDB2 FAQ:

       http://pear.php.net/manual/en/package.database.mdb2.faq.php

  * If changes to the configuration file don't seem to be having any
    effect, ensure that there are not two Server Settings Backend
    configuration files, one in the server_settings_backend directory
    and one in the main SquirrelMail config directory (named
    "config_server_settings_backend.php").  The one in the main
    SquirrelMail config directory will always override the one in the
    server_settings_backend directory.

  * When having trouble setting up the Local File set-UID (SUID)
    access type, use the SUID_DEBUG_OUTPUT setting and make sure the
    SUID backend program was configured with --enable-debug and
    --disable-silent.  When asking for help, you should provide all
    such output related to your issues.  There is also a list of most
    every possible SUID backend error message (along with tips of
    what to do about them) in the SUID_BACKEND_ERRORS file that is
    found alongside this document.

  * Another way to debug the set-UID (SUID) program is to run it
    manually from the command line:

    $ squirrelmail_server_settings_suid_backend localhost username file_exists <some file>

    It will then wait on the next line for you to input the "master"
    username if so configured, then on the next line for the "master"
    password if so configured, and on the next line for the password
    that belongs to the corresponding user.  For more accurate testing,
    you can prepend a sudo command, executing it as the same user that
    your web server runs as (replace "apache" with whatever is
    appropriate on your system):

    $ sudo -u apache squirrelmail_server_settings_suid_backend localhost username file_exists <some file>

    If no error messages are output, then you can check the return
    code (see the list above):

    $ echo $?

  * If you are receiving error 127 when using the Local File set-UID
    (SUID) access type, you may have PHP settings that prohibit or
    limit how PHP can create command pipes.  Please check your PHP
    configuration or any SELinux restrictions that may be in place.

  * When using the vacation_initialize() function (see the "Vacation
    API" section in this document), similar PHP (or SELinux) issues
    (see above) may affect the correct operation of the
    initialization command.  If not using the Local File set-UID
    access type, also check that you can correctly run the vacation
    command as the user that runs your web server.

  * If you are using the FTP backend and files are not being created
    with the correct umask (for example, some MTAs will refuse to
    read vacation files with group write permission), check your FTP
    server configuration.  For WU-FTP, the needed change is
    reportedly as such:  edit /etc/ftpaccess and change the
    "defumask" line to read:

       defumask 022 all

    Then restart the FTP server.

  * Always remember to check the configtest output when testing and
    diagnosing problems:

       http://example.com/squirrelmail/src/configtest.php



Security
========

FTP Issues
----------

When using the FTP backend, there are no security risks added by
this plugin in particular, however, it is suggested that if your
users do not otherwise need FTP access themselves, you configure
your FTP server so that it is only available on the interface or
address needed so that the web server (thus this plugin) can access
it (probably "localhost" if the web server and FTP server are
running on the same machine).  Leaving FTP servers - especially
ones that are entirely unencrypted - running on public-facing
interfaces/ports can lead to system break-ins.

If at all possible, it is recommended to use a FTP server that
supports SSL-FTP and turn on the "SSL" setting in the configuration
array.  To do this, you'll also need to have SSL support in PHP.
For more information about PHP support for SSL, see:

http://php.net/manual/ref.openssl.php

Local File - PHP Access Issues
------------------------------

When using the Local File backend, several security issues come
up.  Using the PHP access type requires that the web server have
read and write access to all files being accessed.  If you decide
to use this method, you'll need to do something similar to the
following for each and every user "home" directory (substitute
the correct user under which your web server runs if it is not
"apache" as below):

   # chgrp -R apache /home/marc
   # chmod -R g+rw /home/marc

This means that the files in users' "home" directories could
conceivably be accessed by any other scripts or applications being
run by the web server, whether or not they are under your control
(particularly a concern in a shared hosting environment).  This
also opens up the possibility that an attacker who manages to upload
malicious code to your web server could gain access to your users'
private data.  And generally speaking, enforcing the needed
permissions is labor intensive or requires special scripts or
setup attention from the administrator, since the web server does
not usually have this kind of access by default.

Local File - Set-UID (SUID) Access Issues
-----------------------------------------

To overcome the difficulties and security risks of the PHP file
access method, a set-UID (SUID) access method is provided.  If you
are prepared to configure and compile the SUID backend program, it
is generally more robust and does not require that you change the
ownership or any permissions of your users' files.  However, this
method is not without its security concerns and setup tasks.  Its
configuration, compilation and setup are covered in detail in the
"Set-UID Configuration" section elsewhere in this document.

You should understand that even if there are no known bugs in the
SUID backend program, just the act of using a set-UID program that
allows the web server to operate with super-user privileges is
inherently risky.  More concrete risks are detailed as follows.

The primary security concern of using this access method is that
the SUID backend program will be executable by any other script
or program being run by the web server, whether or not they are
under your control (particularly a concern in a shared hosting
environment).  Under most configurations, this could allow others
(including any attacker who is able to upload malicious code to
your web server) to make unlimited password guesses for your users,
and upon succeeding, they would have unrestricted access to the
compromised users' data.  Under other, more unusual configurations
(discussed below), full system access might be attainable.

However, to combat this, the SUID backend program supports an extra
feature that requires the caller to first authenticate with
additional "master" credentials.  It is strongly recommended that
you enable this feature (--enable-master-security-check), using a
special-purpose "master" account that is not otherwise used.  Note
that the "master" account need not be a real user, and the lookup
mechanism for it can be different than that of your normal users.

If you follow the instructions in the "Set-UID Configuration"
section to specify the "master" account in the web server's
configuration such that only the root user can read the needed
credentials and they are only made available to scripts running
in the SquirrelMail directory, this will block access to the SUID
backend for any other scripts or programs being run by the web
server.

The SUID backend program provides a great deal of flexibility for
those who need it, but there are several configuration options
that are strongly discouraged since they have the possibility of
creating security issues.  Options to avoid (as well as a couple
you should make use of) if at all possible are as follows.

--disable-authorized-invoker should be avoided.  If you do not know
what user your web server runs as, you should find out instead of
using this option.  Allowing any user to run the SUID backend program
could give users the ability to try to password-guess each others'
accounts and subsequently gain access to unauthorized data.

--enable-root-allowed should be avoided.  The root user should never
be used in any web applications.  Email for the root user should be
forwarded to another account.  Allowing the root user to access the
SUID backend program isn't necessarily risky in and of itself, but
any misconfigurations or malicious code can have disasterous
consequences in this context.

--enable-empty-passwords should be avoided.  It is never a good idea
to have accounts without passords, and allowing access to such
accounts via the SUID backend program could allow anyone to access
such users' data.

--enable-min-uid should be used.  A common value for it is 500, but
check your system.  In virtual user environments, this value might
be much higher.  This will lock out access to most system accounts
through the SUID backend program.

--enable-master-root-allowed should be avoided at all costs.  Letting
the web server have the root password in any context is a very bad
idea.

--enable-master-empty-passwords should be avoided.  While there is
still a good level of security in requiring a password-less "master"
account authentication for the SUID backend program, there is no
reason why it cannot make use of an account with a password.

--enable-remote-subdirectories-ok should be avoided if you are only
using the SUID backend program to access files in the top level of
users' "home" directories.  This prevents mailicious use of the
program against data it shouldn't need access to in the first place.

--enable-remote-filenames-dot=require should be used if possible.
It is a good idea to restrict access to as few a number of files as
possible.  If you use the SUID backend program to provide access to
users' filter or forwarding files, chances are you may be able to
use this option as suggested.

Even better than the previous option, --enable-remote-file-prefix
or --enable-remote-file-custom-prefix are excellent ways to reduce
the access allowed to callers of the SUID backend program.  Use these
if at all possible.

--disable-remote-chars should be avoided.  If you need to allow
access to unusual file names, you should use --enable-remote-chars
instead.

--enable-local-file-prefix should be used.  The value for this
setting is usually the path to the SquirrelMail attachments
directory.  This ensures that someone cannot attempt to transfer
user files to any other location.

--enable-file-manager and --enable-vacation should be avoided unless
you specifically need file manager or vacation functionalities.
Don't open up functionalities and potential avenues for attack if
you don't need them in the first place.

--enable-override-home-dir should be avoided.  The only reason to
use this is if the data users are accessing through the SUID
backend program is the same for everyone.  If that does not apply
to your needs, don't use this option.  If you do use it, don't give
it a value of something such as "/".

--enable-override-uid and --enable-override-gid should be avoided
for much the same reasons as the previous option.  This may very
well causes errors in most circumstances.  If you do use one of
these options, don't give a value such as 0.



Configuration
=============

Configuration for this plugin is found in whatever plugin uses
the libraries herein.  Thus, the following serves as a guide for
the parts of those plugins that concern themselves with how to
access settings stored outside of SquirrelMail.  Please refer to
the plugin documentation for any plugin that uses this one, as
the location and format of these configuration settings might be
slightly different therein.  The Server Settings plugin is one
example of a plugin that makes use of this one.

Typically, *each* setting that is found outside of SquirrelMail
needs an array of settings to help identify it and how it is
accessed outside of SquirrelMail.  Each setting has its own
configuration so that you can simultaneously access settings on
any number of different backend systems.  However, it is possible
that a plugin that makes use of this one will simplify the
configuration process by using the same storage backend settings
for all settings.  What configuration values are needed depends
upon the backend in which the setting is located.

SQL Backend
-----------

Here is an example of some of the most common (and required,
except the "USERNAME" element) configuration values when using
the SQL database backend:

array(
   'BACKEND'    => 'sql',
   'DSN'        => array('VALUE' => 'mysql://username:password@localhost/database'),
   'QUERY'      => array('VALUE' => "SELECT color FROM favorites WHERE username = '%1@%2'"),
   'SAVE_QUERY' => array('VALUE' => "UPDATE favorites SET color = '%3' WHERE username = '%1@%2'"),
   'USERNAME'   => array('STRIP_DOMAIN' => 1),
)

The "BACKEND" key is what dictates where the setting is located (in
a SQL database?  In LDAP?  Etc....) and what other configuration
items are needed.  If, however, you specify a key called "VALUE",
its corresponding value will be immediately used instead of making
any backend lookups - this allows you to hard-code a configuration
item.  Another key called "CUSTOM" allows you to specify an external
PHP function name that will be consulted for obtaining the desired
setting.  That function must be provided by you, and is expected
to return the setting value.  It will be given the entire contents
of the configuration array as its one and only argument, so it can
make use of any custom information you might add to it.  If "VALUE"
is specified, it always takes precendence.  If "CUSTOM" is
specified, it gets second priority before falling back on "BACKEND"
to make a standard lookup.

   'VALUE'  => 'xyz',
   'CUSTOM' => 'my_lookup_function',

The "DSN" is what indicates how to connect to the database.  For
information about the syntax of the DSN, please see:

   http://pear.php.net/manual/en/package.database.db.intro-dsn.php
   http://pear.php.net/manual/en/package.database.mdb2.intro-dsn.php

By default, the database abstraction layer used by this plugin is Pear
DB, however, if you'd like to use MDB2 instead, you can specify a
different "DATABASE_ABSTRACTION_LAYER"

   'DATABASE_ABSTRACTION_LAYER' => array('VALUE' => 'MDB2'),

The "QUERY" key contains the SQL command for how to retrieve the setting's
value.  The "SAVE_QUERY" contains the SQL command for how to update the
value in the database.  You should be SURE that any and all SQL commands
are enclosed in DOUBLE quotes, or lookups are liable to break or become
exposed to security exploits.  Before either of these commands is used,
the following substitutions will be made, if found within them:

   %1  The username of the user who is currently logged in
   %2  The domain of the user who is currently logged in

The "SAVE_QUERY" has one additional substitution that is made:

   %3  The new value being sent to the database

The "USERNAME" key is used to perform manipulations on the current user's
username before using it in the SQL commands.  It should conatin an array
of username manipulation configuration rules.  It is optional, and if not
specified, the username will be used as is.  Please refer to the
Credentials Manipulations section elsewhere for the available "USERNAME"
configuration items.

When working with settings that contain multiple values (say, for example,
a whitelist of email addresses), there are a few choices with how those
values will be stored in the database.  They can be compressed into a
single encoded string or stored in multiple database entries.  In order
to compress the values into one string, you may add the following to your
list of configuration items:

   'MULTIPLE' => ',',

This will create a comma-separated-values format from the values and
store them all in one database entry.  Instead of a comma, you may
use anything you like, but you must consider that if your actual values
contain the same character(s), your data will become corrupt.  Instead,
you can use PHP serialization (which will not suffer from such
corruption, but does require slighly more storage space in your database,
so please make sure your column size is big enough) by specifying:

   'MULTIPLE' => 'SERIALIZE',

If, on the other hand, you want to store your value in multiple
database rows (perhaps your whitelist table is designed to have
a two-column primary key of username and (single) whitelist
address), you can specify:

   'MULTIPLE' => 'MULTIPLE',

If your "QUERY" returns multiple rows but you have not specified
anything for the "MULTIPLE" configuraion, values will be compressed
using PHP serialization, but you should always use the "MULTIPLE"
configuration to avoid problems.

Note that when storing multiple rows, using the SQL UPDATE command
will not produce the desired results.  Instead, you'll want to use
the SQL INSERT command.  However, this leaves no way to remove old
values from the database when updating them.  In this case, you can
specify a special "PRE_SAVE_QUERY" SQL command that will be run once
before your data is saved (it will also be subject to the same "%1"
and "%2" substitutions as the normal "QUERY"):

   'PRE_SAVE_QUERY' => array('VALUE' => "DELETE FROM whitelist WHERE username = '%1@%2'"),
   'SAVE_QUERY'     => array('VALUE' => "INSERT INTO whitelist (username, sender) VALUES ('%1@%2', '%3')"),

Likewise, when SquirrelMail needs to test a setting if it matches a
certain value, the "QUERY" command will work perfectly fine for a
scalar setting, but not very efficiently for a setting that contains
multiple values.  In order to reduce the load on your database and
make such lookups generally much more efficient, you may optionally
specify as special "TEST_QUERY":

   'TEST_QUERY' => array('VALUE' => "SELECT sender FROM whitelist WHERE username = '%1@%2' AND sender = '%3'"),

The substitutions for "TEST_QUERY" are the same as for "SAVE_QUERY".
Note that you'll want to be careful to select the value of the target
row and column because the code test will still be done against the
test value ("%3") as if it were a scalar value.

The "PRE_SAVE_QUERY" can also be used in scalar contexts:  consider
a scenario where a string or integer setting may *not* already be in
the target database table for all users.  "SAVE_QUERY" would therefore
need to use the INSERT syntax, but subsequent save actions would
trigger errors (or corrupt the database) because the query should use
UPDATE instead.  Here, too, the solution is to specify a
"PRE_SAVE_QUERY" with the same DELETE syntax as shown above:

   'PRE_SAVE_QUERY' => array('VALUE' => "DELETE FROM preferences WHERE username = '%1@%2' AND preference_name = 'favorite_color'"),
   'SAVE_QUERY'     => array('VALUE' => "INSERT INTO preferences (username, preference_name, value) VALUES ('%1@%2', 'favorite_color', '%3')"),

Other solutions that could be used in the preceeding scenario are
a REPLACE query in the "SAVE_QUERY" (and no "PRE_SAVE_QUERY" needed),
an INSERT... ON DUPLICATE KEY UPDATE query in the "SAVE_QUERY" (and
again, no "PRE_SAVE_QUERY" needed) or a conditional insert action
such as INSERT... SELECT... FROM DUAL WHERE NOT EXISTS (SELECT...)
subquery in the "PRE_SAVE_QUERY" along with a standard UPDATE in the
"SAVE_QUERY" (some of these options may not be supported in the
database being used; check your documentation).  Some examples of
these can be found in the SQL example configuration file for the
Server Settings plugin.

When a value is being added to a multi-value setting (see the Usage
Guide below regarding the arguments (specifically, $add) to
put_server_setting()), it may in some cases be more efficient to use
a different kind of SQL command to just add that one value (and also
avoid executing the "PRE_SAVE_QUERY").  For any settings that have
the "MULTIPLE" item configured, they may specify an "ADD_QUERY" that
does exactly that.

   'ADD_QUERY' => array('VALUE' => "INSERT INTO whitelist (username, sender) VALUES ('%1@%2', '%3')"),

In this exmple, the command syntax is no different, but the command
is run only once for the new value and "PRE_SAVE_QUERY" will be
skipped, boosting performance just a touch.  The substitutions for
"ADD_QUERY" are the same as for "SAVE_QUERY".

Likewise, when removing a value from a multi-value setting (again,
see the Usage Guide below regarding the arguments (specifically,
$remove) to put_server_setting()), you may use a special
"REMOVE_QUERY" item that will be used to remove just the one value
in question and avoid running the "PRE_SAVE_QUERY".  The
substitutions for "REMOVE_QUERY" are the same as for "SAVE_QUERY".

   'REMOVE_QUERY' => array('VALUE' => "DELETE FROM whitelist WHERE username = '%1@%2' AND SENDER = '%3'"),

For values that are stored in a single database row (single value
settings or multiple value settings compressed into one (using
serialization, comma-separated-values format, etc.)), it is also
possible to delete the setting from the database altogether when
its value is empty.  To do this, the "REMOVE_QUERY" must be specified
along with "DELETE_WHEN_EMPTY".  Set "DELETE_WHEN_EMPTY" to 1 to
enable this behavior or 0 (zero) to leave the empty setting in the
database (which is the default behavior when this element is not
present).  Note that the "REMOVE_QUERY" here will not usually be
the same query as when it is being used with multiple-value settings
(see above).

   'DELETE_WHEN_EMPTY' => array('VALUE' => 1),
   'REMOVE_QUERY'      => array('VALUE' => "DELETE FROM whitelist WHERE username = '%1@%2'"),

You'll notice that some of the configuration settings are embedded in
sub-arrays with the key "VALUE".  These (specifically, any of "DSN",
"DATABASE_ABSTRACTION_LAYER", "QUERY", "SAVE_QUERY", "PRE_SAVE_QUERY",
"TEST_QUERY", "ADD_QUERY", "REMOVE_QUERY" or "DELETE_WHEN_EMPTY") can
all themselves be the subjects of separate backend lookups.  If, for
example, you need to look up the DSN for a certain setting, you can do
something like this:

array(
   'BACKEND'    => 'sql',
   'DSN'        => array(
                      'BACKEND'  => 'sql',
                      'DSN'      => array('VALUE' => 'mysql://username:password@localhost/database'),
                      'QUERY'    => array('VALUE' => "SELECT dsn FROM client_databases WHERE domain = '%2'"),
                   ),
   'QUERY'      => array('VALUE' => "SELECT color FROM favorites WHERE username = '%1@%2'"),
   'SAVE_QUERY' => array('VALUE' => "UPDATE favorites SET color = '%3' WHERE username = '%1@%2'"),
   'USERNAME'   => array('STRIP_DOMAIN' => 1),
)

LDAP Backend
------------

Not yet implemented.

FTP Backend
-----------

Here is an example of some of the most common configuration
values when using the FTP backend:

   'BACKEND'              => 'ftp',
   'HOST'                 => array('VALUE' => 'localhost'),
   'MODE'                 => array('VALUE' => 'BINARY'),
   'DIRECTORY'            => array('VALUE' => '.spamassassin'),
   'FILE'                 => array('VALUE' => 'user_prefs'),
   'PARSE_PATTERN'        => array('VALUE' => "/^required_score\s+(.*)$/m"),
   'PATTERN_GROUP_NUMBER' => array('VALUE' => 1),
   'NEW_SETTING_TEMPLATE' => array('VALUE' => "required_score  $1\n"),
   'DELETE_WHEN_EMPTY'    => array('VALUE' => 1),
   'MAX_SEQUENTIAL_EMPTY_LINES' => 3,
   'TREAT_AS_EMPTY_WHEN_NOT_FOUND' => array('VALUE' => 1),

The minimal required elements are: "BACKEND", "HOST", "FILE",
"PARSE_PATTERN" and "NEW_SETTING_TEMPLATE".

The "BACKEND" key is what dictates where the setting is located (in
a SQL database?  In LDAP?  Etc....) and what other configuration
items are needed.  If, however, you specify a key called "VALUE",
its corresponding value will be immediately used instead of making
any backend lookups - this allows you to hard-code a configuration
item.  Another key called "CUSTOM" allows you to specify an external
PHP function name that will be consulted for obtaining the desired
setting.  That function must be provided by you, and is expected
to return the setting value.  It will be given the entire contents
of the configuration array as its one and only argument, so it can
make use of any custom information you might add to it.  If "VALUE"
is specified, it always takes precendence.  If "CUSTOM" is
specified, it gets second priority before falling back on "BACKEND"
to make a standard lookup.

   'VALUE'  => 'xyz',
   'CUSTOM' => 'my_lookup_function',

The "HOST" is what indicates the address of the FTP server.  "MODE"
indicates the transfer mode of put and get operations and must be
either "BINARY" or "ASCII" (if not specified, "BINARY" is assumed).
"DIRECTORY" specifies the directory where the target file is found,
and if not given, the target file is assumed to be in whatever
directory the FTP server places you in upon login.  "FILE" must
contain the name of the target file.

Some other settings (not shown in the example above) that may be
specified are: "PORT", which indicates the port to be used when
connecting to the FTP server (when not given, 21 is assumed).
"SSL" can be used to indicate that the FTP connection should be
done over SSL (note that this is not the same as the SFTP
protocol; to use SSL-FTP, you need to have SSL support built into
your PHP build).  To enable, it should be 1; when not given,
0 (zero) is assumed, which disables SSL-FTP).  "PASSIVE" can be
used to indicate the status of passive mode FTP connections (1 = on,
0 (zero)= off; default is off).

   'PORT'    => array('VALUE' => 990),
   'SSL'     => array('VALUE' => 1),
   'PASSIVE' => array('VALUE' => 1),

For information about enabling SSL in PHP, see:

   http://php.net/manual/ref.openssl.php

"TREAT_AS_EMPTY_WHEN_NOT_FOUND" specifies whether or not, when the
file is not found, the setting value should be assumed to be
empty/null (if this is the desired behavior, set to 1).  If not
specified, 0 (zero) is assumed, and an error will be caused whenever
a setting's target file is not found on the FTP server.

"DELETE_WHEN_EMPTY" indicates that when nothing but whitespace
is left in the target file (perhaps due to the user deactivating
some setting(s) or clearing a text field), it should be deleted.
Set to 1 to enable this behavior or 0 (zero) to leave the empty
file on the server (which is the default behavior when this
element is not present).

"NEW_SETTING_TEMPLATE" is used to add a setting to its target
file when it is not yet present therein.  The value given here
is, after making the substitutions noted below, added to the
end of the target file.  Before it is used, the following
substitutions will be made, if found:

   %1  The new setting value that is being added
   %u  The current user's parsed (per the USERNAME rules
       explained elsewhere in this document) user name
   %d  The current user's parsed (per the DOMAIN rules
       explained elsewhere in this document) domain name

When "NEW_SETTING_TEMPLATE" is used to add setting(s) to the
target file, they are added to the end of the file by default.
If they should be added to the top of the file instead, use the
"ADD_TO_TOP" entry:

   'ADD_TO_TOP' => array('VALUE' => 1),

Because FTP is a file-level protocol, once the file is downloaded,
the setting value needs to be picked out of that file.  To provide
maximum flexibility, "PARSE_PATTERN" can be given as any regular
expression needed to do just that.  For example, to pull a setting
that occurs on just one line that starts with the setting name
and then some whitespace before the setting value, this would do:

   'PARSE_PATTERN' => array('VALUE' => "/^required_score\s+(.*)$/m"),

Note that the example above works as expected even when the
setting may appear multiple times in the file on more than one
line (as long as MULTIPLE is set correctly).  When the entire
file is needed, this pattern is appropriate:

   'PARSE_PATTERN'  => array('VALUE' => "/^(.*)$/s"),

Note that the setting's value is indicated in these examples by
being enclosed in parenthesis.  In more complex patterns, more
that one set of parenthesis may be required, so in order to
indicate which set of parenthesis contains the needed value, use
"PATTERN_GROUP_NUMBER", which should contain the sequential
number of the desired parenthesis set, counting opening parenthesis
from left to right.  In the two examples above, we want the first
parenthesis pair:

   'PATTERN_GROUP_NUMBER' => array('VALUE' => 1),

If "PATTERN_GROUP_NUMBER" is not given, 0 (zero) is assumed,
which takes the *entire* pattern match.  For more information
about how to design your own custom regular expressions, see:

   http://php.net/manual/reference.pcre.pattern.syntax.php
   http://php.net/manual/reference.pcre.pattern.modifiers.php

One important thing to keep in mind about the "PARSE_PATTERN"
is that it is both used as explained above to pick out a
setting's value from a file, as well as to identify what should
be removed from a file when the setting is being removed (perhaps
the user deselected the setting) or when it is being removed
and then re-added to the file later (which usually only applies
to when multiple-value settings are being reshuffled).  In some
cases, it is desirable to keep some small amount of the setting
in the file after it has been removed (maybe set it to some
default value or keep a placeholder).  You may accomplish
this using the "DELETE_KEEP_PATTERN" element, which is used as
the replacement portion of the regular expression search and
replace operation that is performed on the file.  You can use
any features of normal regular expression replacement strings,
such as back references ($1, $2, etc.), as explained in the PHP
regular expression manual.  Additionally, if "%u" or "%d" are
found in "DELETE_KEEP_PATTERN", they are replaced with the pre-
parsed username and domain before the regular expression is
executed.  If "DELETE_KEEP_PATTERN" is not given, it is not used
and the entire "PARSE_PATTERN" match is removed from the file
when appropriate.

   'DELETE_KEEP_PATTERN' => array('VALUE' => '$3 = none'),

When removing (and possibly replacing) lines from the target file,
the "PARSE_PATTERN" might leave behind extra unwanted blank lines,
gradually growing the file out of control with empty space.  To
avoid this, you may set "MAX_SEQUENTIAL_EMPTY_LINES" to the maximum
allowable number of blank lines in the file.  Any more will be
removed.  Note that this setting will affect the entire file, and
not just the lines that were removed for this setting.  If set to
0 (zero), no blank line replacement will be attempted.  Also note
that this entry is NOT a lookup item like the other entries
described here - it is specified only as a simple integer value.

   'MAX_SEQUENTIAL_EMPTY_LINES' => 1,

In some limited circumstances, you may want to remove a given value
from a file on the backend when a save action is occuring.  This
usually only applies when a secondary save action is in use (see
the Advanced Usage section elsewhere and refer to the example FTP
configuration file for a working example).  In this case, you can
specify a "DELETE_INSTEAD_OF_ADD" entry whose value should then
be 1 (if not specified, 0 (zero) is assumed).  This will force the
setting value being saved to instead be removed from the file (if
found therein).

   'DELETE_INSTEAD_OF_ADD' => array('VALUE' => 1),

It is also possible to use "PARSE_PATTERN" with a corresponding
replacement pattern that can be used when updating single-value
settings.  Instead of just replacing the pattern identified by
"PARSE_PATTERN" with the new value, you can specify a regular
expression replacement pattern in "REPLACEMENT_PATTERN" (where
you can also use back references and any other features of normal
regular expression replacements).  Before it is used, there are
three character sequence substitutions that are made (see below)
if needed.  Typically, this is an advanced usage and is not
recommended unless you know what you are doing.

   'REPLACEMENT_PATTERN' => array('VALUE' => "$1\nSetting: %1\n$2"),

Before "REPLACEMENT_PATTERN" is used, the following substitutions
will be made, if found:

   %1  The new setting value that is being updated
   %u  The current user's parsed (per the USERNAME rules
       explained elsewhere in this document) user name
   %d  The current user's parsed (per the DOMAIN rules
       explained elsewhere in this document) domain name

It is also possible to bypass the use of "PARSE_PATTERN" entirely
when updating settings in the target file.  You can indicate that
"PARSE_PATTERN" is only for use to retrieve a setting by specifying
a separate search AND replacement pattern that will be used when
updating the setting.  You can use full regular expression syntax
in them both, including back references, etc.  For example:

   'UPDATE_SEARCH_PATTERN'  => array('VALUE' => '/^(?(?=Subject:[^\n]*)([^\n]*\n*)|())(.*)/is'),
   'UPDATE_REPLACE_PATTERN' => array('VALUE' => "\${1}%1"),

The same substitutions (for "%1", "%u" and "%d") that are done for
"REPLACEMENT_PATTERN" (see immediately above) are also made on both
"UPDATE_SEARCH_PATTERN" and "UPDATE_REPLACE_PATTERN" before they
are executed.

Note that "REPLACEMENT_PATTERN", "UPDATE_SEARCH_PATTERN", and
"UPDATE_REPLACE_PATTERN" are all applied only when their pattern
matches something in the target file.  If not, standard
functionality is reverted to, which usually means that the
"NEW_SETTING_TEMPLATE" will be used to add a new value to the
file.

The "USERNAME" key is used to perform manipulations on the current
user's username before using it to log into the FTP server.  It
should conatin an array of username manipulation configuration rules.
It is optional, and if not specified, the username will be used as is.
Please refer to the Credentials Manipulations section elsewhere for
the available "USERNAME" configuration items.  If you use a single
username for all FTP logins (not recommended), you can specify a
hard-coded value here as well.  Here are two examples:

   'USERNAME' => array('STRIP_DOMAIN' => 1),
   'USERNAME' => array('VALUE' => 'ftp_user'),

The "PASSWORD" key is used to perform manipulations on the current
user's password before using it to log into the FTP server.  It
should conatin an array of password manipulation configuration rules.
It is optional, and if not specified, the password will be used as is.
Please refer to the Credentials Manipulations section elsewhere for
the available "PASSWORD" configuration items.  If you use a single
password for all FTP logins (not recommended), you can specify a
hard-coded value here as well.  Here are two examples:

   'PASSWORD' => array('VALUE' => 'master_ftp_password'),
   'PASSWORD' => array('REGULAR_EXPRESSION_PATTERN' => '/[-+* =&]/'
                       'REGULAR_EXPRESSION_REPLACEMENT' => '_'),

When working with settings that contain multiple values (say, for
example, a whitelist of email addresses), there are a few choices
with how those values will be stored in the file.  They can be
compressed into a single encoded string or stored in multiple
entries.  In order to compress the values into one string, you may
add the following to your list of configuration items:

   'MULTIPLE' => ',',

This will create a comma-separated-values format from the values
and store them all in one entry.  Instead of a comma, you may use
anything you like, but you must consider that if your actual values
contain the same character(s), your data will become corrupt.
Instead, you can use PHP serialization (which will not suffer from
such corruption, but does require slighly more storage space) by
specifying:

   'MULTIPLE' => 'SERIALIZE',

If, on the other hand, you want to store your value in multiple
entries in the same file, you can specify:

   'MULTIPLE' => 'MULTIPLE',

If your "PARSE_PATTERN" returns multiple setting values but you
have not specified anything for the "MULTIPLE" configuraion, values
will be compressed using PHP serialization, but you should always
use the "MULTIPLE" configuration to avoid problems.

Local File Backend
------------------

Here is an example of some of the most common configuration
values when using the Local File backend:

   'BACKEND'               => 'local_file',
   'ACCESS_TYPE'           => array('VALUE' => 'PHP'),
   'FILE' => array('VALUE' => '/virtual-home/%2/%1/.spamassassin/user_prefs'),
   'PARSE_PATTERN'         => array('VALUE' => "/^required_score[^\S\n]+(.*)$/m"),
   'PATTERN_GROUP_NUMBER'  => array('VALUE' => 1),
   'NEW_SETTING_TEMPLATE'  => array('VALUE' => "required_score  %1\n"),
   'DELETE_WHEN_EMPTY'     => array('VALUE' => 1),
   'TREAT_AS_EMPTY_WHEN_NOT_FOUND' => array('VALUE' => 1),
   'MAX_SEQUENTIAL_EMPTY_LINES' => 1,
   'USERNAME'              => array('STRIP_DOMAIN' => 1),

The above example uses the PHP method of accessing files on
the server; there is also a set-UID (SUID) access method
available, for which a few extra settings are necessary:

   'ACCESS_TYPE'           => array('VALUE' => 'SUID'),
   'SUID_LOCATION'         => array('VALUE' => '/usr/share/squirrelmail/plugins/server_settings_backend/squirrelmail_server_settings_suid_backend'),

The minimal required elements are: "BACKEND", "ACCESS_TYPE",
"FILE", "PARSE_PATTERN", "NEW_SETTING_TEMPLATE" and
"SUID_LOCATION" if using the "SUID" access type.

The "BACKEND" key is what dictates where the setting is located (in
a SQL database?  In LDAP?  Etc....) and what other configuration
items are needed.  If, however, you specify a key called "VALUE",
its corresponding value will be immediately used instead of making
any backend lookups - this allows you to hard-code a configuration
item.  Another key called "CUSTOM" allows you to specify an external
PHP function name that will be consulted for obtaining the desired
setting.  That function must be provided by you, and is expected
to return the setting value.  It will be given the entire contents
of the configuration array as its one and only argument, so it can
make use of any custom information you might add to it.  If "VALUE"
is specified, it always takes precendence.  If "CUSTOM" is
specified, it gets second priority before falling back on "BACKEND"
to make a standard lookup.

   'VALUE'  => 'xyz',
   'CUSTOM' => 'my_lookup_function',

As explained above, "ACCESS_TYPE" can be either "PHP" or "SUID".
The PHP access type employs PHP file functions to access files on
the server, and as such requires that any files being accessed be
readable and writable by the web server.  This introduces a certain
level of security risk, which is why the set-UID (SUID) access
method is more desirable (when using it, files can remain owned by
each user and need not be made available to the web server).
However, the SUID access method requires that you also configure
and compile a separate C program (included with this plugin),
whereas the PHP access method does not require any other setup.
More details about configuring and compiling the SUID access method
are found elsewhere in this document under the "Set-UID
Configuration" section.  Also, please read the "Security" section,
which deals with the security risks of both the PHP and SUID file
access methods.

When using the SUID access type, the "SUID_LOCATION" is used to
indicate the absolute path to the SUID backend program to be used
for this widget (it is possible to configure and compile multiple
versions (with different options) to be used with other widgets)).
If you are having setup difficulties with the SUID access methid,
you can send debug and error output from the SUID program to a
special file by using the "SUID_DEBUG_OUTPUT" setting.  Note that
the SUID program itself has to be configured to produce such output.

   'SUID_DEBUG_OUTPUT' => array('VALUE' => '/tmp/squirrelmail_server_settings_backend_suid_debug'),

"FILE" must contain the path and name of the target file.  For the
PHP access type, this needs to be a full, absolute file path.  For
the SUID access type, the path is generally relative to the user's
home directory and is thus usually just the file name (although
depending on how the SUID backend is configured, there is some
flexibility here).  In either case, before the file path is used,
the following substitutions will be made, if found therein:

   %1  The username of the user who is currently logged in
   %2  The domain of the user who is currently logged in

"TREAT_AS_EMPTY_WHEN_NOT_FOUND" specifies whether or not, when the
file is not found, the setting value should be assumed to be
empty/null (if this is the desired behavior, set to 1).  If not
specified, 0 (zero) is assumed, and an error will be caused whenever
a setting's target file is not found.

"DELETE_WHEN_EMPTY" indicates that when nothing but whitespace
is left in the target file (perhaps due to the user deactivating
some setting(s) or clearing a text field), it should be deleted.
Set to 1 to enable this behavior or 0 (zero) to leave the empty
file on the server (which is the default behavior when this
element is not present).

"NEW_SETTING_TEMPLATE" is used to add a setting to its target
file when it is not yet present therein.  The value given here
is, after making the substitutions noted below, added to the
end of the target file.  Before it is used, the following
substitutions will be made, if found:

   %1  The new setting value that is being added
   %u  The current user's parsed (per the USERNAME rules
       explained elsewhere in this document) user name
   %d  The current user's parsed (per the DOMAIN rules
       explained elsewhere in this document) domain name

When "NEW_SETTING_TEMPLATE" is used to add setting(s) to the
target file, they are added to the end of the file by default.
If they should be added to the top of the file instead, use the
"ADD_TO_TOP" entry:

   'ADD_TO_TOP' => array('VALUE' => 1),

Because we are working with a file-level protocol, once the file
is downloaded, the setting value needs to be picked out of that
file.  To provide maximum flexibility, "PARSE_PATTERN" can be given
as any regular expression needed to do just that.  For example, to
pull a setting that occurs on just one line that starts with the
setting name and then some whitespace before the setting value,
this would do:

   'PARSE_PATTERN' => array('VALUE' => "/^required_score\s+(.*)$/m"),

Note that the example above works as expected even when the
setting may appear multiple times in the file on more than one
line (as long as MULTIPLE is set correctly).  When the entire
file is needed, this pattern is appropriate:

   'PARSE_PATTERN'  => array('VALUE' => "/^(.*)$/s"),

Note that the setting's value is indicated in these examples by
being enclosed in parenthesis.  In more complex patterns, more
that one set of parenthesis may be required, so in order to
indicate which set of parenthesis contains the needed value, use
"PATTERN_GROUP_NUMBER", which should contain the sequential
number of the desired parenthesis set, counting opening parenthesis
from left to right.  In the two examples above, we want the first
parenthesis pair:

   'PATTERN_GROUP_NUMBER' => array('VALUE' => 1),

If "PATTERN_GROUP_NUMBER" is not given, 0 (zero) is assumed,
which takes the *entire* pattern match.  For more information
about how to design your own custom regular expressions, see:

   http://php.net/manual/reference.pcre.pattern.syntax.php
   http://php.net/manual/reference.pcre.pattern.modifiers.php

One important thing to keep in mind about the "PARSE_PATTERN"
is that it is both used as explained above to pick out a
setting's value from a file, as well as to identify what should
be removed from a file when the setting is being removed (perhaps
the user deselected the setting) or when it is being removed
and then re-added to the file later (which usually only applies
to when multiple-value settings are being reshuffled).  In some
cases, it is desirable to keep some small amount of the setting
in the file after it has been removed (maybe set it to some
default value or keep a placeholder).  You may accomplish
this using the "DELETE_KEEP_PATTERN" element, which is used as
the replacement portion of the regular expression search and
replace operation that is performed on the file.  You can use
any features of normal regular expression replacement strings,
such as back references ($1, $2, etc.), as explained in the PHP
regular expression manual.  Additionally, if "%u" or "%d" are
found in "DELETE_KEEP_PATTERN", they are replaced with the pre-
parsed username and domain before the regular expression is
executed.  If "DELETE_KEEP_PATTERN" is not given, it is not used
and the entire "PARSE_PATTERN" match is removed from the file
when appropriate.

   'DELETE_KEEP_PATTERN' => array('VALUE' => '$3 = none'),

When removing (and possibly replacing) lines from the target file,
the "PARSE_PATTERN" might leave behind extra unwanted blank lines,
gradually growing the file out of control with empty space.  To
avoid this, you may set "MAX_SEQUENTIAL_EMPTY_LINES" to the maximum
allowable number of blank lines in the file.  Any more will be
removed.  Note that this setting will affect the entire file, and
not just the lines that were removed for this setting.  If set to
0 (zero), no blank line replacement will be attempted.  Also note
that this entry is NOT a lookup item like the other entries
described here - it is specified only as a simple integer value.

   'MAX_SEQUENTIAL_EMPTY_LINES' => 1,

In some limited circumstances, you may want to remove a given value
from a file on the backend when a save action is occuring.  This
usually only applies when a secondary save action is in use (see
the Advanced Usage section elsewhere and refer to the example "file"
configuration files for working examples).  In this case, you can
specify a "DELETE_INSTEAD_OF_ADD" entry whose value should then
be 1 (if not specified, 0 (zero) is assumed).  This will force the
setting value being saved to instead be removed from the file (if
found therein).

   'DELETE_INSTEAD_OF_ADD' => array('VALUE' => 1),

It is also possible to use "PARSE_PATTERN" with a corresponding
replacement pattern that can be used when updating single-value
settings.  Instead of just replacing the pattern identified by
"PARSE_PATTERN" with the new value, you can specify a regular
expression replacement pattern in "REPLACEMENT_PATTERN" (where
you can also use back references and any other features of normal
regular expression replacements).  Before it is used, there are
three character sequence substitutions that are made (see below)
if needed.  Typically, this is an advanced usage and is not
recommended unless you know what you are doing.

   'REPLACEMENT_PATTERN' => array('VALUE' => "$1\nSetting: %1\n$2"),

Before "REPLACEMENT_PATTERN" is used, the following substitutions
will be made, if found:

   %1  The new setting value that is being updated
   %u  The current user's parsed (per the USERNAME rules
       explained elsewhere in this document) user name
   %d  The current user's parsed (per the DOMAIN rules
       explained elsewhere in this document) domain name

It is also possible to bypass the use of "PARSE_PATTERN" entirely
when updating settings in the target file.  You can indicate that
"PARSE_PATTERN" is only for use to retrieve a setting by specifying
a separate search AND replacement pattern that will be used when
updating the setting.  You can use full regular expression syntax
in them both, including back references, etc.  For example:

   'UPDATE_SEARCH_PATTERN'  => array('VALUE' => '/^(?(?=Subject:[^\n]*)([^\n]*\n*)|())(.*)/is'),
   'UPDATE_REPLACE_PATTERN' => array('VALUE' => "\${1}%1"),

The same substitutions (for "%1", "%u" and "%d") that are done for
"REPLACEMENT_PATTERN" (see immediately above) are also made on both
"UPDATE_SEARCH_PATTERN" and "UPDATE_REPLACE_PATTERN" before they
are executed.

Note that "REPLACEMENT_PATTERN", "UPDATE_SEARCH_PATTERN", and
"UPDATE_REPLACE_PATTERN" are all applied only when their pattern
matches something in the target file.  If not, standard
functionality is reverted to, which usually means that the
"NEW_SETTING_TEMPLATE" will be used to add a new value to the
file.

The "USERNAME" key is used to perform manipulations on the current
user's username before using it in any substitutions in the settings
described above as well as to determine the user's home directory
when using the SUID access type.  It should conatin an array of
username manipulation configuration rules.  It is optional, and if
not specified, the username will be used as is.  Please refer to the
Credentials Manipulations section elsewhere for the available
"USERNAME" configuration items.

The "PASSWORD" key is used to perform manipulations on the current
user's password before using it to authenticate against the SUID
backend access type.  It should conatin an array of password
manipulation configuration rules.  It is optional, and if not
specified, the password will be used as is.  Please refer to the
Credentials Manipulations section elsewhere for the available
"PASSWORD" configuration items.

When working with settings that contain multiple values (say, for
example, a whitelist of email addresses), there are a few choices
with how those values will be stored in the file.  They can be
compressed into a single encoded string or stored in multiple
entries.  In order to compress the values into one string, you may
add the following to your list of configuration items:

   'MULTIPLE' => ',',

This will create a comma-separated-values format from the values
and store them all in one entry.  Instead of a comma, you may use
anything you like, but you must consider that if your actual values
contain the same character(s), your data will become corrupt.
Instead, you can use PHP serialization (which will not suffer from
such corruption, but does require slighly more storage space) by
specifying:

   'MULTIPLE' => 'SERIALIZE',

If, on the other hand, you want to store your value in multiple
entries in the same file, you can specify:

   'MULTIPLE' => 'MULTIPLE',

If your "PARSE_PATTERN" returns multiple setting values but you
have not specified anything for the "MULTIPLE" configuraion, values
will be compressed using PHP serialization, but you should always
use the "MULTIPLE" configuration to avoid problems.

When using the SUID access method, it is possible to configure the
SUID backend to require a "master" set of credentials as an extra
layer of security.  The "master" username and/or password would need
to be sent to the SUID backend program correctly before sending the
current user credentials.  You can turn this feature on by using the
"MASTER_SECURITY_CHECK" setting.  When set to 1, the "master"
password should be specified in the web server's startup
configuration (so that only the super-user can read it and it is only
available to the directory where SquirrelMail is located) under the
name "SQUIRRELMAIL_MASTER_PASSWORD".

When "MASTER_SECURITY_CHECK" is turned on, this setting indicates
whether or not to also look for the "master" username in the server
environment under the name "SQUIRRELMAIL_MASTER_USERNAME".  If this
setting is not used, but "MASTER_SECURITY_CHECK" is, only the
"master" password will be sent and the SUID backend is expected to
know the "master" username already.

   'MASTER_SECURITY_CHECK' => array('VALUE' => 1),
   'MASTER_USERNAME'       => array('VALUE' => 1),

Note that these settings require extra configuration of the SUID
backend's C program as well.  More information about using this
feature can be found elsewhere in this document under the "Set-UID
Configuration" section.

Credentials Manipulations
-------------------------

Many of the backends used in this plugin require some kind of
username, domain and/or password information.  You'll need to
refer to the documentation for the backend in question as to
if and how these modified credentials are used.

The "USERNAME" key is used to perform manipulations on the current
user's username.  It should conatin an array of username manipulation
configuration rules.  It is optional, and if not specified, the
username will be used as is.  The available username manipulations
are any one of the following:

   STRIP_DOMAIN  This removes any domain information from the
                 username as well as the email delimiter, if
                 found.  So, if your usernames are of the format
                 "user@example.org", the resulting username for
                 use as the "%1" substitution in the SQL commands
                 will be just "user".  If the domain is not found
                 the username will be unchanged, so it does not
                 hurt to leave this turned on always.  The domain
                 is identified by the presence of the email
                 delimiter, so you'll want to make sure it is
                 also set appropriately for your system.  This
                 should be enabled by setting it to something
                 non-zero:
                    'STRIP_DOMAIN' => 1

   ADD_DOMAIN    This adds an email delimiter and the current domain
                 to the username if they are not already found in
                 the username.  The domain being added may either
                 be the original SquirrelMail domain or the one
                 that results after applying the "DOMAIN"
                 maniuplations specified herein.  To specify one
                 or the other:
                    'ADD_DOMAIN' => 'ORIGINAL'
                    'ADD_DOMAIN' => 'MODIFIED'

   DELIMITER     This specifies the email delimiter used for the
                 other rules herein, and if not given, defaults to
                 "@".  You could change it to "&" as such:
                    'DELIMITER' => '&'

   REGULAR_EXPRESSION_PATTERN
   REGULAR_EXPRESSION_REPLACEMENT
                 These two items are used to pass the username
                 through a regular expression pattern replacement.
                 This allows any manner of complex replacements
                 to be made as necessary.  Both must be specified
                 to make use of this functionality.
                    'REGULAR_EXPRESSION_PATTERN' => '/example.com$/'
                    'REGULAR_EXPRESSION_REPLACEMENT' => 'example.org'

   VALUE         When this is specified, the username will be used
                 as given here - it will be the same for ALL USERS.
                 This might be helpful when you have just one
                 username and password for making FTP lookups.
                    'VALUE' => 'master.user.name'

   CUSTOM        When this is specified, the username will be looked
                 up by calling a custom function that you provide.
                 That function must then return the username.  It is
                 given the entire contents of the "USERNAME" array
                 of configuration items as a parameter, so your
                 function can make use of any custom information you
                 add to it.
                    'CUSTOM' => 'my_username_lookup'

   BACKEND       When this is specified, the username will be looked
                 up by making a separate backend inquiry.  In this
                 case, this "USERNAME" sub-array will need to
                 contain all the same backend configuration items
                 for the desired lookup.  This might be useful when,
                 for example, you need to consult an LDAP data store
                 or SQL database to determine what the FTP login
                 information is for the current user when making a
                 FTP lookup.  Note that the use of the recursive
                 "USERNAME" item below is perfectly acceptable.
                    'BACKEND'  => 'sql'
                    'DSN'      => array('VALUE' => 'mysql://username:password@localhost/database'),
                    'QUERY'    => array('VALUE' => "SELECT username FROM ftp_accounts WHERE username = '%1@%2'"),
                    'USERNAME' => array('STRIP_DOMAIN' => 1),

The "DOMAIN" key is used to perform manipulations on the current
user's domain.  It should conatin an array of domain manipulation
configuration rules.  It is optional, and if not specified, the
domain will be used as is.  The available domain manipulations
are any one of the following:

   REGULAR_EXPRESSION_PATTERN
   REGULAR_EXPRESSION_REPLACEMENT
                 These two items are used to pass the domain
                 through a regular expression pattern replacement.
                 This allows any manner of complex replacements
                 to be made as necessary.  Both must be specified
                 to make use of this functionality.
                    'REGULAR_EXPRESSION_PATTERN' => '/^example.com$/'
                    'REGULAR_EXPRESSION_REPLACEMENT' => 'example.org'

   VALUE         When this is specified, the domain will be used
                 as given here - it will be the same for ALL USERS.
                    'VALUE' => 'master.domain.name'

   CUSTOM        When this is specified, the domain will be looked
                 up by calling a custom function that you provide.
                 That function must then return the domain.  It is
                 given the entire contents of the "DOMAIN" array
                 of configuration items as a parameter, so your
                 function can make use of any custom information you
                 add to it.
                    'CUSTOM' => 'my_domain_lookup'

   BACKEND       When this is specified, the domain will be looked
                 up by making a separate backend inquiry.  In this
                 case, this "DOMAIN" sub-array will need to
                 contain all the same backend configuration items
                 for the desired lookup.
                    'BACKEND'  => 'sql'
                    'DSN'      => array('VALUE' => 'mysql://username:password@localhost/database'),
                    'QUERY'    => array('VALUE' => "SELECT domain FROM user_domains WHERE username = '%1@%2'"),
                    'USERNAME' => array('STRIP_DOMAIN' => 1),

The "PASSWORD" key is used to perform manipulations on the current
user's password.  It should conatin an array of password manipulation
configuration rules.  It is optional, and if not specified, the
password will be used as is.  The available password manipulations
are any one of the following:

   REGULAR_EXPRESSION_PATTERN
   REGULAR_EXPRESSION_REPLACEMENT
                 These two items are used to pass the password
                 through a regular expression pattern replacement.
                 This allows any manner of complex replacements
                 to be made as necessary.  Both must be specified
                 to make use of this functionality.
                    'REGULAR_EXPRESSION_PATTERN' => '/aeiou/'
                    'REGULAR_EXPRESSION_REPLACEMENT' => ''

   VALUE         When this is specified, the password will be used
                 as given here - it will be the same for ALL USERS.
                 This might be helpful when you have just one
                 username and password for making FTP lookups.
                    'VALUE' => 'master.password'

   CUSTOM        When this is specified, the password will be looked
                 up by calling a custom function that you provide.
                 That function must then return the password.  It is
                 given the entire contents of the "PASSWORD" array
                 of configuration items as a parameter, so your
                 function can make use of any custom information you
                 add to it.
                    'CUSTOM' => 'my_password_lookup'

   BACKEND       When this is specified, the password will be looked
                 up by making a separate backend inquiry.  In this
                 case, this "PASSWORD" sub-array will need to
                 contain all the same backend configuration items
                 for the desired lookup.  This might be useful when,
                 for example, you need to consult an LDAP data store
                 or SQL database to determine what the FTP login
                 information is for the current user when making a
                 FTP lookup.
                    'BACKEND'  => 'sql'
                    'DSN'      => array('VALUE' => 'mysql://username:password@localhost/database'),
                    'QUERY'    => array('VALUE' => "SELECT password FROM ftp_accounts WHERE username = '%1@%2'"),
                    'USERNAME' => array('STRIP_DOMAIN' => 1),

Advanced Usage
--------------

Also allowed is an advanced usage where more than one set of
configuration values is given.  This allows more than one save
action to be performed for any one setting.  In this case, the
lookup will always use the FIRST set of configuration values,
but each configuration set will be used once when saving the
setting's value.  Note that each set may have different BACKEND
types.

array(
   array(
      'BACKEND'              => 'ftp',
      'HOST'                 => array('VALUE' => 'localhost'),
      'MODE'                 => array('VALUE' => 'BINARY'),
      'FILE'                 => array('VALUE' => 'autoreply_message_body'),
      'PARSE_PATTERN'        => array('VALUE' => "/^(.*)$/s"),
      'NEW_SETTING_TEMPLATE' => array('VALUE' => '%1'),
      'TREAT_AS_EMPTY_WHEN_NOT_FOUND' => array('VALUE' => 1),
      'DELETE_WHEN_EMPTY'    => array('VALUE' => 1),
   ),
   array(
      'BACKEND'    => 'sql',
      'DSN'        => array('VALUE' => 'mysql://username:password@localhost/autoresponder_database'),
      'SAVE_QUERY' => array('VALUE' => "UPDATE autoreply_messages SET message_body = '%3' WHERE username = '%1@%2'"),
      'USERNAME'   => array('STRIP_DOMAIN' => 1),
   ),
)



Set-UID Configuration
=====================

When using the Local File backend with the set-UID (SUID) access
type, configuration and compilation of an extra C program is
required.  This should be trivial on most platforms where one has
root/super-user access, but it is strongly recommended that you
take the time to read and understand the configuration options, if
only to learn how to build the SUID program in a secure manner.

The SUID program is included in the "suid_backend" directory within
this plugin, and all configuration and compilation commands should
be run from within that directory.  You'll also want to change to
the root/super-user before compiling.

  $ cd suid_backend
  $ su

The most simple configuration and compilation of the SUID program
can be achieved by using these three commands:

  # ./configure
  # make
  # make install

However, the default configuration options used in this case will
assume that your users are local system users amongst other things.
It is strongly advised that you review the various configuration
options that are available by using this command:

  # ./configure --help

In particular, you'll want to use the options that tell the program
how to correctly look up and authenticate your users (see the
--enable-user-lookup and --enable-auth options).  You'll also want
to use the options suggested in the Security section of this
document (and avoid the ones it describes as dangerous).  Remember
that if you are having problems, you should use the --enable-debug
and --disable-silent options (and refer to the SUID_BACKEND_ERRORS
file).

Also, make sure to set the --enable-authorized-invoker option to
specify the name of the system user that runs your web server if
it is not "apache" (which is the default value).  If you don't
know what user runs your web server, refer to SquirrelMail's
src/configtest.php utility (other common ones are "www", "httpd"
and "wheel").

Account Lookup Options
----------------------

The program supports user account lookup for local/system users and
accounts in a database (via unixODBC).  Specify
--enable-user-lookup-sql-dsn and --enable-user-lookup-sql-query to
use database account lookups, otherwise, lookups will be made against
the local password database (or shadow password database, if the
system uses one).

Note that the value specified for --enable-user-lookup-sql-dsn should
be the NAME of the data source in your unixODBC configuration, and
not the actual DSN string itself.  The database connection parameters
are configured by the administrator in ODBC using the ODBC manager or
by editing odbcinit.ini and odbc.ini files directly.  See the
"unixODBC Tips" section below for more help configuring unixODBC.

The first "%u" (sans quotes) found in --enable-user-lookup-sql-query
is replaced with the user name before executing that database query.
The query given must retrieve the account's user name, password, UID,
GID and home directory (in that order).

An example configuration using account database lookups might look
like this ("email_accounts" being a data source that is already set
up in the system ODBC configuration):

  # ./configure --enable-user-lookup=sql --enable-user-lookup-sql-dsn=email_accounts --enable-user-lookup-sql-query="SELECT username, password, uid, gid, home FROM email_users WHERE username='%u'"

Account Authentication Options
------------------------------

The program supports IMAP, UW c-client (IMAP), PAM, SHADOW and PASSWD
authentication mechanisms.  When no mechanism is specified, local
password authentication is used (shadow password authentication is
auto-detected if available).  Note that even database account lookup
can be combined with local password validation in the case that the
password from the database is encrypted in a way that the local
system understands.

If IMAP or UW c-client authentication is turned on, but the IMAP
server details are not specified, they default to "localhost" for
--enable-imap-addr localhost and 143 for --enable-imap-port.

Both the IMAP and the c-client mechanisms authenticate against an
IMAP server, the only difference being that the IMAP mechanism is a
small module that works by opening a local (unix domain) socket to
the IMAP server and attempting a normal LOGIN command, whereas the
UW c-client mechanism relies on a much more complex and robust
library developed by the University of Washington as part of their
IMAP package.  It can support more exotic connection types if needed.

For help building the UW c-client library that is required in order
to use the UW c-client configuration option, see the "UW c-client
Tips" section below.

Master Credentials Security Check
---------------------------------

As explained in the Security section in this document, enabling the
use of an additional authentication check against a master
credentials set can be an effective way to limit use of the SUID
program only to its intended caller(s).  

It is advised that the "master" account be a special purpose account
that is not otherwise used, especially from external locations.  To
enable this security mechanism, start by using the
--enable-master-security-check options and then configure any other
account lookup or authentication options as needed for the "master"
account (which need not be the same as that for your other, normal
accounts).  There is a second set of lookup and authentication
configuration options that are only used for the "master" account
but that operate the same as do their like-named "normal" options
(for example, --enable-master-user-lookup works just the same for
"master" account lookups as does --enable-user-lookup for "normal"
accounts).

Now, in your corresponding setting configuration set, make sure to
enable MASTER_SECURITY_CHECK, and optionally MASTER_USERNAME (if you
do not use MASTER_USERNAME, then you need to configure the SUID
program with --enable-master-username).

The third and final step is to place the needed "master" credentials
in the web server's configuration file.  Note that these instructions
assume the Apache web server, and it is not known whether this scheme
will work with any other web servers (although in principle, it
should).  

An example configuration file named apache.conf_example is provided
for this purpose.  That file includes detailed instructions on how
to get the web server to read the file upon startup.  Generally
speaking, you'll want to refer to that file using an Apache
"Include" directive that points to it or put a copy of the file in
the web server's "conf.d" directory, where it will be read
automatically.  It is VERY IMPORTANT that the file be owned ONLY
by the root/super-user and ONLY readable by that user!

Notes About Miscellaneous Options
---------------------------------

As described in the Security section of this document, it is highly
desirable to use the --enable-min-uid configuration option, which is
typically set to something such as 500 for local system accounts, but
for virtual accounts, it may be even more specific.

It is also highly recommended that you use the
--enable-local-file-prefix option, the value for which is usually the
path to the SquirrelMail attachments directory.

In addition, you are strongly encouraged to place restrictions
on the file names available via the SUID program by using
--enable-remote-filenames-dot and/or either
--enable-remote-file-prefix or --enable-remote-file-custom-prefix.

The --enable-home-dir-prefix option is also highly recommended, as
it jails all file system access within the given directory path,
which may limit the fallout from any successful attacks.

If files created by the SUID program must be readable by some other
user process (e.g., a mail filtering agent), you may need to use the
--enable-remote-file-mode configuration option (e.g.,
--enable-remote-file-mode=644).

If you wish to change the installation location of the program, you
can specify the configuration option --bindir, setting its value
to the desired location, or edit the "bindir" setting in Makefile.am.
Otherwise, by default, the compiled program will be installed to the
main plugin directory (one level above the "suid_backend" directory).
If you change the executable program location, be sure to synchronize
the SUID_LOCATION entry in all setting configuration sets (note that
is feasible to use multiple (differently compiled) versions of the
SUID program for different uses - perhaps one for a vacation/
autoresponder system, one for allowing editing of anti-spam
configuration settings, and even one for a file manager-type system.

Note that if you have configured with the --enable-debug and/or
--disable-silent options, but you do not use the SUID_DEBUG_OUTPUT
entry in a corresponding setting configuration set, you may still
find output from the SUID program in your web server's error log.
Using --enable-silent will eliminate all such output except any
specifically created by the use of --enable-debug.

File Manager-Type Options
-------------------------

The following options are not necessarily only limited to use in file
manager-type applications, but usually their use outside of such
environments is not needed and moreover, such use could lead to less
secure setups.

The --enable-auto-adjust-absolute-paths option is often helpful with
path problems, especially with file manager type operations.  It
makes the SUID program assume that any absolute path references are
actually relative to the user's home directory.  One indication that
you might need this option is if you are getting the error "Remote
file path cannot be absolute".

The --enable-remote-subdirectories-ok option is usually desirable
with file manager configurations, but in most other cases it is not
necessary.  One indication that you might need it is if you are
getting the error "Remote file name cannot contain "/"".

Note that when using file manager operations (--enable-file-manager)
to provide access to a shared part of the file system (same location
for more than one user), consider carefully how permissions are
assigned and how the SUID program is given access to that location.
If you must override the UID or GID of the user (see the
--enable-override-uid and --enable-override-gid options and read
about them in the Security section in this document), start with the
GID, perhaps assigning a special group to the shared location.

unixODBC Tips
-------------

When using the SUID program to look up account data from a database,
you need to have an already-working unixODBC system in place.  What
follows are some tips on how to set up unixODBC, but for more
thorough assistance, please refer to your own system documentation
and that available at the unixODBC web site: http://www.unixodbc.org

Some helpful articles for getting started with unixODBC and/or
setting up data sources are:

http://www.easysoft.com/developer/interfaces/odbc/linux.html
http://www.easysoft.com/developer/interfaces/odbc/linux.html#dsn_install_unixodbc
http://www.unixodbc.org/odbcinst.html
http://www.unixodbc.org/doc/
http://www.unixodbc.org/doc/UserManual/
http://dev.mysql.com/doc/refman/5.0/en/connector-odbc-configuration.html
http://dev.mysql.com/doc/refman/5.5/en/connector-odbc-configuration-connection-parameters.html
http://dev.mysql.com/doc/refman/5.5/en/connector-odbc-configuration-dsn-unix.html

First, you'll need to ensure that you have installed the unixODBC
and unixODBC-devel (and mysql-connector-odbc if using a MySQL
database) packages, which are commonly available in most operating
system package repositories (yum, apt-get, etc.).

When beginning to configure unixODBC, it can be very helpful to know
what configuration files are in use.  You can see this by running the
following command:

  odbcinst -j

Note that on some RedHat family systems, the default "Driver" entry
for the MySQL driver in /etc/odbcinst.ini can be wrong.  It often
looks like this:

   Driver         = /usr/lib/libmyodbc.so

But may need to be changed to this:

   Driver          = /usr/lib/libmyodbc3.so

Of course, you can simply check to see which driver exists on your
machine and make this change only if necessary.

Here is an example /etc/odbcinst.ini from a RedHat family system
that has PostgreSQL and MySQL drivers set up:

   [PostgreSQL]
   Description     = ODBC for PostgreSQL
   Driver          = /usr/lib/libodbcpsql.so
   Setup           = /usr/lib/libodbcpsqlS.so
   FileUsage       = 1
   [MySQL]
   Description     = ODBC for MySQL
   Driver          = /usr/lib/libmyodbc3.so
   Setup           = /usr/lib/libodbcmyS.so
   FileUsage       = 1

Here is an example /etc/odbc.ini from a RedHat family system that
has a single data source set up called "email_accounts".  Of course,
you'll need to specify your own connection parameters in place of
these examples.

   [email_accounts]
   Description = Email Account Lookups
   Trace       = Off
   TraceFile   = stderr
   Driver      = MySQL
   Server      = localhost
   Port        = 3306
   User        = email_administrator
   Password    = abcd1234
   Database    = user_accounts

Once you have completed your initial configuration, you can confirm
it by using the following commands, the first of which shows what
drivers you have configured (make sure yours is listed):

  $ odbcinst -q -d

Next, check that your data source is listed when you query for all
configured data sources:

  $ odbcinst -q -s

You can then test your unixODBC configuration/data source setup by
running the following command:

  $ isql -v <data source name>

Substitute the actual data source that you've configured for "<data
source name>" and if a "SQL>" prompt is displayed (and no ERROR
messages), then your system is probably in working order.

UW c-client Tips
----------------

When using the UW c-client authentication mechanism, you need to have
the c-client library pre-installed on your system (perhaps as part of
the UW IMAP package) or you need to download and build it yourself.

First, a note about the flags that the library uses when connecting
to IMAP accounts - these flags are used to construct mailbox names
that control how the library connects to the IMAP server and which
flags you use depend on where your IMAP server resides and the kind
of connection you need to use with it.  A common use is:

   --enable-cclient-mailbox-flags=/norsh/notls

For all possible flags, see section III ("Remote names") in this
document:

   http://www.washington.edu/imap/documentation/naming.txt.html

If c-client is already present, no further action may be necessary,
as long as its library and include files are in your system's default
library and include paths.  If not, you'll need to use the
--enable-cclient-includes configuration option to point to the
directory where its "mail.h" header file is found and the
--enable-cclient-libs option to specify the location where its
"c-client.a" file is found.

For BSD family systems with the c-client library pre-installed, it
has been reported in the past that these options will suit (if you
use these (un)successfully (or find that you don't need to specify
them at all), please report your findings to the authors of this
plugin):

   --enable-cclient-includes=/usr/local/include
   --enable-cclient-libs=/usr/local/lib

If you use a RedHat family system with the yum package maintenance
system, you can install the UW c-client library as such:

  # yum install libc-client-devel

Then, these configuration options can be used:

   --enable-auth=cclient
   --enable-cclient-includes=/usr/include/imap
   --enable-cclient-libs=/usr/lib
   --enable-cclient-mailbox-flags=/norsh/notls

Finally, if you need to build c-client on your own, you'll first
need to download it from:

   ftp://ftp.cac.washington.edu/mail/imap.tar.Z

Note that this location may change in the future, so you may want to
check the main UW IMAP page for more information:

   http://www.washington.edu/imap

After you unpackage the downloaded file (use a command such as
"tar -xzvf imap.tar.Z"), read the README file to proceed (usually
only steps 1 and 2 are necesary from that file).  That is, typically
you just need to choose the correct "make" command (such as
"make lrh") and then c-client will be ready for use with this plugin.
No installation of c-client is necessary.

Once you have c-client built and ready for use with the SUID program
for this plugin, you'll need to use these configuration options:

   --enable-auth=cclient
   --enable-cclient-includes=/usr/local/src/uw-cclient/imap-2007e/c-client
   --enable-cclient-libs=/usr/local/src/uw-cclient/imap-2007e/c-client
   --enable-cclient-mailbox-flags=/norsh/notls

You'll need to substitute "/usr/local/src/uw-cclient..." for the
directory where you downloaded and built your copy of the c-client
library (note that in most cases, both the includes and library
directories are the same).



Usage (Plugin Authors' Guide)
=============================

Retrieve/Store Settings
-----------------------

The points of entry to this plugin related to storing and
retrieving setting values are few and simple - one for
retrieving a setting value, one for testing a setting's value,
and one for saving a value.  Note that all of them have a
parameter called $quiet.  If it is TRUE, any errors are
silently ignored and NULL is returned.  Typically, you'll
want to leave $quiet set to FALSE, since any errors
encountered are usually configuration errors that you will
want to see and debug.

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/functions.php');
   $setting = retrieve_server_setting($storage_info, $quiet);

This will return the desired setting value (whose name is specified
in the $storage_info variable, which is explained in the Configuration
section).  If the setting value consists of multiple values, it will
be returned in an array, unless there is a configuration problem in
the $storage_info variable.

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/functions.php');
   $result = test_server_setting($test_value, $storage_info, $quiet);

This function will test if the setting identified in the $storage_info
variable (see the Configuration section) contains the given test value.
If the setting is comprised of multiple values, it is considered a
match if one of the values therein matches the test value, whereas if
the setting is scalar, its value must match the test value exactly to
produce an affirmative return value.  The returned value is boolean,
indicating if the test matched or not.  

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/functions.php');
   $result = put_server_setting($new_value, $storage_info, $add, $remove, $quiet);

This will return TRUE when the setting was saved normally.  Here, too,
the $storage_info variable contains all the information needed to
identify the setting name and where and how it is accessed.  See the
Configuration section for more details.  When $add is TRUE, it
indicates that a single value is to be added on to a multi-value
setting.  This allows the caller to push an additional value into the
setting without needing to worry about retrieving and re-saving all
the other values it contains.  Similarly, if $remove is TRUE, this
indicates that the given value is to be removed from a multi-value
setting.  This function will return TRUE when the setting was saved
normally.

File Management API
-------------------

There are an additional set of API entry points for managing files in
a server backend.  The backend type must be one of the file-based
protocols (so, for example, SQL and LDAP backend types won't work and
will produce errors if used with the following functions).  They all
share a $backend_info parameter, which is mostly identical to the
$storage_info parameter to the functions explained above (and defined
in detail in the Configuration section), except that they typically 
only use a subset of the entries therein, ignoring things such as
entries that define how to parse a setting out of a file or how
multiple values are represented.  The $quiet parameter also works
the same as explained above.  For examples of the use of these
functions, see the File Manager plugin, version 3.0.
   
   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $listing = retrieve_directory_listing($directory, $backend_info, $use_lstat, $quiet);

This retrieves a listing of the files and directories within a
designated directory.  It returns an array containing sub-arrays
which each represent a file or directory in the listing.  These
sub-arrays will contain possibly several different key-value pairs,
each for a different file attribute, but should always at least
contain a "name" key whose value is the file or directory name.
Other possible attributes are: "type" ("file", "directory", "link",
"pipe", "socket"), "owner", "group", "size", "permissions",
"date_modified" (date last modified), "date_accessed" (date last
accessed), "date_changed" (date last changed), "link_saki" (link
destination), "numeric_permissions", "raw_mode" (permissions).
The $use_lstat parameter (which defaults to TRUE) determines if
link targets or the links themselves should be queried (if any
exist).  The $directory parameter should be the full path to the
desired directory (keep in mind that this may be relative to where
the user is rooted, especially when using the FTP or SUID backends).

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $result = path_exists($path, $return_info, $backend_info, $quiet);

This will return TRUE when the specified file or directory exists on
the server, or FALSE if not.  The $path parameter should be the full
path to the desired file or directory (keep in mind that this may be
relative to where the user is rooted, especially when using the FTP
or SUID backends).  When $return_info is TRUE, an array of file (or
directory) information will be returned instead (one of the attribute
arrays identical to what is explained above).

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $result = create_directory($directory, $mode, $backend_info, $quiet);

This will return TRUE when the desired directory was created normally.
The $directory parameter should be the full path to the desired
directory (keep in mind that this may be relative to where the user is
rooted, especially when using the FTP or SUID backends).  The $mode
parameter is the octal permissions number to apply to the newly created
directory, which may be ignored depending on the backend (using 0755
is a common default you can use unless more restricted access is desired).

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $result = delete_file_or_directory($path, $is_directory, $backend_info, $autodetect, $quiet);

This will return TRUE when the desired directory or file was deleted
normally.  The $path parameter should be the full path to the desired
file or directory (keep in mind that this may be relative to where the
user is rooted, especially when using the FTP or SUID backends).  The
$is_directory parameter should be TRUE if it is a directory being
deleted and FALSE if it is a file, however most backends will ignore
this and take appropriate actions based on its own evaluation of the
path UNLESS $autodetect is FALSE, in which case, $is_directory is used
unquestioningly).  Remember that directories must be empty before
deleting them.  Note that if it is possible that users will try to
delete non-empty directories, it may be best to set $quiet to TRUE
and catch NULL return values, displaying a warning to the user.

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $result = create_file($path, $contents, $mode, $allow_overwrite, $backend_info, $quiet);

This will return TRUE when the desired file was created normally.
The $path parameter should be the full path to the desired file
(keep in mind that this may be relative to where the user is rooted,
especially when using the FTP or SUID backends).  $contents should
contain anything that should be put in the new file, but need not
contain anything.  The $mode parameter is the octal permissions
number to apply to the newly created file, which may be ignored
depending on the backend (using 0644 is a common default you can
use unless more restricted access is desired).  Note that when
$allow_overwrite is TRUE, this function will replace any file that
already exists; if $allow_overwrite is FALSE and $path already
exists, an error will be tripped.

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $result = change_permissions($path, $mode, $backend_info, $quiet);

This will return TRUE when the desired file has had its permissions
changed to $mode, which is the octal permissions number to apply.
The $path parameter should be the full path to the desired file
(keep in mind that this may be relative to where the user is rooted,
especially when using the FTP or SUID backends).  Note that not all
backends support this functionality, so depending on the situation
it may be useful to specify $quiet as TRUE and deal with the return
value (TRUE or NULL/FALSE) as needed.

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $contents = get_file($path, $backend_info, $quiet);

This will return the contents of the desired file.  The $path
parameter should be the full path to the desired file (keep in
mind that this may be relative to where the user is rooted,
especially when using the FTP or SUID backends).

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $result = move_file_or_directory($source_path, $destination_path, $allow_overwrite, $backend_info, $quiet);

This will return TRUE when the file or directory has been renamed
or moved to the new location.  The $source_path parameter should
be the full path to the file or directory to be renamed/moved
(keep in mind that this may be relative to where the user is
rooted, especially when using the FTP or SUID backends).  Likewise,
the $destination_path is the target location, which should also be
a full path.  Note that when $allow_overwrite is TRUE, this function
will replace any file that already exists; if $allow_overwrite is
FALSE and $path already exists, an error will be tripped.

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $result = copy_file_or_directory($source_path, $destination_path, $is_directory, $recursive, $allow_overwrite, $backend_info, $autodetect, $quiet);

This will return TRUE when the file or directory has been copied
to the new location.  The $source_path parameter should be the
full path to the file or directory to be copied (keep in mind
that this may be relative to where the user is rooted, especially
when using the FTP or SUID backends).  Likewise, the
$destination_path is the target location, which should also be a
full path.  The $is_directory parameter should be TRUE if it is a
directory being copied and FALSE if it is a file, however most
backends will ignore this and take appropriate actions based on
its own evaluation of the path UNLESS $autodetect is FALSE, in
which case, $is_directory is used unquestioningly).  When
$recursive is TRUE and a directory is being copied, any
subdirectories and their contents are recursively copied as well;
otherwise only the immediate contents of the directory (first
level) are copied - no subdirectories will be included.  Links
are not copied at all.  Note that when $allow_overwrite is TRUE,
this function will replace any file or directory that already
exists at the destination; if $allow_overwrite is FALSE and
$destination_path already exists, an error will be tripped.

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $file_path = create_archive($path, $archive_type, $use_path, $is_directory, $recursive, $backend_info, $quiet, $autodetect);

This will build an archive (zip-compressed) file containing the
file or directory pointed to by $path.  $path should be the full
path to the file or directory to be archived (keep in mind that
this may be relative to where the user is rooted, especially when
using the FTP or SUID backends).  The $use_path parameter (must
be a boolean value) indicates whether or not the full path to the
item being archived will be included in the archive or not.  The
$is_directory parameter should be TRUE if it is a directory being
archived and FALSE if it is a file, however most backends will
ignore this and take appropriate actions based on its own
evaluation of the path UNLESS $autodetect is FALSE, in which case,
$is_directory is used unquestioningly).  When $recursive is TRUE
and a directory is being archived, any subdirectories and their
contents are recursively included in the archive; otherwise only
the immediate contents of the directory (first level) are
archived - no subdirectories will be included.  The path to the
archive file on the local file system (usually in the SquirrelMail
attachments directory) is returned under normal conditions.

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/file_functions.php');
   $size = calculate_size($path, $is_directory, $recursive, $backend_info, $autodetect, $quiet);

This returns the approximate size of the file or directory pointed
to by $path.  $path should be the full path to the file or directory
whose size is to be determined (keep in mind that this may be
relative to where the user is rooted, especially when using the FTP
or SUID backends).  The $is_directory parameter should be TRUE if
it is a directory being calculated and FALSE if it is a file,
however most backends will ignore this and take appropriate actions
based on its own evaluation of the path UNLESS $autodetect is FALSE,
in which case, $is_directory is used unquestioningly).  When
$recursive is TRUE and a directory is being calculated, any
subdirectories and their contents are recursively included in the
final size calculation; otherwise only the immediate contents of
the directory (first level) are counted - no subdirectories will
be included.

Vacation API
------------

There is also an interface that allows SquirrelMail plugins to
interact with the "vacation" program on the server.

   $quiet = FALSE;
   include_once(SM_PATH . 'plugins/server_settings_backend/functions.php');
   $result = vacation_initialize($backend_info, $interval, $quiet);

This function will execute the "vacation" program for the user with
the needed flags so that the user's autoresponder database is
initialized.  Note that if you need to use this command, the Local
File backend with the set-UID access method is the most reliable.
If you are not using that backend or access method, you'll need to
make sure the web server can run the "vacation" program (see below)
and add some special entries to the corresponding settings array as
needed:

   'VACATION_PROGRAM' => array('VALUE' => '/usr/bin/vacation'),

The "VACATION_PROGRAM" entry is required and should contain the full
path to the "vacation" program on your system.  Before it is used,
the following substitutions will be made, if found:

   %u  The current user's parsed (per the USERNAME rules
       explained elsewhere in this document) user name
   %d  The current user's parsed (per the DOMAIN rules
       explained elsewhere in this document) domain name

Next, the "VACATION_INIT_FLAG" entry is optional, and "-I" is used if
it is not given.

   'VACATION_INIT_FLAG' => array('VALUE' => '-i'),

Some "vacation" programs support setting up a reply interval when
initializing the autoresponder.  If you want to use it, you have to
add the "VACATION_INTERVAL_FLAG" entry:

   'VACATION_INTERVAL_FLAG' => array('VALUE' => '-r'),

Once you've done that, the $interval argument to vacation_initialize()
that you see above can be used (otherwise it should be specified as
NULL).

Finally, there is an optional "VACATION_USER_ARG" that can be used to
indicate that the user name should be added to the arguments when
calling the "vacation" program.  Its value should be the user name as
it should be sent to the program.  Before it is used, the following
substitutions will be made, if found:

   %u  The current user's parsed (per the USERNAME rules
       explained elsewhere in this document) user name
   %d  The current user's parsed (per the DOMAIN rules
       explained elsewhere in this document) domain name

   'VACATION_USER_ARG' => array('VALUE' => '%u'),

As for running the "vacation" program from the web server, if the
"VACATION_USER_ARG" cannot be used to specify the target user, you
may need to run the program as that user, which usually means that
the web server will need to "sudo" (change) to that user and then
run the program.  Thus, you may have to add the web server user as
a "sudoer" for each and every one of your webmail users.  One way
this can be accomplished is to add the following to your sudoers
file, which is usually /etc/sudoers, making sure to change "apache"
to whatever user runs your web server:

   Runas_Alias MAILUSERS = {list of users using webmail and need vacation functionality}
   apache ALL=(MAILUSERS) NOPASSWD: /usr/bin/vacation

In general, giving the web server "sudo" privileges can be risky,
so BE CAREFUL if you choose this method.  It is strongly recommended
to consider the Local File set-UID backend instead (which requires
none of this special configuration and is much more secure).



TODO
====

  * Implement unfinished backends: LDAP, SOCKET

  * Implement some other backend(s)?  What one(s)?

  * Just a note that the following project might be useful or
    maybe just have some code worth borrowing if the FTP backend
    herein needs work: http://sourceforge.net/projects/kioobftp/

  * Add ability to "addDirectory()" to the tar.class.php package
    that will recursively add everything therein (not strictly
    related to this plugin)



Change Log
==========

  v2.0  2010/06/11  Paul Lesniewski <paul@squirrelmail.org>
    * Added local file backend (uses direct PHP file access
      or a set-UID wrapper)
    * Added vacation program autoresponder initialization
      functionality for other plugins that need it
    * Fixed a few small issues in SQL and FTP backends

  v1.0  2009/02/05  Paul Lesniewski <paul@squirrelmail.org>
    * Initial release; only SQL and FTP backends have been
      implemented so far



