Source for file addressbook.php

Documentation is available at addressbook.php

  1. <?php
  2.  
  3. /**
  4.  * functions/addressbook.php - Functions and classes for the addressbook system
  5.  *
  6.  * Functions require SM_PATH and support of forms.php functions
  7.  *
  8.  * @copyright 1999-2020 The SquirrelMail Project Team
  9.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10.  * @version $Id: addressbook.php 14840 2020-01-07 07:42:38Z pdontthink $
  11.  * @package squirrelmail
  12.  * @subpackage addressbook
  13.  */
  14.  
  15. /**
  16.  * If SM_PATH isn't defined, define it.  Required to include files.
  17.  * @ignore
  18.  */
  19. if (!defined('SM_PATH'))  {
  20.     define('SM_PATH','../');
  21. }
  22.  
  23. /* make sure that display_messages.php is loaded */
  24. include_once(SM_PATH 'functions/display_messages.php');
  25.  
  26. global $addrbook_dsn$addrbook_global_dsn;
  27.  
  28. /**
  29.    Create and initialize an addressbook object.
  30.    Returns the created object
  31. */
  32. function addressbook_init($showerr true$onlylocal false{
  33.     global $data_dir$username$color$ldap_server$address_book_global_filename;
  34.     global $addrbook_dsn$addrbook_table;
  35.     // Shared file based address book globals
  36.     // Shared DB based address book globals
  37.     // Record size restriction in file based address books
  38.     global $abook_file_line_length;
  39.  
  40.     /* Create a new addressbook object */
  41.     $abook new AddressBook;
  42.  
  43.     /* Create empty error message */
  44.     $abook_init_error='';
  45.  
  46.     /*
  47.         Always add a local backend. We use *either* file-based *or* a
  48.         database addressbook. If $addrbook_dsn is set, the database
  49.         backend is used. If not, addressbooks are stores in files.
  50.     */
  51.     if (isset($addrbook_dsn&& !empty($addrbook_dsn)) {
  52.         /* Database */
  53.         if (!isset($addrbook_table|| empty($addrbook_table)) {
  54.             $addrbook_table 'address';
  55.         }
  56.         $r $abook->add_backend('database'Array('dsn' => $addrbook_dsn,
  57.                             'owner' => $username,
  58.                             'table' => $addrbook_table));
  59.         if (!$r && $showerr{
  60.             $abook_init_error.=_("Error initializing address book database.".' '$abook->error;
  61.         }
  62.     else {
  63.         /* File */
  64.         $filename getHashedFile($username$data_dir"$username.abook");
  65.         $r $abook->add_backend('local_file'Array('filename' => $filename,
  66.                                                      'umask' => 0077,
  67.                                                      'line_length' => $abook_file_line_length,
  68.                                                      'create'   => true));
  69.         if(!$r && $showerr{
  70.             $abook_init_error.=sprintf_("Error opening file %s")$filename );
  71.         }
  72.     }
  73.  
  74.     /* This would be for the global addressbook */
  75.     if (isset($abook_global_file&& isset($abook_global_file_writeable)
  76.         && trim($abook_global_file)!=''){
  77.         // Detect place of address book
  78.         if (preg_match("/[\/\\\]/",$abook_global_file)) {
  79.             /* no path chars, address book stored in data directory
  80.              * make sure that there is a slash between data directory
  81.              * and address book file name
  82.              */
  83.             $abook_global_filename=$data_dir
  84.                 . ((substr($data_dir-1!= '/''/' '')
  85.                 . $abook_global_file;
  86.         elseif (preg_match("/^\/|\w:/",$abook_global_file)) {
  87.             // full path is set in options (starts with slash or x:)
  88.             $abook_global_filename=$abook_global_file;
  89.         else {
  90.             $abook_global_filename=SM_PATH $abook_global_file;
  91.         }
  92.         $r $abook->add_backend('local_file',array('filename'=>$abook_global_filename,
  93.                                                     'name' => _("Global address book"),
  94.                                                     'detect_writeable' => false,
  95.                                                     'line_length' => $abook_file_line_length,
  96.                                                     'writeable'=> $abook_global_file_writeable,
  97.                                                     'listing' => $abook_global_file_listing));
  98.         if (!$r && $showerr{
  99.             if ($abook_init_error!=''$abook_init_error.="\n";
  100.             $abook_init_error.=_("Error initializing global address book.""\n" $abook->error;
  101.         }
  102.     }
  103.  
  104.     /* Load global addressbook from SQL if configured */
  105.     if (isset($addrbook_global_dsn&& !empty($addrbook_global_dsn)) {
  106.         /* Database configured */
  107.         if (!isset($addrbook_global_table|| empty($addrbook_global_table)) {
  108.             $addrbook_global_table 'global_abook';
  109.         }
  110.         $r $abook->add_backend('database',
  111.                                  Array('dsn' => $addrbook_global_dsn,
  112.                                        'owner' => 'global',
  113.                                        'name' => _("Global address book"),
  114.                                        'writeable' => $addrbook_global_writeable,
  115.                                        'listing' => $addrbook_global_listing,
  116.                                        'table' => $addrbook_global_table));
  117.         if (!$r && $showerr{
  118.             if ($abook_init_error!=''$abook_init_error.="\n";
  119.             $abook_init_error.=_("Error initializing global address book.""\n" $abook->error;
  120.     }
  121.     }
  122.  
  123.     /*
  124.      * hook allows to include different address book backends.
  125.      * plugins should extract $abook and $r from arguments
  126.      * and use same add_backend commands as above functions.
  127.      * @since 1.5.1 and 1.4.5
  128.      */
  129.     $hookReturn do_hook('abook_init'$abook$r);
  130.     $abook $hookReturn[1];
  131.     $r $hookReturn[2];
  132.  
  133.     if ($onlylocal{
  134.     /* Load configured LDAP servers (if PHP has LDAP support) */
  135.     if (isset($ldap_server&& is_array($ldap_server&& function_exists('ldap_connect')) {
  136.         reset($ldap_server);
  137.         while (list($undef,$parameach($ldap_server)) {
  138.             if (is_array($param)) {
  139.                 $r $abook->add_backend('ldap_server'$param);
  140.                 if (!$r && $showerr{
  141.                         if ($abook_init_error!=''$abook_init_error.="\n";
  142.                         $abook_init_error.=sprintf(_("Error initializing LDAP server %s:".
  143.                             "\n"$param['host']);
  144.                         $abook_init_error.= $abook->error;
  145.                     }
  146.                 }
  147.             }
  148.         }
  149.     // end of remote abook backends init
  150.  
  151.     /**
  152.      * display address book init errors.
  153.      */
  154.     if ($abook_init_error!='' && $showerr{
  155.         $abook_init_error sm_encode_html_special_chars($abook_init_error);
  156.         error_box($abook_init_error,$color);
  157.     }
  158.  
  159.     /* Return the initialized object */
  160.     return $abook;
  161. }
  162.  
  163.  
  164. /*
  165.  *   Had to move this function outside of the Addressbook Class
  166.  *   PHP 4.0.4 Seemed to be having problems with inline functions.
  167.  */
  168. function addressbook_cmp($a,$b{
  169.  
  170.     if($a['backend'$b['backend']{
  171.         return 1;
  172.     else if($a['backend'$b['backend']{
  173.         return -1;
  174.     }
  175.  
  176.     return (strtolower($a['name']strtolower($b['name'])) : -1;
  177.  
  178. }
  179.  
  180. /**
  181.  * Sort array by the key "name"
  182.  */
  183. function alistcmp($a,$b{
  184.     $abook_sort_order=get_abook_sort();
  185.  
  186.     switch ($abook_sort_order{
  187.     case 0:
  188.     case 1:
  189.       $abook_sort='nickname';
  190.       break;
  191.     case 4:
  192.     case 5:
  193.       $abook_sort='email';
  194.       break;
  195.     case 6:
  196.     case 7:
  197.       $abook_sort='label';
  198.       break;
  199.     case 2:
  200.     case 3:
  201.     case 8:
  202.     default:
  203.       $abook_sort='name';
  204.     }
  205.  
  206.     if ($a['backend'$b['backend']{
  207.         return 1;
  208.     else {
  209.         if ($a['backend'$b['backend']{
  210.             return -1;
  211.         }
  212.     }
  213.  
  214.     if( (($abook_sort_order+22== 1{
  215.       return (strtolower($a[$abook_sort]strtolower($b[$abook_sort])) : -1;
  216.     else {
  217.       return (strtolower($a[$abook_sort]strtolower($b[$abook_sort])) : -1;
  218.     }
  219. }
  220.  
  221. /**
  222.  * Address book sorting options
  223.  *
  224.  * returns address book sorting order
  225.  * @return integer book sorting options order
  226.  */
  227. function get_abook_sort({
  228.     global $data_dir$username;
  229.  
  230.     /* get sorting order */
  231.     if(sqgetGlobalVar('abook_sort_order'$tempSQ_GET)) {
  232.       $abook_sort_order = (int) $temp;
  233.  
  234.       if ($abook_sort_order or $abook_sort_order 8)
  235.         $abook_sort_order=8;
  236.  
  237.       setPref($data_dir$username'abook_sort_order'$abook_sort_order);
  238.     else {
  239.       /* get previous sorting options. default to unsorted */
  240.       $abook_sort_order getPref($data_dir$username'abook_sort_order'8);
  241.     }
  242.  
  243.     return $abook_sort_order;
  244. }
  245.  
  246. /**
  247.  * This function shows the address book sort button.
  248.  *
  249.  * @param integer $abook_sort_order current sort value
  250.  * @param string $alt_tag alt tag value (string visible to text only browsers)
  251.  * @param integer $Down sort value when list is sorted ascending
  252.  * @param integer $Up sort value when list is sorted descending
  253.  * @return string html code with sorting images and urls
  254.  * @since 1.5.1 and 1.4.6
  255.  */
  256. function show_abook_sort_button($abook_sort_order$alt_tag$Down$Up {
  257.     global $form_url;
  258.  
  259.      /* Figure out which image we want to use. */
  260.     if ($abook_sort_order != $Up && $abook_sort_order != $Down{
  261.         $img 'sort_none.png';
  262.         $which $Up;
  263.     elseif ($abook_sort_order == $Up{
  264.         $img 'up_pointer.png';
  265.         $which $Down;
  266.     else {
  267.         $img 'down_pointer.png';
  268.         $which 8;
  269.     }
  270.  
  271.       /* Now that we have everything figured out, show the actual button. */
  272.     return ' <a href="' $form_url .'?abook_sort_order=' $which
  273.          . '"><img src="../images/' $img
  274.          . '" border="0" width="12" height="10" alt="' $alt_tag '" title="'
  275.          . _("Click here to change the sorting of the address list".'" /></a>';
  276. }
  277.  
  278.  
  279. /**
  280.  * This is the main address book class that connect all the
  281.  * backends and provide services to the functions above.
  282.  * @package squirrelmail
  283.  */
  284.  
  285. class AddressBook {
  286.  
  287.     var $backends    = array();
  288.     var $numbackends = 0;
  289.     var $error       = '';
  290.     var $localbackend = 0;
  291.     var $localbackendname = '';
  292.     var $add_extra_field = false;
  293.  
  294.     /**
  295.      * Constructor (PHP5 style, required in some future version of PHP)
  296.      */
  297.     function __construct({
  298.         $this->localbackendname = _("Personal address book");
  299.     }
  300.  
  301.     /**
  302.      * Constructor (PHP4 style, kept for compatibility reasons)
  303.      */
  304.     function AddressBook({
  305.         self::__construct();
  306.     }
  307.  
  308.     /*
  309.      * Return an array of backends of a given type,
  310.      * or all backends if no type is specified.
  311.      */
  312.     function get_backend_list($type ''{
  313.         $ret array();
  314.         for ($i $i <= $this->numbackends $i++{
  315.             if (empty($type|| $type == $this->backends[$i]->btype{
  316.                 $ret[&$this->backends[$i];
  317.             }
  318.         }
  319.         return $ret;
  320.     }
  321.  
  322.  
  323.     /*
  324.        ========================== Public ========================
  325.  
  326.         Add a new backend. $backend is the name of a backend
  327.         (without the abook_ prefix), and $param is an optional
  328.         mixed variable that is passed to the backend constructor.
  329.         See each of the backend classes for valid parameters.
  330.      */
  331.     function add_backend($backend$param ''{
  332.         $backend_name 'abook_' $backend;
  333.         eval('$newback = new ' $backend_name '($param);');
  334.         if(!empty($newback->error)) {
  335.             $this->error = $newback->error;
  336.             return false;
  337.         }
  338.  
  339.         $this->numbackends++;
  340.  
  341.         $newback->bnum $this->numbackends;
  342.         $this->backends[$this->numbackends$newback;
  343.  
  344.         /* Store ID of first local backend added */
  345.         if ($this->localbackend == && $newback->btype == 'local'{
  346.             $this->localbackend = $this->numbackends;
  347.             $this->localbackendname = $newback->sname;
  348.         }
  349.  
  350.         return $this->numbackends;
  351.     }
  352.  
  353.  
  354.     /*
  355.      * This function takes a $row array as returned by the addressbook
  356.      * search and returns an e-mail address with the full name or
  357.      * nickname optionally prepended.
  358.      */
  359.  
  360.     static function full_address($row{
  361.         global $data_dir$username;
  362.         $addrsrch_fullname getPref($data_dir$username'addrsrch_fullname''fullname');
  363.  
  364.         // allow multiple addresses in one row (poor person's grouping - bah)
  365.         // (separate with commas)
  366.         //
  367.         $return '';
  368.         $addresses explode(','$row['email']);
  369.         foreach ($addresses as $address{
  370.  
  371.             if (!empty($return)) $return .= ', ';
  372.  
  373.             if ($addrsrch_fullname == 'fullname')
  374.                 $return .= '"' $row['name''" <' trim($address'>';
  375.             else if ($addrsrch_fullname == 'nickname')
  376.                 $return .= '"' $row['nickname''" <' trim($address'>';
  377.             else // "noprefix"
  378.                 $return .= trim($address);
  379.  
  380.         }
  381.  
  382.         return $return;
  383.     }
  384.  
  385.     /*
  386.         Return a list of addresses matching expression in
  387.         all backends of a given type.
  388.     */
  389.     function search($expression$bnum = -1{
  390.         $ret array();
  391.         $this->error = '';
  392.  
  393.         /* Search all backends */
  394.         if ($bnum == -1{
  395.             $sel $this->get_backend_list('');
  396.             $failed 0;
  397.             for ($i $i sizeof($sel$i++{
  398.                 $backend &$sel[$i];
  399.                 $backend->error '';
  400.                 $res $backend->search($expression);
  401.                 if (is_array($res)) {
  402.                     $ret array_merge($ret$res);
  403.                 else {
  404.                     $this->error .= "\n" $backend->error;
  405.                     $failed++;
  406.                 }
  407.             }
  408.  
  409.             /* Only fail if all backends failed */
  410.             if$failed >= sizeof$sel ) ) {
  411.                 $ret FALSE;
  412.             }
  413.  
  414.         }  else {
  415.  
  416.             /* Search only one backend */
  417.  
  418.             $ret $this->backends[$bnum]->search($expression);
  419.             if (!is_array($ret)) {
  420.                 $this->error .= "\n" $this->backends[$bnum]->error;
  421.                 $ret FALSE;
  422.             }
  423.         }
  424.  
  425.         return$ret );
  426.     }
  427.  
  428.  
  429.     /* Return a sorted search */
  430.     function s_search($expression$bnum = -1{
  431.  
  432.         $ret $this->search($expression$bnum);
  433.         if is_array$ret ) ) {
  434.             usort($ret'addressbook_cmp');
  435.         }
  436.         return $ret;
  437.     }
  438.  
  439.  
  440.     /*
  441.      *  Lookup an address by the indicated field. Only
  442.      *  possible in local backends.
  443.      */
  444.     function lookup($value$bnum = -1$field SM_ABOOK_FIELD_NICKNAME{
  445.  
  446.         $ret array();
  447.  
  448.         if ($bnum > -1{
  449.             $res $this->backends[$bnum]->lookup($value$field);
  450.             if (is_array($res)) {
  451.                return $res;
  452.             else {
  453.                $this->error = $this->backends[$bnum]->error;
  454.                return false;
  455.             }
  456.         }
  457.  
  458.         $sel $this->get_backend_list('local');
  459.         for ($i $i sizeof($sel$i++{
  460.             $backend &$sel[$i];
  461.             $backend->error '';
  462.             $res $backend->lookup($value$field);
  463.  
  464.             // return an address if one is found
  465.             // (empty array means lookup concluded
  466.             // but no result found - in this case,
  467.             // proceed to next backend)
  468.             //
  469.             if (is_array($res)) {
  470.                 if (!empty($res)) return $res;
  471.             else {
  472.                 $this->error = $backend->error;
  473.                 return false;
  474.             }
  475.         }
  476.  
  477.         return $ret;
  478.     }
  479.  
  480.  
  481.     /* Return all addresses */
  482.     function list_addr($bnum = -1{
  483.         $ret array();
  484.  
  485.         if ($bnum == -1{
  486.             $sel $this->get_backend_list('');
  487.         else {
  488.             $sel array(=> &$this->backends[$bnum]);
  489.         }
  490.  
  491.         for ($i $i sizeof($sel$i++{
  492.             $backend &$sel[$i];
  493.             $backend->error '';
  494.             $res $backend->list_addr();
  495.             if (is_array($res)) {
  496.                $ret array_merge($ret$res);
  497.             else {
  498.                $this->error = $backend->error;
  499.                return false;
  500.             }
  501.         }
  502.  
  503.         return $ret;
  504.     }
  505.  
  506.     /*
  507.      * Create a new address from $userdata, in backend $bnum.
  508.      * Return the backend number that the/ address was added
  509.      * to, or false if it failed.
  510.      */
  511.     function add($userdata$bnum{
  512.  
  513.         /* Validate data */
  514.         if (!is_array($userdata)) {
  515.             $this->error = _("Invalid input data");
  516.             return false;
  517.         }
  518.         if (empty($userdata['firstname']&& empty($userdata['lastname'])) {
  519.             $this->error = _("Name is missing");
  520.             return false;
  521.         }
  522.         if (empty($userdata['email'])) {
  523.             $this->error = _("E-mail address is missing");
  524.             return false;
  525.         }
  526.         if (empty($userdata['nickname'])) {
  527.             $userdata['nickname'$userdata['email'];
  528.         }
  529.  
  530.         /* Check that specified backend accept new entries */
  531.         if (!$this->backends[$bnum]->writeable{
  532.             $this->error = _("Address book is read-only");
  533.             return false;
  534.         }
  535.  
  536.         /* Add address to backend */
  537.         $res $this->backends[$bnum]->add($userdata);
  538.         if ($res{
  539.             return $bnum;
  540.         else {
  541.             $this->error = $this->backends[$bnum]->error;
  542.             return false;
  543.         }
  544.  
  545.         return false;  // Not reached
  546.     /* end of add() */
  547.  
  548.  
  549.     /*
  550.      * Remove the user identified by $alias from backend $bnum
  551.      * If $alias is an array, all users in the array are removed.
  552.      */
  553.     function remove($alias$bnum{
  554.  
  555.         /* Check input */
  556.         if (empty($alias)) {
  557.             return true;
  558.         }
  559.  
  560.         /* Convert string to single element array */
  561.         if (!is_array($alias)) {
  562.             $alias array(=> $alias);
  563.         }
  564.  
  565.         /* Check that specified backend is writable */
  566.         if (!$this->backends[$bnum]->writeable{
  567.             $this->error = _("Address book is read-only");
  568.             return false;
  569.         }
  570.  
  571.         /* Remove user from backend */
  572.         $res $this->backends[$bnum]->remove($alias);
  573.         if ($res{
  574.             return $bnum;
  575.         else {
  576.             $this->error = $this->backends[$bnum]->error;
  577.             return false;
  578.         }
  579.  
  580.         return FALSE;  /* Not reached */
  581.     /* end of remove() */
  582.  
  583.  
  584.     /*
  585.      * Remove the user identified by $alias from backend $bnum
  586.      * If $alias is an array, all users in the array are removed.
  587.      */
  588.     function modify($alias$userdata$bnum{
  589.  
  590.         /* Check input */
  591.         if (empty($alias|| !is_string($alias)) {
  592.             return true;
  593.         }
  594.  
  595.         /* Validate data */
  596.         if(!is_array($userdata)) {
  597.             $this->error = _("Invalid input data");
  598.             return false;
  599.         }
  600.         if (empty($userdata['firstname']&& empty($userdata['lastname'])) {
  601.             $this->error = _("Name is missing");
  602.             return false;
  603.         }
  604.         if (empty($userdata['email'])) {
  605.             $this->error = _("E-mail address is missing");
  606.             return false;
  607.         }
  608.  
  609.         if (empty($userdata['nickname'])) {
  610.             $userdata['nickname'$userdata['email'];
  611.         }
  612.  
  613.         /* Check that specified backend is writable */
  614.         if (!$this->backends[$bnum]->writeable{
  615.             $this->error = _("Address book is read-only");;
  616.             return false;
  617.         }
  618.  
  619.         /* Modify user in backend */
  620.         $res $this->backends[$bnum]->modify($alias$userdata);
  621.         if ($res{
  622.             return $bnum;
  623.         else {
  624.             $this->error = $this->backends[$bnum]->error;
  625.             return false;
  626.         }
  627.  
  628.         return FALSE;  /* Not reached */
  629.     /* end of modify() */
  630.  
  631.  
  632. /* End of class Addressbook */
  633.  
  634. /**
  635.  * Generic backend that all other backends extend
  636.  * @package squirrelmail
  637.  */
  638.  
  639.     /* Variables that all backends must provide. */
  640.     var $btype      = 'dummy';
  641.     var $bname      = 'dummy';
  642.     var $sname      = 'Dummy backend';
  643.  
  644.     /*
  645.      * Variables common for all backends, but that
  646.      * should not be changed by the backends.
  647.      */
  648.     var $bnum       = -1;
  649.     var $error      = '';
  650.     var $writeable  = false;
  651.  
  652.     function set_error($string{
  653.         $this->error = '[' $this->sname . '] ' $string;
  654.         return false;
  655.     }
  656.  
  657.  
  658.     /* ========================== Public ======================== */
  659.  
  660.     function search($expression{
  661.         $this->set_error('search not implemented');
  662.         return false;
  663.     }
  664.  
  665.     function lookup($value$field=SM_ABOOK_FIELD_NICKNAME{
  666.         $this->set_error('lookup not implemented');
  667.         return false;
  668.     }
  669.  
  670.     function list_addr({
  671.         $this->set_error('list_addr not implemented');
  672.         return false;
  673.     }
  674.  
  675.     function add($userdata{
  676.         $this->set_error('add not implemented');
  677.         return false;
  678.     }
  679.  
  680.     function remove($alias{
  681.         $this->set_error('delete not implemented');
  682.         return false;
  683.     }
  684.  
  685.     function modify($alias$newuserdata{
  686.         $this->set_error('modify not implemented');
  687.         return false;
  688.     }
  689.  
  690. }
  691.  
  692. /*
  693.   PHP 5 requires that the class be made first, which seems rather
  694.   logical, and should have been the way it was generated the first time.
  695. */
  696.  
  697. require_once(SM_PATH 'functions/abook_local_file.php');
  698. require_once(SM_PATH 'functions/abook_ldap_server.php');
  699.  
  700. /* Only load database backend if database is configured */
  701. if((isset($addrbook_dsn&& !empty($addrbook_dsn)) ||
  702.  (isset($addrbook_global_dsn&& !empty($addrbook_global_dsn))) {
  703.   include_once(SM_PATH 'functions/abook_database.php');
  704. }
  705.  
  706. /*
  707.  * hook allows adding different address book classes.
  708.  * class must follow address book class coding standards.
  709.  *
  710.  * see addressbook_backend class and functions/abook_*.php files.
  711.  * @since 1.5.1 and 1.4.5
  712.  */
  713. do_hook('abook_add_class');

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