Source for file abook_database.php

Documentation is available at abook_database.php

  1. <?php
  2.  
  3. /**
  4.  * abook_database.php
  5.  *
  6.  * Supported database schema
  7.  * <pre>
  8.  *  owner varchar(128) NOT NULL
  9.  *  nickname varchar(16) NOT NULL
  10.  *  firstname varchar(128)
  11.  *  lastname varchar(128)
  12.  *  email varchar(128) NOT NULL
  13.  *  label varchar(255)
  14.  *  PRIMARY KEY (owner,nickname)
  15.  * </pre>
  16.  *
  17.  * @copyright 1999-2014 The SquirrelMail Project Team
  18.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  19.  * @version $Id: abook_database.php 14420 2014-01-01 20:33:20Z pdontthink $
  20.  * @package squirrelmail
  21.  * @subpackage addressbook
  22.  */
  23.  
  24. /**
  25.  * Needs the DB functions
  26.  * Don't display errors here. Error will be set in class constructor function.
  27.  */
  28. @include_once('DB.php');
  29.  
  30. /**
  31.  * Address book in a database backend
  32.  *
  33.  * Backend for personal/shared address book stored in a database,
  34.  * accessed using the DB-classes in PEAR.
  35.  *
  36.  * IMPORTANT:  The PEAR modules must be in the include path
  37.  * for this class to work.
  38.  *
  39.  * An array with the following elements must be passed to
  40.  * the class constructor (elements marked ? are optional):
  41.  * <pre>
  42.  *   dsn       => database DNS (see PEAR for syntax)
  43.  *   table     => table to store addresses in (must exist)
  44.  *   owner     => current user (owner of address data)
  45.  * ? name      => name of address book
  46.  * ? writeable => set writeable flag (true/false)
  47.  * ? listing   => enable/disable listing
  48.  * </pre>
  49.  * The table used should have the following columns:
  50.  * owner, nickname, firstname, lastname, email, label
  51.  * The pair (owner,nickname) should be unique (primary key).
  52.  *
  53.  *  NOTE. This class should not be used directly. Use the
  54.  *        "AddressBook" class instead.
  55.  * @package squirrelmail
  56.  * @subpackage addressbook
  57.  */
  58.     /**
  59.      * Backend type
  60.      * @var string 
  61.      */
  62.     var $btype = 'local';
  63.     /**
  64.      * Backend name
  65.      * @var string 
  66.      */
  67.     var $bname = 'database';
  68.  
  69.     /**
  70.      * Data Source Name (connection description)
  71.      * @var string 
  72.      */
  73.     var $dsn       = '';
  74.     /**
  75.      * Table that stores addresses
  76.      * @var string 
  77.      */
  78.     var $table     = '';
  79.     /**
  80.      * Owner name
  81.      *
  82.      * Limits list of database entries visible to end user
  83.      * @var string 
  84.      */
  85.     var $owner     = '';
  86.     /**
  87.      * Database Handle
  88.      * @var resource 
  89.      */
  90.     var $dbh       = false;
  91.     /**
  92.      * Enable/disable writing into address book
  93.      * @var bool 
  94.      */
  95.     var $writeable = true;
  96.     /**
  97.      * Enable/disable address book listing
  98.      * @var bool 
  99.      */
  100.     var $listing = true;
  101.  
  102.     /* ========================== Private ======================= */
  103.  
  104.     /**
  105.      * Constructor
  106.      * @param array $param address book backend options
  107.      */
  108.     function abook_database($param{
  109.         $this->sname = _("Personal Address Book");
  110.  
  111.         /* test if Pear DB class is available and freak out if it is not */
  112.         if (class_exists('DB')) {
  113.             // same error also in db_prefs.php
  114.             $error  _("Could not include PEAR database functions required for the database backend.""\n";
  115.             $error .= sprintf(_("Is PEAR installed, and is the include path set correctly to find %s?"),
  116.                               'DB.php'"\n";
  117.             $error .= _("Please contact your system administrator and report this error.");
  118.             return $this->set_error($error);
  119.         }
  120.  
  121.         if (is_array($param)) {
  122.             if (empty($param['dsn']||
  123.                 empty($param['table']||
  124.                 empty($param['owner'])) {
  125.                 return $this->set_error('Invalid parameters');
  126.             }
  127.  
  128.             $this->dsn   = $param['dsn'];
  129.             $this->table = $param['table'];
  130.             $this->owner = $param['owner'];
  131.  
  132.             if (!empty($param['name'])) {
  133.                $this->sname = $param['name'];
  134.             }
  135.  
  136.             if (isset($param['writeable'])) {
  137.                $this->writeable = $param['writeable'];
  138.             }
  139.  
  140.             if (isset($param['listing'])) {
  141.                $this->listing = $param['listing'];
  142.             }
  143.  
  144.             $this->open(true);
  145.         }
  146.         else {
  147.             return $this->set_error('Invalid argument to constructor');
  148.         }
  149.     }
  150.  
  151.  
  152.     /**
  153.      * Open the database.
  154.      * @param bool $new new connection if it is true
  155.      * @return bool 
  156.      */
  157.     function open($new false{
  158.         $this->error = '';
  159.  
  160.         /* Return true is file is open and $new is unset */
  161.         if ($this->dbh && !$new{
  162.             return true;
  163.         }
  164.  
  165.         /* Close old file, if any */
  166.         if ($this->dbh{
  167.             $this->close();
  168.         }
  169.  
  170.         $dbh DB::connect($this->dsntrue);
  171.  
  172.         if (DB::isError($dbh)) {
  173.             return $this->set_error(sprintf(_("Database error: %s"),
  174.                                             DB::errorMessage($dbh)));
  175.         }
  176.  
  177.         $this->dbh = $dbh;
  178.  
  179.         /**
  180.          * field names are lowercased.
  181.          * We use unquoted identifiers and they use upper case in Oracle
  182.          */
  183.         $this->dbh->setOption('portability'DB_PORTABILITY_LOWERCASE);
  184.  
  185.         return true;
  186.     }
  187.  
  188.     /**
  189.      * Close the file and forget the filehandle
  190.      */
  191.     function close({
  192.         $this->dbh->disconnect();
  193.         $this->dbh = false;
  194.     }
  195.  
  196.     /**
  197.      * Determine internal database field name given one of
  198.      * the SquirrelMail SM_ABOOK_FIELD_* constants
  199.      *
  200.      * @param integer $field The SM_ABOOK_FIELD_* contant to look up
  201.      *
  202.      * @return string The desired field name, or the string "ERROR"
  203.      *                 if the $field is not understood (the caller
  204.      *                 is responsible for handing errors)
  205.      *
  206.      */
  207.     function get_field_name($field{
  208.         switch ($field{
  209.             case SM_ABOOK_FIELD_NICKNAME:
  210.                 return 'nickname';
  211.             case SM_ABOOK_FIELD_FIRSTNAME:
  212.                 return 'firstname';
  213.             case SM_ABOOK_FIELD_LASTNAME:
  214.                 return 'lastname';
  215.             case SM_ABOOK_FIELD_EMAIL:
  216.                 return 'email';
  217.             case SM_ABOOK_FIELD_LABEL:
  218.                 return 'label';
  219.             default:
  220.                 return 'ERROR';
  221.         }
  222.     }
  223.  
  224.     /* ========================== Public ======================== */
  225.  
  226.     /**
  227.      * Search the database
  228.      *
  229.      * Backend supports only * and ? wildcards. Complex eregs are not supported.
  230.      * Search is case insensitive.
  231.      * @param string $expr search expression
  232.      * @return array search results. boolean false on error
  233.      */
  234.     function search($expr{
  235.         $ret array();
  236.         if(!$this->open()) {
  237.             return false;
  238.         }
  239.  
  240.         /* To be replaced by advanded search expression parsing */
  241.         if (is_array($expr)) {
  242.             return;
  243.         }
  244.  
  245.         // don't allow wide search when listing is disabled.
  246.         if ($expr=='*' && $this->listing)
  247.             return array();
  248.  
  249.         /* lowercase expression in order to make it case insensitive */
  250.         $expr strtolower($expr);
  251.  
  252.         /* escape SQL wildcards */
  253.         $expr str_replace('_''\\_'$expr);
  254.         $expr str_replace('%''\\%'$expr);
  255.  
  256.         /* Convert wildcards to SQL syntax  */
  257.         $expr str_replace('?''_'$expr);
  258.         $expr str_replace('*''%'$expr);
  259.         $expr $this->dbh->quoteString($expr);
  260.         $expr "%$expr%";
  261.  
  262.         /* create escape expression */
  263.         $escape 'ESCAPE \'' $this->dbh->quoteString('\\''\'';
  264.     
  265.         $query sprintf("SELECT * FROM %s WHERE owner='%s' AND " .
  266.                          "(LOWER(firstname) LIKE '%s' %s " .
  267.                          "OR LOWER(lastname) LIKE '%s' %s " .
  268.                          "OR LOWER(email) LIKE '%s' %s " .
  269.                          "OR LOWER(nickname) LIKE '%s' %s)",
  270.                          $this->table$this->owner$expr$escape$expr$escape,
  271.                                                      $expr$escape$expr$escape);
  272.  
  273.         $res $this->dbh->query($query);
  274.  
  275.         if (DB::isError($res)) {
  276.             return $this->set_error(sprintf(_("Database error: %s"),
  277.                                             DB::errorMessage($res)));
  278.         }
  279.  
  280.         while ($row $res->fetchRow(DB_FETCHMODE_ASSOC)) {
  281.             array_push($retarray('nickname'  => $row['nickname'],
  282.                                    'name'      => $this->fullname($row['firstname']$row['lastname']),
  283.                                    'firstname' => $row['firstname'],
  284.                                    'lastname'  => $row['lastname'],
  285.                                    'email'     => $row['email'],
  286.                                    'label'     => $row['label'],
  287.                                    'backend'   => $this->bnum,
  288.                                    'source'    => &$this->sname));
  289.         }
  290.         return $ret;
  291.     }
  292.  
  293.     /**
  294.      * Lookup an address by the indicated field.
  295.      *
  296.      * @param string  $value The value to look up
  297.      * @param integer $field The field to look in, should be one
  298.      *                        of the SM_ABOOK_FIELD_* constants
  299.      *                        defined in include/constants.php
  300.      *                        (OPTIONAL; defaults to nickname field)
  301.      *                        NOTE: uniqueness is only guaranteed
  302.      *                        when the nickname field is used here;
  303.      *                        otherwise, the first matching address
  304.      *                        is returned.
  305.      *
  306.      * @return array Array with lookup results when the value
  307.      *                was found, an empty array if the value was
  308.      *                not found.
  309.      *
  310.      */
  311.     function lookup($value$field=SM_ABOOK_FIELD_NICKNAME{
  312.         if (empty($value)) {
  313.             return array();
  314.         }
  315.  
  316.         $value strtolower($value);
  317.  
  318.         if (!$this->open()) {
  319.             return false;
  320.         }
  321.  
  322.         $db_field $this->get_field_name($field);
  323.         if ($db_field == 'ERROR'{
  324.             return $this->set_error(sprintf(_("Unknown field name: %s")$field));
  325.         }
  326.  
  327.         $query sprintf("SELECT * FROM %s WHERE owner = '%s' AND LOWER(%s) = '%s'",
  328.                          $this->table$this->owner$db_field
  329.                          $this->dbh->quoteString($value));
  330.  
  331.         $res $this->dbh->query($query);
  332.  
  333.         if (DB::isError($res)) {
  334.             return $this->set_error(sprintf(_("Database error: %s"),
  335.                                             DB::errorMessage($res)));
  336.         }
  337.  
  338.         if ($row $res->fetchRow(DB_FETCHMODE_ASSOC)) {
  339.             return array('nickname'  => $row['nickname'],
  340.                          'name'      => $this->fullname($row['firstname']$row['lastname']),
  341.                          'firstname' => $row['firstname'],
  342.                          'lastname'  => $row['lastname'],
  343.                          'email'     => $row['email'],
  344.                          'label'     => $row['label'],
  345.                          'backend'   => $this->bnum,
  346.                          'source'    => &$this->sname);
  347.         }
  348.         return array();
  349.     }
  350.  
  351.     /**
  352.      * List all addresses
  353.      * @return array search results
  354.      */
  355.     function list_addr({
  356.         $ret array();
  357.         if (!$this->open()) {
  358.             return false;
  359.         }
  360.  
  361.         if(isset($this->listing&& !$this->listing{
  362.             return array();
  363.         }
  364.  
  365.  
  366.         $query sprintf("SELECT * FROM %s WHERE owner='%s'",
  367.                          $this->table$this->owner);
  368.  
  369.         $res $this->dbh->query($query);
  370.  
  371.         if (DB::isError($res)) {
  372.             return $this->set_error(sprintf(_("Database error: %s"),
  373.                                             DB::errorMessage($res)));
  374.         }
  375.  
  376.         while ($row $res->fetchRow(DB_FETCHMODE_ASSOC)) {
  377.             array_push($retarray('nickname'  => $row['nickname'],
  378.                                    'name'      => $this->fullname($row['firstname']$row['lastname']),
  379.                                    'firstname' => $row['firstname'],
  380.                                    'lastname'  => $row['lastname'],
  381.                                    'email'     => $row['email'],
  382.                                    'label'     => $row['label'],
  383.                                    'backend'   => $this->bnum,
  384.                                    'source'    => &$this->sname));
  385.         }
  386.         return $ret;
  387.     }
  388.  
  389.     /**
  390.      * Add address
  391.      * @param array $userdata added data
  392.      * @return bool 
  393.      */
  394.     function add($userdata{
  395.         if (!$this->writeable{
  396.             return $this->set_error(_("Address book is read-only"));
  397.         }
  398.  
  399.         if (!$this->open()) {
  400.             return false;
  401.         }
  402.  
  403.         /* See if user exist already */
  404.         $ret $this->lookup($userdata['nickname']);
  405.         if (!empty($ret)) {
  406.             return $this->set_error(sprintf(_("User \"%s\" already exists"),$ret['nickname']));
  407.         }
  408.  
  409.         /* Create query */
  410.         $query sprintf("INSERT INTO %s (owner, nickname, firstname, " .
  411.                          "lastname, email, label) VALUES('%s','%s','%s'," .
  412.                          "'%s','%s','%s')",
  413.                          $this->table$this->owner,
  414.                          $this->dbh->quoteString($userdata['nickname']),
  415.                          $this->dbh->quoteString($userdata['firstname']),
  416.                          $this->dbh->quoteString((!empty($userdata['lastname'])?$userdata['lastname']:'')),
  417.                          $this->dbh->quoteString($userdata['email']),
  418.                          $this->dbh->quoteString((!empty($userdata['label'])?$userdata['label']:'')) );
  419.  
  420.          /* Do the insert */
  421.          $r $this->dbh->simpleQuery($query);
  422.  
  423.          /* Check for errors */
  424.          if (DB::isError($r)) {
  425.              return $this->set_error(sprintf(_("Database error: %s"),
  426.                                              DB::errorMessage($r)));
  427.          }
  428.          return true;
  429.     }
  430.  
  431.     /**
  432.      * Deletes address book entries
  433.      * @param array $alias aliases that have to be deleted. numerical
  434.      *   array with nickname values
  435.      * @return bool 
  436.      */
  437.     function remove($alias{
  438.         if (!$this->writeable{
  439.             return $this->set_error(_("Address book is read-only"));
  440.         }
  441.  
  442.         if (!$this->open()) {
  443.             return false;
  444.         }
  445.  
  446.         /* Create query */
  447.         $query sprintf("DELETE FROM %s WHERE owner='%s' AND (",
  448.                          $this->table$this->owner);
  449.  
  450.         $sepstr '';
  451.         while (list($undef$nicknameeach($alias)) {
  452.             $query .= sprintf("%s nickname='%s' "$sepstr,
  453.                               $this->dbh->quoteString($nickname));
  454.             $sepstr 'OR';
  455.         }
  456.         $query .= ')';
  457.  
  458.         /* Delete entry */
  459.         $r $this->dbh->simpleQuery($query);
  460.  
  461.         /* Check for errors */
  462.         if (DB::isError($r)) {
  463.             return $this->set_error(sprintf(_("Database error: %s"),
  464.                                             DB::errorMessage($r)));
  465.         }
  466.         return true;
  467.     }
  468.  
  469.     /**
  470.      * Modify address
  471.      * @param string $alias modified alias
  472.      * @param array $userdata new data
  473.      * @return bool 
  474.      */
  475.     function modify($alias$userdata{
  476.         if (!$this->writeable{
  477.             return $this->set_error(_("Address book is read-only"));
  478.         }
  479.  
  480.         if (!$this->open()) {
  481.             return false;
  482.         }
  483.  
  484.          /* See if user exist */
  485.         $ret $this->lookup($alias);
  486.         if (empty($ret)) {
  487.             return $this->set_error(sprintf(_("User \"%s\" does not exist"),$alias));
  488.         }
  489.  
  490.         /* make sure that new nickname is not used */
  491.         if (strtolower($alias!= strtolower($userdata['nickname'])) {
  492.             /* same check as in add() */
  493.             $ret $this->lookup($userdata['nickname']);
  494.             if (!empty($ret)) {
  495.                 $error sprintf(_("User '%s' already exist.")$ret['nickname']);
  496.                 return $this->set_error($error);
  497.             }
  498.         }
  499.  
  500.         /* Create query */
  501.         $query sprintf("UPDATE %s SET nickname='%s', firstname='%s', ".
  502.                          "lastname='%s', email='%s', label='%s' ".
  503.                          "WHERE owner='%s' AND nickname='%s'",
  504.                          $this->table,
  505.                          $this->dbh->quoteString($userdata['nickname']),
  506.                          $this->dbh->quoteString($userdata['firstname']),
  507.                          $this->dbh->quoteString((!empty($userdata['lastname'])?$userdata['lastname']:'')),
  508.                          $this->dbh->quoteString($userdata['email']),
  509.                          $this->dbh->quoteString((!empty($userdata['label'])?$userdata['label']:'')),
  510.                          $this->owner,
  511.                          $this->dbh->quoteString($alias) );
  512.  
  513.         /* Do the insert */
  514.         $r $this->dbh->simpleQuery($query);
  515.  
  516.         /* Check for errors */
  517.         if (DB::isError($r)) {
  518.             return $this->set_error(sprintf(_("Database error: %s"),
  519.                                             DB::errorMessage($r)));
  520.         }
  521.         return true;
  522.     }
  523. /* End of class abook_database */
  524.  
  525. // vim: et ts=4

Documentation generated on Thu, 23 Oct 2014 04:17:40 +0200 by phpDocumentor 1.4.3