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-2014 The SquirrelMail Project Team
  8.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  9.  * @version $Id: addressbook.php 14420 2014-01-01 20:33:20Z 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 function.
  427.      */
  428.     function AddressBook({
  429.         $this->localbackendname = _("Personal Address Book");
  430.     }
  431.  
  432.     /**
  433.      * Return an array of backends of a given type,
  434.      * or all backends if no type is specified.
  435.      * @param string $type backend type
  436.      * @return array list of backends
  437.      */
  438.     function get_backend_list($type ''{
  439.         $ret array();
  440.         for ($i $i <= $this->numbackends $i++{
  441.             if (empty($type|| $type == $this->backends[$i]->btype{
  442.                 $ret[&$this->backends[$i];
  443.             }
  444.         }
  445.         return $ret;
  446.     }
  447.  
  448.  
  449.     /* ========================== Public ======================== */
  450.  
  451.     /**
  452.      * Add a new backend.
  453.      *
  454.      * @param string $backend backend name (without the abook_ prefix)
  455.      * @param mixed optional variable that is passed to the backend constructor.
  456.      *  See each of the backend classes for valid parameters
  457.      * @return integer number of backends
  458.      */
  459.     function add_backend($backend$param ''{
  460.         static $backend_classes;
  461.         if (!isset($backend_classes)) {
  462.             $backend_classes array();
  463.         }
  464.         if (!isset($backend_classes[$backend])) {
  465.             /**
  466.               * Support backend provided by plugins. Plugin function must
  467.               * return an associative array with as key the backend name ($backend)
  468.               * and as value the file including the path containing the backend class.
  469.               * i.e.: $aBackend = array('backend_template' => SM_PATH . 'plugins/abook_backend_template/functions.php')
  470.               *
  471.               * NB: Because the backend files are included from within this function they DO NOT have access to
  472.               * vars in the global scope. This function is the global scope for the included backend !!!
  473.               */
  474.             global $null;
  475.             $aBackend do_hook('abook_add_class'$null);
  476.             if (isset($aBackend&& is_array($aBackend&& isset($aBackend[$backend])) {
  477.                 require_once($aBackend[$backend]);
  478.             else {
  479.                 require_once(SM_PATH 'functions/abook_'.$backend.'.php');
  480.             }
  481.             $backend_classes[$backendtrue;
  482.         }
  483.         $backend_name 'abook_' $backend;
  484.         $newback new $backend_name($param);
  485.         //eval('$newback = new ' . $backend_name . '($param);');
  486.         if(!empty($newback->error)) {
  487.             $this->error = $newback->error;
  488.             return false;
  489.         }
  490.  
  491.         $this->numbackends++;
  492.  
  493.         $newback->bnum $this->numbackends;
  494.         $this->backends[$this->numbackends$newback;
  495.  
  496.         /* Store ID of first local backend added */
  497.         if ($this->localbackend == && $newback->btype == 'local'{
  498.             $this->localbackend = $this->numbackends;
  499.             $this->localbackendname = $newback->sname;
  500.         }
  501.  
  502.         return $this->numbackends;
  503.     }
  504.  
  505.  
  506.     /**
  507.      * create string with name and email address
  508.      *
  509.      * This function takes a $row array as returned by the addressbook
  510.      * search and returns an e-mail address with the full name or
  511.      * nickname optionally prepended.
  512.      * @param array $row address book entry
  513.      * @return string email address with real name prepended
  514.      */
  515.     function full_address($row{
  516.         global $data_dir$username$addrsrch_fullname;
  517.  
  518.         // allow multiple addresses in one row (poor person's grouping - bah)
  519.         // (separate with commas)
  520.         //
  521.         $return '';
  522.         $addresses explode(','$row['email']);
  523.         foreach ($addresses as $address{
  524.             
  525.             if (!empty($return)) $return .= ', ';
  526.  
  527.             if ($addrsrch_fullname == 'fullname')
  528.                 $return .= '"' $row['name''" <' trim($address'>';
  529.             else if ($addrsrch_fullname == 'nickname')
  530.                 $return .= '"' $row['nickname''" <' trim($address'>';
  531.             else // "noprefix"
  532.                 $return .= trim($address);
  533.  
  534.         }
  535.  
  536.         return $return;
  537.     }
  538.  
  539.     /**
  540.      * Search for entries in address books
  541.      *
  542.      * Return a list of addresses matching expression in
  543.      * all backends of a given type.
  544.      * @param string $expression search expression
  545.      * @param integer $bnum backend number. default to search in all backends
  546.      * @return array search results
  547.      */
  548.     function search($expression$bnum = -1{
  549.         $ret array();
  550.         $this->error = '';
  551.  
  552.         /* Search all backends */
  553.         if ($bnum == -1{
  554.             $sel $this->get_backend_list('');
  555.             $failed 0;
  556.             for ($i $i sizeof($sel$i++{
  557.                 $backend &$sel[$i];
  558.                 $backend->error '';
  559.                 $res $backend->search($expression);
  560.                 if (is_array($res)) {
  561.                     $ret array_merge($ret$res);
  562.                 else {
  563.                     $this->error .= "\n" $backend->error;
  564.                     $failed++;
  565.                 }
  566.             }
  567.  
  568.             /* Only fail if all backends failed */
  569.             if$failed >= sizeof$sel ) ) {
  570.                 $ret FALSE;
  571.             }
  572.  
  573.         elseif (isset($this->backends[$bnum])) {
  574.             /* make sure that backend exists */
  575.             $this->error = _("Unknown address book backend");
  576.             $ret false;
  577.         else {
  578.  
  579.             /* Search only one backend */
  580.  
  581.             $ret $this->backends[$bnum]->search($expression);
  582.             if (!is_array($ret)) {
  583.                 $this->error .= "\n" $this->backends[$bnum]->error;
  584.                 $ret FALSE;
  585.             }
  586.         }
  587.  
  588.         return$ret );
  589.     }
  590.  
  591.  
  592.     /**
  593.      * Sorted search
  594.      * @param string $expression search expression
  595.      * @param integer $bnum backend number. default to search in all backends
  596.      * @return array search results
  597.      */
  598.     function s_search($expression$bnum = -1{
  599.  
  600.         $ret $this->search($expression$bnum);
  601.         if is_array$ret ) ) {
  602.             usort($ret'addressbook_cmp');
  603.         }
  604.         return $ret;
  605.     }
  606.  
  607.  
  608.     /**
  609.      * Lookup an address by the indicated field.
  610.      *
  611.      * Only possible in local backends.
  612.      *
  613.      * @param string  $value The value to look up
  614.      * @param integer $bnum  The number of the backend to
  615.      *                        look within (OPTIONAL; defaults
  616.      *                        to look in all local backends)
  617.      * @param integer $field The field to look in, should be one
  618.      *                        of the SM_ABOOK_FIELD_* constants
  619.      *                        defined in include/constants.php
  620.      *                        (OPTIONAL; defaults to nickname field)
  621.      *                        NOTE: uniqueness is only guaranteed
  622.      *                        when the nickname field is used here;
  623.      *                        otherwise, the first matching address
  624.      *                        is returned.
  625.      *
  626.      * @return mixed Array with lookup results when the value
  627.      *                was found, an empty array if the value was
  628.      *                not found, or false if an error occured.
  629.      *
  630.      */
  631.     function lookup($value$bnum = -1$field SM_ABOOK_FIELD_NICKNAME{
  632.  
  633.         $ret array();
  634.  
  635.         if ($bnum > -1{
  636.             if (!isset($this->backends[$bnum])) {
  637.                 $this->error = _("Unknown address book backend");
  638.                 return false;
  639.             }
  640.             $res $this->backends[$bnum]->lookup($value$field);
  641.             if (is_array($res)) {
  642.                return $res;
  643.             else {
  644.                $this->error = $this->backends[$bnum]->error;
  645.                return false;
  646.             }
  647.         }
  648.  
  649.         $sel $this->get_backend_list('local');
  650.         for ($i $i sizeof($sel$i++{
  651.             $backend &$sel[$i];
  652.             $backend->error '';
  653.             $res $backend->lookup($value$field);
  654.  
  655.             // return an address if one is found
  656.             // (empty array means lookup concluded
  657.             // but no result found - in this case,
  658.             // proceed to next backend)
  659.             //
  660.             if (is_array($res)) {
  661.                 if (!empty($res)) return $res;
  662.             else {
  663.                 $this->error = $backend->error;
  664.                 return false;
  665.             }
  666.         }
  667.  
  668.         return $ret;
  669.     }
  670.  
  671.  
  672.     /**
  673.      * Return all addresses
  674.      * @param integer $bnum backend number
  675.      * @return mixed array with search results or boolean false on error.
  676.      */
  677.     function list_addr($bnum = -1{
  678.         $ret array();
  679.  
  680.         if ($bnum == -1{
  681.             $sel $this->get_backend_list('');
  682.         elseif (isset($this->backends[$bnum])) {
  683.             /* make sure that backend exists */
  684.             $this->error = _("Unknown address book backend");
  685.             $ret false;
  686.         else {
  687.             $sel array(=> &$this->backends[$bnum]);
  688.         }
  689.  
  690.         for ($i $i sizeof($sel$i++{
  691.             $backend &$sel[$i];
  692.             $backend->error '';
  693.             $res $backend->list_addr();
  694.             if (is_array($res)) {
  695.                $ret array_merge($ret$res);
  696.             else {
  697.                $this->error = $backend->error;
  698.                return false;
  699.             }
  700.         }
  701.  
  702.         return $ret;
  703.     }
  704.  
  705.     /**
  706.      * Create a new address
  707.      * @param array $userdata added address record
  708.      * @param integer $bnum backend number
  709.      * @return integer the backend number that the/ address was added
  710.      *  to, or false if it failed.
  711.      */
  712.     function add($userdata$bnum{
  713.  
  714.         /* Validate data */
  715.         if (!is_array($userdata)) {
  716.             $this->error = _("Invalid input data");
  717.             return false;
  718.         }
  719.         if (empty($userdata['firstname']&& empty($userdata['lastname'])) {
  720.             $this->error = _("Name is missing");
  721.             return false;
  722.         }
  723.         if (empty($userdata['email'])) {
  724.             $this->error = _("E-mail address is missing");
  725.             return false;
  726.         }
  727.         if (empty($userdata['nickname'])) {
  728.             $userdata['nickname'$userdata['email'];
  729.         }
  730.  
  731.         /* Blocks use of space, :, |, #, " and ! in nickname */
  732.         if (preg_match('/[ :|#"!]/'$userdata['nickname'])) {
  733.             $this->error = _("Nickname contains illegal characters");
  734.             return false;
  735.         }
  736.  
  737.         /* make sure that backend exists */
  738.         if (isset($this->backends[$bnum])) {
  739.             $this->error = _("Unknown address book backend");
  740.             return false;
  741.         }
  742.  
  743.         /* Check that specified backend accept new entries */
  744.         if (!$this->backends[$bnum]->writeable{
  745.             $this->error = _("Address book is read-only");
  746.             return false;
  747.         }
  748.  
  749.         /* Add address to backend */
  750.         $res $this->backends[$bnum]->add($userdata);
  751.         if ($res{
  752.             return $bnum;
  753.         else {
  754.             $this->error = $this->backends[$bnum]->error;
  755.             return false;
  756.         }
  757.  
  758.         return false;  // Not reached
  759.     /* end of add() */
  760.  
  761.  
  762.     /**
  763.      * Remove the entries from address book
  764.      * @param mixed $alias entries that have to be removed. Can be string with nickname or array with list of nicknames
  765.      * @param integer $bnum backend number
  766.      * @return bool true if removed successfully. false if there s an error. $this->error contains error message
  767.      */
  768.     function remove($alias$bnum{
  769.  
  770.         /* Check input */
  771.         if (empty($alias)) {
  772.             return true;
  773.         }
  774.  
  775.         /* Convert string to single element array */
  776.         if (!is_array($alias)) {
  777.             $alias array(=> $alias);
  778.         }
  779.  
  780.         /* make sure that backend exists */
  781.         if (isset($this->backends[$bnum])) {
  782.             $this->error = _("Unknown address book backend");
  783.             return false;
  784.         }
  785.  
  786.         /* Check that specified backend is writable */
  787.         if (!$this->backends[$bnum]->writeable{
  788.             $this->error = _("Address book is read-only");
  789.             return false;
  790.         }
  791.  
  792.         /* Remove user from backend */
  793.         $res $this->backends[$bnum]->remove($alias);
  794.         if ($res{
  795.             return $bnum;
  796.         else {
  797.             $this->error = $this->backends[$bnum]->error;
  798.             return false;
  799.         }
  800.  
  801.         return FALSE;  /* Not reached */
  802.     /* end of remove() */
  803.  
  804.  
  805.     /**
  806.      * Modify entry in address book
  807.      * @param string $alias nickname
  808.      * @param array $userdata newdata
  809.      * @param integer $bnum backend number
  810.      */
  811.     function modify($alias$userdata$bnum{
  812.  
  813.         /* Check input */
  814.         if (empty($alias|| !is_string($alias)) {
  815.             return true;
  816.         }
  817.  
  818.         /* Validate data */
  819.         if(!is_array($userdata)) {
  820.             $this->error = _("Invalid input data");
  821.             return false;
  822.         }
  823.         if (empty($userdata['firstname']&& empty($userdata['lastname'])) {
  824.             $this->error = _("Name is missing");
  825.             return false;
  826.         }
  827.         if (empty($userdata['email'])) {
  828.             $this->error = _("E-mail address is missing");
  829.             return false;
  830.         }
  831.  
  832.         if (preg_match('/[: |#"!]/'$userdata['nickname'])) {
  833.             $this->error = _("Nickname contains illegal characters");
  834.             return false;
  835.         }
  836.  
  837.         if (empty($userdata['nickname'])) {
  838.             $userdata['nickname'$userdata['email'];
  839.         }
  840.  
  841.         /* make sure that backend exists */
  842.         if (isset($this->backends[$bnum])) {
  843.             $this->error = _("Unknown address book backend");
  844.             return false;
  845.         }
  846.  
  847.         /* Check that specified backend is writable */
  848.         if (!$this->backends[$bnum]->writeable{
  849.             $this->error = _("Address book is read-only");;
  850.             return false;
  851.         }
  852.  
  853.         /* Modify user in backend */
  854.         $res $this->backends[$bnum]->modify($alias$userdata);
  855.         if ($res{
  856.             return $bnum;
  857.         else {
  858.             $this->error = $this->backends[$bnum]->error;
  859.             return false;
  860.         }
  861.  
  862.         return FALSE;  /* Not reached */
  863.     /* end of modify() */
  864.  
  865.  
  866. /* End of class Addressbook */
  867.  
  868. /**
  869.  * Generic backend that all other backends extend
  870.  * @package squirrelmail
  871.  * @subpackage addressbook
  872.  */
  873.  
  874.     /* Variables that all backends must provide. */
  875.     /**
  876.      * Backend type
  877.      *
  878.      * Can be 'local' or 'remote'
  879.      * @var string backend type
  880.      */
  881.     var $btype      = 'dummy';
  882.     /**
  883.      * Internal backend name
  884.      * @var string 
  885.      */
  886.     var $bname      = 'dummy';
  887.     /**
  888.      * Displayed backend name
  889.      * @var string 
  890.      */
  891.     var $sname      = 'Dummy backend';
  892.  
  893.     /*
  894.      * Variables common for all backends, but that
  895.      * should not be changed by the backends.
  896.      */
  897.     /**
  898.      * Backend number
  899.      * @var integer 
  900.      */
  901.     var $bnum       = -1;
  902.     /**
  903.      * Error messages
  904.      * @var string 
  905.      */
  906.     var $error      = '';
  907.     /**
  908.      * Writeable flag
  909.      * @var bool 
  910.      */
  911.     var $writeable  = false;
  912.  
  913.     /**
  914.      * Set error message
  915.      * @param string $string error message
  916.      * @return bool 
  917.      */
  918.     function set_error($string{
  919.         $this->error = '[' $this->sname . '] ' $string;
  920.         return false;
  921.     }
  922.  
  923.  
  924.     /* ========================== Public ======================== */
  925.  
  926.     /**
  927.      * Search for entries in backend
  928.      *
  929.      * Working backend should support use of wildcards. * symbol
  930.      * should match one or more symbols. ? symbol should match any
  931.      * single symbol.
  932.      * @param string $expression 
  933.      * @return bool 
  934.      */
  935.     function search($expression{
  936.         $this->set_error('search is not implemented');
  937.         return false;
  938.     }
  939.  
  940.     /**
  941.      * Find entry in backend by the indicated field
  942.      *
  943.      * @param string  $value The value to look up
  944.      * @param integer $field The field to look in, should be one
  945.      *                        of the SM_ABOOK_FIELD_* constants
  946.      *                        defined in include/constants.php
  947.      *                        NOTE: uniqueness is only guaranteed
  948.      *                        when the nickname field is used here;
  949.      *                        otherwise, the first matching address
  950.      *                        is returned.
  951.      *
  952.      * @return mixed Array with lookup results when the value
  953.      *                was found, an empty array if the value was
  954.      *                not found, or false if an error occured.
  955.      *
  956.      */
  957.     function lookup($value$field=SM_ABOOK_FIELD_NICKNAME{
  958.         $this->set_error('lookup is not implemented');
  959.         return false;
  960.     }
  961.  
  962.     /**
  963.      * List all entries in backend
  964.      *
  965.      * Working backend should provide this function or at least
  966.      * dummy function that returns empty array.
  967.      * @return bool 
  968.      */
  969.     function list_addr({
  970.         $this->set_error('list_addr is not implemented');
  971.         return false;
  972.     }
  973.  
  974.     /**
  975.      * Add entry to backend
  976.      * @param array userdata
  977.      * @return bool 
  978.      */
  979.     function add($userdata{
  980.         $this->set_error('add is not implemented');
  981.         return false;
  982.     }
  983.  
  984.     /**
  985.      * Remove entry from backend
  986.      * @param string $alias name used for id
  987.      * @return bool 
  988.      */
  989.     function remove($alias{
  990.         $this->set_error('delete is not implemented');
  991.         return false;
  992.     }
  993.  
  994.     /**
  995.      * Modify entry in backend
  996.      * @param string $alias name used for id
  997.      * @param array $newuserdata new data
  998.      * @return bool 
  999.      */
  1000.     function modify($alias$newuserdata{
  1001.         $this->set_error('modify is not implemented');
  1002.         return false;
  1003.     }
  1004.  
  1005.     /**
  1006.      * Creates full name from given name and surname
  1007.      *
  1008.      * Handles name order differences. Function always runs in SquirrelMail gettext domain.
  1009.      * Plugins don't have to switch domains before calling this function.
  1010.      * @param string $firstname given name
  1011.      * @param string $lastname surname
  1012.      * @return string full name
  1013.      * @since 1.5.2
  1014.      */
  1015.     function fullname($firstname,$lastname{
  1016.         // i18n: allows to control fullname layout in address book listing
  1017.         // first %s is for first name, second %s is for last name.
  1018.         // Translate it to '%2$s %1$s', if surname must be displayed first in your language.
  1019.         // Please note that variables can be set to empty string and extra formating 
  1020.         // (for example '%2$s, %1$s' as in 'Smith, John') might break. Use it only for 
  1021.         // setting name and surname order. scripts will remove all prepended and appended
  1022.         // whitespace.
  1023.         return trim(sprintf(dgettext('squirrelmail',"%s %s"),$firstname,$lastname));
  1024.     }
  1025. }

Documentation generated on Sat, 20 Dec 2014 04:17:11 +0100 by phpDocumentor 1.4.3