Source for file addressbook.php

Documentation is available at addressbook.php

  1. <?php
  2. /**
  3.  * functions/addressbook.php - Functions and classes for the addressbook system
  4.  *
  5.  * Functions require SM_PATH and support of forms.php functions
  6.  *
  7.  * @copyright 1999-2020 The SquirrelMail Project Team
  8.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  9.  * @version $Id: addressbook.php 14845 2020-01-07 08:09:34Z pdontthink $
  10.  * @package squirrelmail
  11.  * @subpackage addressbook
  12.  */
  13.  
  14. /**
  15.  * Create and initialize an addressbook object.
  16.  * @param boolean $showerr display any address book init errors. html page header
  17.  *  must be created before calling addressbook_init() with $showerr enabled.
  18.  * @param boolean $onlylocal enable only local address book backends. Should
  19.  *   be used when code does not need access to remote backends. Backends
  20.  *   that provide read only address books with limited listing options can be
  21.  *   tagged as remote.
  22.  * @return object address book object.
  23.  */
  24. function addressbook_init($showerr true$onlylocal false{
  25.     global $data_dir$username$ldap_server$address_book_global_filename;
  26.     global $addrbook_dsn$addrbook_table;
  27.     global $abook_file_line_length;
  28.  
  29.     /* Create a new addressbook object */
  30.     $abook new AddressBook;
  31.  
  32.     /* Create empty error message */
  33.     $abook_init_error='';
  34.  
  35.     /*
  36.         Always add a local backend. We use *either* file-based *or* a
  37.         database addressbook. If $addrbook_dsn is set, the database
  38.         backend is used. If not, addressbooks are stores in files.
  39.     */
  40.     if (isset($addrbook_dsn&& !empty($addrbook_dsn)) {
  41.         /* Database */
  42.         if (!isset($addrbook_table|| empty($addrbook_table)) {
  43.             $addrbook_table 'address';
  44.         }
  45.         $r $abook->add_backend('database'Array('dsn' => $addrbook_dsn,
  46.                             'owner' => $username,
  47.                             'table' => $addrbook_table));
  48.         if (!$r && $showerr{
  49.             $abook_init_error.=_("Error initializing address book database.""\n" $abook->error;
  50.         }
  51.     else {
  52.         /* File */
  53.         $filename getHashedFile($username$data_dir"$username.abook");
  54.         $r $abook->add_backend('local_file'Array('filename' => $filename,
  55.                                                      'umask' => 0077,
  56.                                                      'line_length' => $abook_file_line_length,
  57.                                                      'create'   => true));
  58.         if(!$r && $showerr{
  59.             // no need to use $abook->error, because message explains error.
  60.             $abook_init_error.=sprintf_("Error opening file %s")$filename );
  61.         }
  62.     }
  63.  
  64.     /* Global file based addressbook */
  65.     if (isset($abook_global_file&&
  66.         isset($abook_global_file_writeable&&
  67.         isset($abook_global_file_listing&&
  68.         trim($abook_global_file)!=''){
  69.  
  70.         // Detect place of address book
  71.         if (preg_match("/[\/\\\]/",$abook_global_file)) {
  72.             /* no path chars, address book stored in data directory
  73.              * make sure that there is a slash between data directory
  74.              * and address book file name
  75.              */
  76.             $abook_global_filename=$data_dir
  77.                 . ((substr($data_dir-1!= '/''/' '')
  78.                 . $abook_global_file;
  79.         elseif (preg_match("/^\/|\w:/",$abook_global_file)) {
  80.             // full path is set in options (starts with slash or x:)
  81.             $abook_global_filename=$abook_global_file;
  82.         else {
  83.             $abook_global_filename=SM_PATH $abook_global_file;
  84.         }
  85.  
  86.         $r $abook->add_backend('local_file',array('filename'=>$abook_global_filename,
  87.                                                     'name' => _("Global Address Book"),
  88.                                                     'detect_writeable' => false,
  89.                                                     'line_length' => $abook_file_line_length,
  90.                                                     'writeable'=> $abook_global_file_writeable,
  91.                                                     'listing' => $abook_global_file_listing));
  92.  
  93.         /* global abook init error is not fatal. add error message and continue */
  94.         if (!$r && $showerr{
  95.             if ($abook_init_error!=''$abook_init_error.="\n";
  96.             $abook_init_error.=_("Error initializing global address book.""\n" $abook->error;
  97.         }
  98.     }
  99.  
  100.     /* Load global addressbook from SQL if configured */
  101.     if (isset($addrbook_global_dsn&& !empty($addrbook_global_dsn)) {
  102.       /* Database configured */
  103.       if (!isset($addrbook_global_table|| empty($addrbook_global_table)) {
  104.           $addrbook_global_table 'global_abook';
  105.       }
  106.       $r $abook->add_backend('database',
  107.                                Array('dsn' => $addrbook_global_dsn,
  108.                                      'owner' => 'global',
  109.                                      'name' => _("Global Address Book"),
  110.                                      'writeable' => $addrbook_global_writeable,
  111.                                      'listing' => $addrbook_global_listing,
  112.                                      'table' => $addrbook_global_table));
  113.       /* global abook init error is not fatal. add error message and continue */
  114.       if (!$r && $showerr{
  115.           if ($abook_init_error!=''$abook_init_error.="\n";
  116.           $abook_init_error.=_("Error initializing global address book.""\n" $abook->error;
  117.       }
  118.     }
  119.  
  120.     /*
  121.      * hook allows to include different address book backends.
  122.      * plugins should extract $abook and $r from arguments
  123.      * and use same add_backend commands as above functions.
  124.      * Since 1.5.2 hook sends third ($onlylocal) argument to address book
  125.      * plugins in order to allow detection of local address book init.
  126.      * @since 1.5.1 and 1.4.5
  127.      * Since 1.5.2, the plugin arguments are passed inside an array
  128.      * and by reference, so plugins hooking in here need to accept arguments
  129.      * in an array and change those values as needed instead of returning
  130.      * the changed values.
  131.      */
  132.     $temp array(&$abook&$r&$onlylocal);
  133.     do_hook('abook_init'$temp);
  134.     if (!$r && $showerr{
  135.         if ($abook_init_error!=''$abook_init_error.="\n";
  136.         $abook_init_error.=_("Error initializing other address books.""\n" $abook->error;
  137.     }
  138.  
  139.     /* Load configured LDAP servers (if PHP has LDAP support) */
  140.     if (isset($ldap_server&& is_array($ldap_server)) {
  141.         reset($ldap_server);
  142.         while (list($undef,$parameach($ldap_server)) {
  143.             if (!is_array($param))
  144.                 continue;
  145.  
  146.             /* if onlylocal is true, we only add writeable ldap servers */
  147.             if ($onlylocal && (!isset($param['writeable']|| $param['writeable'!= true))
  148.                 continue;
  149.  
  150.             $r $abook->add_backend('ldap_server'$param);
  151.             if (!$r && $showerr{
  152.                 if ($abook_init_error!=''$abook_init_error.="\n";
  153.                 $abook_init_error.=sprintf(_("Error initializing LDAP server %s:")$param['host'])."\n";
  154.                 $abook_init_error.= $abook->error;
  155.             }
  156.         }
  157.     // end of ldap server init
  158.  
  159.     /**
  160.      * display address book init errors.
  161.      */
  162.     if ($abook_init_error!='' && $showerr{
  163.         error_box(nl2br(sm_encode_html_special_chars($abook_init_error)));
  164.     }
  165.  
  166.     /* Return the initialized object */
  167.     return $abook;
  168. }
  169.  
  170. /**
  171.  * Constructs the "new address" form
  172.  *
  173.  * NOTE!  The form is not closed - the caller
  174.  *        must add the closing form tag itself.
  175.  *
  176.  * @since 1.5.1
  177.  *
  178.  * @param string $form_url Form action url
  179.  * @param string $name     Form name
  180.  * @param string $title    Form title
  181.  * @param string $button   Form button name
  182.  * @param int    $backend  The current backend being displayed
  183.  * @param array  $defdata  Values of form fields
  184.  *
  185.  * @return string The desired address form display code
  186.  *
  187.  */
  188. function abook_create_form($form_url$name$title$button,
  189.                            $backend$defdata=array()) {
  190.  
  191.     global $oTemplate;
  192.  
  193.     $output addForm($form_url'post''f_add'''''array()TRUE);
  194.  
  195.     if ($button == _("Update address")) {
  196.         $edit true;
  197.         $backends NULL;
  198.     else {
  199.         $edit false;
  200.         $backends getWritableBackends();
  201.     }
  202.     
  203.     $fields array (
  204.                         'nickname'  => 'NickName',
  205.                         'firstname' => 'FirstName',
  206.                         'lastname'  => 'LastName',
  207.                         'email'     => 'Email',
  208.                         'label'     => 'Info',
  209.                     );
  210.     $values array();
  211.     foreach ($fields as $sqm=>$template{
  212.         $values[$template= isset($defdata[$sqm]$defdata[$sqm'';
  213.     }
  214.     
  215.     $oTemplate->assign('writable_backends'$backends);
  216.     $oTemplate->assign('values'$values);
  217.     $oTemplate->assign('edit'$edit);
  218.     $oTemplate->assign('current_backend'$backend);
  219.     
  220.     $output .= $oTemplate->fetch('addrbook_addedit.tpl');
  221.  
  222.     return $output;
  223. }
  224.  
  225.  
  226. /**
  227.  *   Had to move this function outside of the Addressbook Class
  228.  *   PHP 4.0.4 Seemed to be having problems with inline functions.
  229.  *   Note: this can return now since we don't support 4.0.4 anymore.
  230.  */
  231. function addressbook_cmp($a,$b{
  232.  
  233.     if($a['backend'$b['backend']{
  234.         return 1;
  235.     else if($a['backend'$b['backend']{
  236.         return -1;
  237.     }
  238.  
  239.     return (strtolower($a['name']strtolower($b['name'])) : -1;
  240.  
  241. }
  242.  
  243. /**
  244.  * Retrieve a list of writable backends
  245.  * @since 1.5.2
  246.  */
  247. function getWritableBackends ({
  248.     global $abook;
  249.     
  250.     $write array();
  251.     $backends $abook->get_backend_list();
  252.     while (list($undef,$veach($backends)) {
  253.         if ($v->writeable{
  254.             $write[$v->bnum]=$v->sname;
  255.         }
  256.     }
  257.  
  258.     return $write;
  259. }
  260.  
  261. /**
  262.  * Sort array by the key "name"
  263.  */
  264. function alistcmp($a,$b{
  265.     $abook_sort_order=get_abook_sort();
  266.  
  267.     switch ($abook_sort_order{
  268.     case 0:
  269.     case 1:
  270.       $abook_sort='nickname';
  271.       break;
  272.     case 4:
  273.     case 5:
  274.       $abook_sort='email';
  275.       break;
  276.     case 6:
  277.     case 7:
  278.       $abook_sort='label';
  279.       break;
  280.     case 2:
  281.     case 3:
  282.     case 8:
  283.     default:
  284.       $abook_sort='name';
  285.     }
  286.  
  287.     if ($a['backend'$b['backend']{
  288.         return 1;
  289.     else {
  290.         if ($a['backend'$b['backend']{
  291.             return -1;
  292.         }
  293.     }
  294.  
  295.     if( (($abook_sort_order+22== 1{
  296.       return (strtolower($a[$abook_sort]strtolower($b[$abook_sort])) : -1;
  297.     else {
  298.       return (strtolower($a[$abook_sort]strtolower($b[$abook_sort])) : -1;
  299.     }
  300. }
  301.  
  302. /**
  303.  * Address book sorting options
  304.  *
  305.  * returns address book sorting order
  306.  * @return integer book sorting options order
  307.  */
  308. function get_abook_sort({
  309.     global $data_dir$username;
  310.  
  311.     /* get sorting order */
  312.     if(sqgetGlobalVar('abook_sort_order'$tempSQ_GET)) {
  313.       $abook_sort_order = (int) $temp;
  314.  
  315.       if ($abook_sort_order or $abook_sort_order 8)
  316.         $abook_sort_order=8;
  317.  
  318.       setPref($data_dir$username'abook_sort_order'$abook_sort_order);
  319.     else {
  320.       /* get previous sorting options. default to unsorted */
  321.       $abook_sort_order getPref($data_dir$username'abook_sort_order'8);
  322.     }
  323.  
  324.     return $abook_sort_order;
  325. }
  326.  
  327. /**
  328.  * This function shows the address book sort button.
  329.  *
  330.  * @param integer $abook_sort_order Current sort value
  331.  * @param string  $alt_tag          The alt tag value (string
  332.  *                                   visible to text only browsers)
  333.  * @param integer $Down             Sort value when list is sorted
  334.  *                                   ascending
  335.  * @param integer $Up               Sort value when list is sorted
  336.  *                                   descending
  337.  * @param array   $uri_extra        Any additional parameters to add
  338.  *                                   to the button's link, as an
  339.  *                                   associative array of key/value pairs
  340.  *                                   (OPTIONAL; default none)
  341.  *
  342.  * @return string html code with sorting images and urls
  343.  *
  344.  */
  345. function show_abook_sort_button($abook_sort_order$alt_tag,
  346.                                 $Down$Up$uri_extra=array() ) {
  347.  
  348.     global $form_url$icon_theme_path;
  349.  
  350.      /* Figure out which image we want to use. */
  351.     if ($abook_sort_order != $Up && $abook_sort_order != $Down{
  352.         $img 'sort_none.png';
  353.         $text_icon '&#9723;'// U+25FB WHITE MEDIUM SQUARE
  354.         $which $Up;
  355.     elseif ($abook_sort_order == $Up{
  356.         $img 'up_pointer.png';
  357.         $text_icon '&#8679;'// U+21E7 UPWARDS WHITE ARROW
  358.         $which $Down;
  359.     else {
  360.         $img 'down_pointer.png';
  361.         $text_icon '&#8681;'// U+21E9 DOWNWARDS WHITE ARROW
  362.         $which 8;
  363.     }
  364.  
  365.     $uri_extra['abook_sort_order'$which;
  366.     $uri set_uri_vars($form_url$uri_extraFALSE);
  367.  
  368.     /* Now that we have everything figured out, show the actual button. */
  369.     return create_hyperlink($uri,
  370.                             getIcon($icon_theme_path$img$text_icon$alt_tag),
  371.                             '''''''''',
  372.                             array('style' => 'text-decoration:none',
  373.                                   'title' => $alt_tag),
  374.                             FALSE);
  375. }
  376.  
  377.  
  378. /**
  379.  * This is the main address book class that connect all the
  380.  * backends and provide services to the functions above.
  381.  * @package squirrelmail
  382.  * @subpackage addressbook
  383.  */
  384. class AddressBook {
  385.     /**
  386.      * Enabled address book backends
  387.      * @var array 
  388.      */
  389.     var $backends    = array();
  390.     /**
  391.      * Number of enabled backends
  392.      * @var integer 
  393.      */
  394.     var $numbackends = 0;
  395.     /**
  396.      * Error messages
  397.      * @var string 
  398.      */
  399.     var $error       = '';
  400.     /**
  401.      * id of backend with personal address book
  402.      * @var integer 
  403.      */
  404.     var $localbackend = 0;
  405.     /**
  406.      * Name of backend with personal address book
  407.      * @var string 
  408.      */
  409.     var $localbackendname = '';
  410.     /**
  411.      * Controls use of 'extra' field
  412.      *
  413.      * Extra field can be used to add link to form, which allows
  414.      * to modify all fields supported by backend. This is the only field
  415.      * that is not sanitized with sm_encode_html_special_chars. Backends MUST make
  416.      * sure that field data is sanitized and displayed correctly inside
  417.      * table cell. Use of html formating in other address book fields is
  418.      * not allowed. Backends that don't return 'extra' row in address book
  419.      * data should not modify this object property.
  420.      * @var boolean 
  421.      * @since 1.5.1
  422.      */
  423.     var $add_extra_field = false;
  424.  
  425.     /**
  426.      * Constructor (PHP5 style, required in some future version of PHP)
  427.      */
  428.     function __construct({
  429.         $this->localbackendname = _("Personal Address Book");
  430.     }
  431.  
  432.     /**
  433.      * Constructor (PHP4 style, kept for compatibility reasons)
  434.      */
  435.     function AddressBook({
  436.         self::__construct();
  437.     }
  438.  
  439.     /**
  440.      * Return an array of backends of a given type,
  441.      * or all backends if no type is specified.
  442.      * @param string $type backend type
  443.      * @return array list of backends
  444.      */
  445.     function get_backend_list($type ''{
  446.         $ret array();
  447.         for ($i $i <= $this->numbackends $i++{
  448.             if (empty($type|| $type == $this->backends[$i]->btype{
  449.                 $ret[&$this->backends[$i];
  450.             }
  451.         }
  452.         return $ret;
  453.     }
  454.  
  455.  
  456.     /* ========================== Public ======================== */
  457.  
  458.     /**
  459.      * Add a new backend.
  460.      *
  461.      * @param string $backend backend name (without the abook_ prefix)
  462.      * @param mixed optional variable that is passed to the backend constructor.
  463.      *  See each of the backend classes for valid parameters
  464.      * @return integer number of backends
  465.      */
  466.     function add_backend($backend$param ''{
  467.         static $backend_classes;
  468.         if (!isset($backend_classes)) {
  469.             $backend_classes array();
  470.         }
  471.         if (!isset($backend_classes[$backend])) {
  472.             /**
  473.               * Support backend provided by plugins. Plugin function must
  474.               * return an associative array with as key the backend name ($backend)
  475.               * and as value the file including the path containing the backend class.
  476.               * i.e.: $aBackend = array('backend_template' => SM_PATH . 'plugins/abook_backend_template/functions.php')
  477.               *
  478.               * NB: Because the backend files are included from within this function they DO NOT have access to
  479.               * vars in the global scope. This function is the global scope for the included backend !!!
  480.               */
  481.             global $null;
  482.             $aBackend do_hook('abook_add_class'$null);
  483.             if (isset($aBackend&& is_array($aBackend&& isset($aBackend[$backend])) {
  484.                 require_once($aBackend[$backend]);
  485.             else {
  486.                 require_once(SM_PATH 'functions/abook_'.$backend.'.php');
  487.             }
  488.             $backend_classes[$backendtrue;
  489.         }
  490.         $backend_name 'abook_' $backend;
  491.         $newback new $backend_name($param);
  492.         //eval('$newback = new ' . $backend_name . '($param);');
  493.         if(!empty($newback->error)) {
  494.             $this->error = $newback->error;
  495.             return false;
  496.         }
  497.  
  498.         $this->numbackends++;
  499.  
  500.         $newback->bnum $this->numbackends;
  501.         $this->backends[$this->numbackends$newback;
  502.  
  503.         /* Store ID of first local backend added */
  504.         if ($this->localbackend == && $newback->btype == 'local'{
  505.             $this->localbackend = $this->numbackends;
  506.             $this->localbackendname = $newback->sname;
  507.         }
  508.  
  509.         return $this->numbackends;
  510.     }
  511.  
  512.  
  513.     /**
  514.      * create string with name and email address
  515.      *
  516.      * This function takes a $row array as returned by the addressbook
  517.      * search and returns an e-mail address with the full name or
  518.      * nickname optionally prepended.
  519.      * @param array $row address book entry
  520.      * @return string email address with real name prepended
  521.      */
  522.     static function full_address($row{
  523.         global $data_dir$username$addrsrch_fullname;
  524.  
  525.         // allow multiple addresses in one row (poor person's grouping - bah)
  526.         // (separate with commas)
  527.         //
  528.         $return '';
  529.         $addresses explode(','$row['email']);
  530.         foreach ($addresses as $address{
  531.             
  532.             if (!empty($return)) $return .= ', ';
  533.  
  534.             if ($addrsrch_fullname == 'fullname')
  535.                 $return .= '"' $row['name''" <' trim($address'>';
  536.             else if ($addrsrch_fullname == 'nickname')
  537.                 $return .= '"' $row['nickname''" <' trim($address'>';
  538.             else // "noprefix"
  539.                 $return .= trim($address);
  540.  
  541.         }
  542.  
  543.         return $return;
  544.     }
  545.  
  546.     /**
  547.      * Search for entries in address books
  548.      *
  549.      * Return a list of addresses matching expression in
  550.      * all backends of a given type.
  551.      * @param string $expression search expression
  552.      * @param integer $bnum backend number. default to search in all backends
  553.      * @return array search results
  554.      */
  555.     function search($expression$bnum = -1{
  556.         $ret array();
  557.         $this->error = '';
  558.  
  559.         /* Search all backends */
  560.         if ($bnum == -1{
  561.             $sel $this->get_backend_list('');
  562.             $failed 0;
  563.             for ($i $i sizeof($sel$i++{
  564.                 $backend &$sel[$i];
  565.                 $backend->error '';
  566.                 $res $backend->search($expression);
  567.                 if (is_array($res)) {
  568.                     $ret array_merge($ret$res);
  569.                 else {
  570.                     $this->error .= "\n" $backend->error;
  571.                     $failed++;
  572.                 }
  573.             }
  574.  
  575.             /* Only fail if all backends failed */
  576.             if$failed >= sizeof$sel ) ) {
  577.                 $ret FALSE;
  578.             }
  579.  
  580.         elseif (isset($this->backends[$bnum])) {
  581.             /* make sure that backend exists */
  582.             $this->error = _("Unknown address book backend");
  583.             $ret false;
  584.         else {
  585.  
  586.             /* Search only one backend */
  587.  
  588.             $ret $this->backends[$bnum]->search($expression);
  589.             if (!is_array($ret)) {
  590.                 $this->error .= "\n" $this->backends[$bnum]->error;
  591.                 $ret FALSE;
  592.             }
  593.         }
  594.  
  595.         return$ret );
  596.     }
  597.  
  598.  
  599.     /**
  600.      * Sorted search
  601.      * @param string $expression search expression
  602.      * @param integer $bnum backend number. default to search in all backends
  603.      * @return array search results
  604.      */
  605.     function s_search($expression$bnum = -1{
  606.  
  607.         $ret $this->search($expression$bnum);
  608.         if is_array$ret ) ) {
  609.             usort($ret'addressbook_cmp');
  610.         }
  611.         return $ret;
  612.     }
  613.  
  614.  
  615.     /**
  616.      * Lookup an address by the indicated field.
  617.      *
  618.      * Only possible in local backends.
  619.      *
  620.      * @param string  $value The value to look up
  621.      * @param integer $bnum  The number of the backend to
  622.      *                        look within (OPTIONAL; defaults
  623.      *                        to look in all local backends)
  624.      * @param integer $field The field to look in, should be one
  625.      *                        of the SM_ABOOK_FIELD_* constants
  626.      *                        defined in include/constants.php
  627.      *                        (OPTIONAL; defaults to nickname field)
  628.      *                        NOTE: uniqueness is only guaranteed
  629.      *                        when the nickname field is used here;
  630.      *                        otherwise, the first matching address
  631.      *                        is returned.
  632.      *
  633.      * @return mixed Array with lookup results when the value
  634.      *                was found, an empty array if the value was
  635.      *                not found, or false if an error occured.
  636.      *
  637.      */
  638.     function lookup($value$bnum = -1$field SM_ABOOK_FIELD_NICKNAME{
  639.  
  640.         $ret array();
  641.  
  642.         if ($bnum > -1{
  643.             if (!isset($this->backends[$bnum])) {
  644.                 $this->error = _("Unknown address book backend");
  645.                 return false;
  646.             }
  647.             $res $this->backends[$bnum]->lookup($value$field);
  648.             if (is_array($res)) {
  649.                return $res;
  650.             else {
  651.                $this->error = $this->backends[$bnum]->error;
  652.                return false;
  653.             }
  654.         }
  655.  
  656.         $sel $this->get_backend_list('local');
  657.         for ($i $i sizeof($sel$i++{
  658.             $backend &$sel[$i];
  659.             $backend->error '';
  660.             $res $backend->lookup($value$field);
  661.  
  662.             // return an address if one is found
  663.             // (empty array means lookup concluded
  664.             // but no result found - in this case,
  665.             // proceed to next backend)
  666.             //
  667.             if (is_array($res)) {
  668.                 if (!empty($res)) return $res;
  669.             else {
  670.                 $this->error = $backend->error;
  671.                 return false;
  672.             }
  673.         }
  674.  
  675.         return $ret;
  676.     }
  677.  
  678.  
  679.     /**
  680.      * Return all addresses
  681.      * @param integer $bnum backend number
  682.      * @return mixed array with search results or boolean false on error.
  683.      */
  684.     function list_addr($bnum = -1{
  685.         $ret array();
  686.  
  687.         if ($bnum == -1{
  688.             $sel $this->get_backend_list('');
  689.         elseif (isset($this->backends[$bnum])) {
  690.             /* make sure that backend exists */
  691.             $this->error = _("Unknown address book backend");
  692.             $ret false;
  693.         else {
  694.             $sel array(=> &$this->backends[$bnum]);
  695.         }
  696.  
  697.         for ($i $i sizeof($sel$i++{
  698.             $backend &$sel[$i];
  699.             $backend->error '';
  700.             $res $backend->list_addr();
  701.             if (is_array($res)) {
  702.                $ret array_merge($ret$res);
  703.             else {
  704.                $this->error = $backend->error;
  705.                return false;
  706.             }
  707.         }
  708.  
  709.         return $ret;
  710.     }
  711.  
  712.     /**
  713.      * Create a new address
  714.      * @param array $userdata added address record
  715.      * @param integer $bnum backend number
  716.      * @return integer the backend number that the/ address was added
  717.      *  to, or false if it failed.
  718.      */
  719.     function add($userdata$bnum{
  720.  
  721.         /* Validate data */
  722.         if (!is_array($userdata)) {
  723.             $this->error = _("Invalid input data");
  724.             return false;
  725.         }
  726.         if (empty($userdata['firstname']&& empty($userdata['lastname'])) {
  727.             $this->error = _("Name is missing");
  728.             return false;
  729.         }
  730.         if (empty($userdata['email'])) {
  731.             $this->error = _("E-mail address is missing");
  732.             return false;
  733.         }
  734.         if (empty($userdata['nickname'])) {
  735.             $userdata['nickname'$userdata['email'];
  736.         }
  737.  
  738.         /* Blocks use of space, :, |, #, " and ! in nickname */
  739.         if (preg_match('/[ :|#"!]/'$userdata['nickname'])) {
  740.             $this->error = _("Nickname contains illegal characters");
  741.             return false;
  742.         }
  743.  
  744.         /* make sure that backend exists */
  745.         if (isset($this->backends[$bnum])) {
  746.             $this->error = _("Unknown address book backend");
  747.             return false;
  748.         }
  749.  
  750.         /* Check that specified backend accept new entries */
  751.         if (!$this->backends[$bnum]->writeable{
  752.             $this->error = _("Address book is read-only");
  753.             return false;
  754.         }
  755.  
  756.         /* Add address to backend */
  757.         $res $this->backends[$bnum]->add($userdata);
  758.         if ($res{
  759.             return $bnum;
  760.         else {
  761.             $this->error = $this->backends[$bnum]->error;
  762.             return false;
  763.         }
  764.  
  765.         return false;  // Not reached
  766.     /* end of add() */
  767.  
  768.  
  769.     /**
  770.      * Remove the entries from address book
  771.      * @param mixed $alias entries that have to be removed. Can be string with nickname or array with list of nicknames
  772.      * @param integer $bnum backend number
  773.      * @return bool true if removed successfully. false if there s an error. $this->error contains error message
  774.      */
  775.     function remove($alias$bnum{
  776.  
  777.         /* Check input */
  778.         if (empty($alias)) {
  779.             return true;
  780.         }
  781.  
  782.         /* Convert string to single element array */
  783.         if (!is_array($alias)) {
  784.             $alias array(=> $alias);
  785.         }
  786.  
  787.         /* make sure that backend exists */
  788.         if (isset($this->backends[$bnum])) {
  789.             $this->error = _("Unknown address book backend");
  790.             return false;
  791.         }
  792.  
  793.         /* Check that specified backend is writable */
  794.         if (!$this->backends[$bnum]->writeable{
  795.             $this->error = _("Address book is read-only");
  796.             return false;
  797.         }
  798.  
  799.         /* Remove user from backend */
  800.         $res $this->backends[$bnum]->remove($alias);
  801.         if ($res{
  802.             return $bnum;
  803.         else {
  804.             $this->error = $this->backends[$bnum]->error;
  805.             return false;
  806.         }
  807.  
  808.         return FALSE;  /* Not reached */
  809.     /* end of remove() */
  810.  
  811.  
  812.     /**
  813.      * Modify entry in address book
  814.      * @param string $alias nickname
  815.      * @param array $userdata newdata
  816.      * @param integer $bnum backend number
  817.      */
  818.     function modify($alias$userdata$bnum{
  819.  
  820.         /* Check input */
  821.         if (empty($alias|| !is_string($alias)) {
  822.             return true;
  823.         }
  824.  
  825.         /* Validate data */
  826.         if(!is_array($userdata)) {
  827.             $this->error = _("Invalid input data");
  828.             return false;
  829.         }
  830.         if (empty($userdata['firstname']&& empty($userdata['lastname'])) {
  831.             $this->error = _("Name is missing");
  832.             return false;
  833.         }
  834.         if (empty($userdata['email'])) {
  835.             $this->error = _("E-mail address is missing");
  836.             return false;
  837.         }
  838.  
  839.         if (preg_match('/[: |#"!]/'$userdata['nickname'])) {
  840.             $this->error = _("Nickname contains illegal characters");
  841.             return false;
  842.         }
  843.  
  844.         if (empty($userdata['nickname'])) {
  845.             $userdata['nickname'$userdata['email'];
  846.         }
  847.  
  848.         /* make sure that backend exists */
  849.         if (isset($this->backends[$bnum])) {
  850.             $this->error = _("Unknown address book backend");
  851.             return false;
  852.         }
  853.  
  854.         /* Check that specified backend is writable */
  855.         if (!$this->backends[$bnum]->writeable{
  856.             $this->error = _("Address book is read-only");;
  857.             return false;
  858.         }
  859.  
  860.         /* Modify user in backend */
  861.         $res $this->backends[$bnum]->modify($alias$userdata);
  862.         if ($res{
  863.             return $bnum;
  864.         else {
  865.             $this->error = $this->backends[$bnum]->error;
  866.             return false;
  867.         }
  868.  
  869.         return FALSE;  /* Not reached */
  870.     /* end of modify() */
  871.  
  872.  
  873. /* End of class Addressbook */
  874.  
  875. /**
  876.  * Generic backend that all other backends extend
  877.  * @package squirrelmail
  878.  * @subpackage addressbook
  879.  */
  880.  
  881.     /* Variables that all backends must provide. */
  882.     /**
  883.      * Backend type
  884.      *
  885.      * Can be 'local' or 'remote'
  886.      * @var string backend type
  887.      */
  888.     var $btype      = 'dummy';
  889.     /**
  890.      * Internal backend name
  891.      * @var string 
  892.      */
  893.     var $bname      = 'dummy';
  894.     /**
  895.      * Displayed backend name
  896.      * @var string 
  897.      */
  898.     var $sname      = 'Dummy backend';
  899.  
  900.     /*
  901.      * Variables common for all backends, but that
  902.      * should not be changed by the backends.
  903.      */
  904.     /**
  905.      * Backend number
  906.      * @var integer 
  907.      */
  908.     var $bnum       = -1;
  909.     /**
  910.      * Error messages
  911.      * @var string 
  912.      */
  913.     var $error      = '';
  914.     /**
  915.      * Writeable flag
  916.      * @var bool 
  917.      */
  918.     var $writeable  = false;
  919.  
  920.     /**
  921.      * Set error message
  922.      * @param string $string error message
  923.      * @return bool 
  924.      */
  925.     function set_error($string{
  926.         $this->error = '[' $this->sname . '] ' $string;
  927.         return false;
  928.     }
  929.  
  930.  
  931.     /* ========================== Public ======================== */
  932.  
  933.     /**
  934.      * Search for entries in backend
  935.      *
  936.      * Working backend should support use of wildcards. * symbol
  937.      * should match one or more symbols. ? symbol should match any
  938.      * single symbol.
  939.      * @param string $expression 
  940.      * @return bool 
  941.      */
  942.     function search($expression{
  943.         $this->set_error('search is not implemented');
  944.         return false;
  945.     }
  946.  
  947.     /**
  948.      * Find entry in backend by the indicated field
  949.      *
  950.      * @param string  $value The value to look up
  951.      * @param integer $field The field to look in, should be one
  952.      *                        of the SM_ABOOK_FIELD_* constants
  953.      *                        defined in include/constants.php
  954.      *                        NOTE: uniqueness is only guaranteed
  955.      *                        when the nickname field is used here;
  956.      *                        otherwise, the first matching address
  957.      *                        is returned.
  958.      *
  959.      * @return mixed Array with lookup results when the value
  960.      *                was found, an empty array if the value was
  961.      *                not found, or false if an error occured.
  962.      *
  963.      */
  964.     function lookup($value$field=SM_ABOOK_FIELD_NICKNAME{
  965.         $this->set_error('lookup is not implemented');
  966.         return false;
  967.     }
  968.  
  969.     /**
  970.      * List all entries in backend
  971.      *
  972.      * Working backend should provide this function or at least
  973.      * dummy function that returns empty array.
  974.      * @return bool 
  975.      */
  976.     function list_addr({
  977.         $this->set_error('list_addr is not implemented');
  978.         return false;
  979.     }
  980.  
  981.     /**
  982.      * Add entry to backend
  983.      * @param array userdata
  984.      * @return bool 
  985.      */
  986.     function add($userdata{
  987.         $this->set_error('add is not implemented');
  988.         return false;
  989.     }
  990.  
  991.     /**
  992.      * Remove entry from backend
  993.      * @param string $alias name used for id
  994.      * @return bool 
  995.      */
  996.     function remove($alias{
  997.         $this->set_error('delete is not implemented');
  998.         return false;
  999.     }
  1000.  
  1001.     /**
  1002.      * Modify entry in backend
  1003.      * @param string $alias name used for id
  1004.      * @param array $newuserdata new data
  1005.      * @return bool 
  1006.      */
  1007.     function modify($alias$newuserdata{
  1008.         $this->set_error('modify is not implemented');
  1009.         return false;
  1010.     }
  1011.  
  1012.     /**
  1013.      * Creates full name from given name and surname
  1014.      *
  1015.      * Handles name order differences. Function always runs in SquirrelMail gettext domain.
  1016.      * Plugins don't have to switch domains before calling this function.
  1017.      * @param string $firstname given name
  1018.      * @param string $lastname surname
  1019.      * @return string full name
  1020.      * @since 1.5.2
  1021.      */
  1022.     function fullname($firstname,$lastname{
  1023.         // i18n: allows to control fullname layout in address book listing
  1024.         // first %s is for first name, second %s is for last name.
  1025.         // Translate it to '%2$s %1$s', if surname must be displayed first in your language.
  1026.         // Please note that variables can be set to empty string and extra formating 
  1027.         // (for example '%2$s, %1$s' as in 'Smith, John') might break. Use it only for 
  1028.         // setting name and surname order. scripts will remove all prepended and appended
  1029.         // whitespace.
  1030.         return trim(sprintf(dgettext('squirrelmail',"%s %s"),$firstname,$lastname));
  1031.     }
  1032. }

Documentation generated on Mon, 13 Jan 2020 04:21:59 +0100 by phpDocumentor 1.4.3