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-2014 The SquirrelMail Project Team
  9.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10.  * @version $Id: addressbook.php 14466 2014-06-16 07:27:02Z 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.       // Constructor function.
  295.     function AddressBook({
  296.         $this->localbackendname = _("Personal address book");
  297.     }
  298.  
  299.     /*
  300.      * Return an array of backends of a given type,
  301.      * or all backends if no type is specified.
  302.      */
  303.     function get_backend_list($type ''{
  304.         $ret array();
  305.         for ($i $i <= $this->numbackends $i++{
  306.             if (empty($type|| $type == $this->backends[$i]->btype{
  307.                 $ret[&$this->backends[$i];
  308.             }
  309.         }
  310.         return $ret;
  311.     }
  312.  
  313.  
  314.     /*
  315.        ========================== Public ========================
  316.  
  317.         Add a new backend. $backend is the name of a backend
  318.         (without the abook_ prefix), and $param is an optional
  319.         mixed variable that is passed to the backend constructor.
  320.         See each of the backend classes for valid parameters.
  321.      */
  322.     function add_backend($backend$param ''{
  323.         $backend_name 'abook_' $backend;
  324.         eval('$newback = new ' $backend_name '($param);');
  325.         if(!empty($newback->error)) {
  326.             $this->error = $newback->error;
  327.             return false;
  328.         }
  329.  
  330.         $this->numbackends++;
  331.  
  332.         $newback->bnum $this->numbackends;
  333.         $this->backends[$this->numbackends$newback;
  334.  
  335.         /* Store ID of first local backend added */
  336.         if ($this->localbackend == && $newback->btype == 'local'{
  337.             $this->localbackend = $this->numbackends;
  338.             $this->localbackendname = $newback->sname;
  339.         }
  340.  
  341.         return $this->numbackends;
  342.     }
  343.  
  344.  
  345.     /*
  346.      * This function takes a $row array as returned by the addressbook
  347.      * search and returns an e-mail address with the full name or
  348.      * nickname optionally prepended.
  349.      */
  350.  
  351.     function full_address($row{
  352.         global $data_dir$username;
  353.         $addrsrch_fullname getPref($data_dir$username'addrsrch_fullname''fullname');
  354.  
  355.         // allow multiple addresses in one row (poor person's grouping - bah)
  356.         // (separate with commas)
  357.         //
  358.         $return '';
  359.         $addresses explode(','$row['email']);
  360.         foreach ($addresses as $address{
  361.  
  362.             if (!empty($return)) $return .= ', ';
  363.  
  364.             if ($addrsrch_fullname == 'fullname')
  365.                 $return .= '"' $row['name''" <' trim($address'>';
  366.             else if ($addrsrch_fullname == 'nickname')
  367.                 $return .= '"' $row['nickname''" <' trim($address'>';
  368.             else // "noprefix"
  369.                 $return .= trim($address);
  370.  
  371.         }
  372.  
  373.         return $return;
  374.     }
  375.  
  376.     /*
  377.         Return a list of addresses matching expression in
  378.         all backends of a given type.
  379.     */
  380.     function search($expression$bnum = -1{
  381.         $ret array();
  382.         $this->error = '';
  383.  
  384.         /* Search all backends */
  385.         if ($bnum == -1{
  386.             $sel $this->get_backend_list('');
  387.             $failed 0;
  388.             for ($i $i sizeof($sel$i++{
  389.                 $backend &$sel[$i];
  390.                 $backend->error '';
  391.                 $res $backend->search($expression);
  392.                 if (is_array($res)) {
  393.                     $ret array_merge($ret$res);
  394.                 else {
  395.                     $this->error .= "\n" $backend->error;
  396.                     $failed++;
  397.                 }
  398.             }
  399.  
  400.             /* Only fail if all backends failed */
  401.             if$failed >= sizeof$sel ) ) {
  402.                 $ret FALSE;
  403.             }
  404.  
  405.         }  else {
  406.  
  407.             /* Search only one backend */
  408.  
  409.             $ret $this->backends[$bnum]->search($expression);
  410.             if (!is_array($ret)) {
  411.                 $this->error .= "\n" $this->backends[$bnum]->error;
  412.                 $ret FALSE;
  413.             }
  414.         }
  415.  
  416.         return$ret );
  417.     }
  418.  
  419.  
  420.     /* Return a sorted search */
  421.     function s_search($expression$bnum = -1{
  422.  
  423.         $ret $this->search($expression$bnum);
  424.         if is_array$ret ) ) {
  425.             usort($ret'addressbook_cmp');
  426.         }
  427.         return $ret;
  428.     }
  429.  
  430.  
  431.     /*
  432.      *  Lookup an address by the indicated field. Only
  433.      *  possible in local backends.
  434.      */
  435.     function lookup($value$bnum = -1$field SM_ABOOK_FIELD_NICKNAME{
  436.  
  437.         $ret array();
  438.  
  439.         if ($bnum > -1{
  440.             $res $this->backends[$bnum]->lookup($value$field);
  441.             if (is_array($res)) {
  442.                return $res;
  443.             else {
  444.                $this->error = $this->backends[$bnum]->error;
  445.                return false;
  446.             }
  447.         }
  448.  
  449.         $sel $this->get_backend_list('local');
  450.         for ($i $i sizeof($sel$i++{
  451.             $backend &$sel[$i];
  452.             $backend->error '';
  453.             $res $backend->lookup($value$field);
  454.  
  455.             // return an address if one is found
  456.             // (empty array means lookup concluded
  457.             // but no result found - in this case,
  458.             // proceed to next backend)
  459.             //
  460.             if (is_array($res)) {
  461.                 if (!empty($res)) return $res;
  462.             else {
  463.                 $this->error = $backend->error;
  464.                 return false;
  465.             }
  466.         }
  467.  
  468.         return $ret;
  469.     }
  470.  
  471.  
  472.     /* Return all addresses */
  473.     function list_addr($bnum = -1{
  474.         $ret array();
  475.  
  476.         if ($bnum == -1{
  477.             $sel $this->get_backend_list('');
  478.         else {
  479.             $sel array(=> &$this->backends[$bnum]);
  480.         }
  481.  
  482.         for ($i $i sizeof($sel$i++{
  483.             $backend &$sel[$i];
  484.             $backend->error '';
  485.             $res $backend->list_addr();
  486.             if (is_array($res)) {
  487.                $ret array_merge($ret$res);
  488.             else {
  489.                $this->error = $backend->error;
  490.                return false;
  491.             }
  492.         }
  493.  
  494.         return $ret;
  495.     }
  496.  
  497.     /*
  498.      * Create a new address from $userdata, in backend $bnum.
  499.      * Return the backend number that the/ address was added
  500.      * to, or false if it failed.
  501.      */
  502.     function add($userdata$bnum{
  503.  
  504.         /* Validate data */
  505.         if (!is_array($userdata)) {
  506.             $this->error = _("Invalid input data");
  507.             return false;
  508.         }
  509.         if (empty($userdata['firstname']&& empty($userdata['lastname'])) {
  510.             $this->error = _("Name is missing");
  511.             return false;
  512.         }
  513.         if (empty($userdata['email'])) {
  514.             $this->error = _("E-mail address is missing");
  515.             return false;
  516.         }
  517.         if (empty($userdata['nickname'])) {
  518.             $userdata['nickname'$userdata['email'];
  519.         }
  520.  
  521.         /* Check that specified backend accept new entries */
  522.         if (!$this->backends[$bnum]->writeable{
  523.             $this->error = _("Address book is read-only");
  524.             return false;
  525.         }
  526.  
  527.         /* Add address to backend */
  528.         $res $this->backends[$bnum]->add($userdata);
  529.         if ($res{
  530.             return $bnum;
  531.         else {
  532.             $this->error = $this->backends[$bnum]->error;
  533.             return false;
  534.         }
  535.  
  536.         return false;  // Not reached
  537.     /* end of add() */
  538.  
  539.  
  540.     /*
  541.      * Remove the user identified by $alias from backend $bnum
  542.      * If $alias is an array, all users in the array are removed.
  543.      */
  544.     function remove($alias$bnum{
  545.  
  546.         /* Check input */
  547.         if (empty($alias)) {
  548.             return true;
  549.         }
  550.  
  551.         /* Convert string to single element array */
  552.         if (!is_array($alias)) {
  553.             $alias array(=> $alias);
  554.         }
  555.  
  556.         /* Check that specified backend is writable */
  557.         if (!$this->backends[$bnum]->writeable{
  558.             $this->error = _("Address book is read-only");
  559.             return false;
  560.         }
  561.  
  562.         /* Remove user from backend */
  563.         $res $this->backends[$bnum]->remove($alias);
  564.         if ($res{
  565.             return $bnum;
  566.         else {
  567.             $this->error = $this->backends[$bnum]->error;
  568.             return false;
  569.         }
  570.  
  571.         return FALSE;  /* Not reached */
  572.     /* end of remove() */
  573.  
  574.  
  575.     /*
  576.      * Remove the user identified by $alias from backend $bnum
  577.      * If $alias is an array, all users in the array are removed.
  578.      */
  579.     function modify($alias$userdata$bnum{
  580.  
  581.         /* Check input */
  582.         if (empty($alias|| !is_string($alias)) {
  583.             return true;
  584.         }
  585.  
  586.         /* Validate data */
  587.         if(!is_array($userdata)) {
  588.             $this->error = _("Invalid input data");
  589.             return false;
  590.         }
  591.         if (empty($userdata['firstname']&& empty($userdata['lastname'])) {
  592.             $this->error = _("Name is missing");
  593.             return false;
  594.         }
  595.         if (empty($userdata['email'])) {
  596.             $this->error = _("E-mail address is missing");
  597.             return false;
  598.         }
  599.  
  600.         if (empty($userdata['nickname'])) {
  601.             $userdata['nickname'$userdata['email'];
  602.         }
  603.  
  604.         /* Check that specified backend is writable */
  605.         if (!$this->backends[$bnum]->writeable{
  606.             $this->error = _("Address book is read-only");;
  607.             return false;
  608.         }
  609.  
  610.         /* Modify user in backend */
  611.         $res $this->backends[$bnum]->modify($alias$userdata);
  612.         if ($res{
  613.             return $bnum;
  614.         else {
  615.             $this->error = $this->backends[$bnum]->error;
  616.             return false;
  617.         }
  618.  
  619.         return FALSE;  /* Not reached */
  620.     /* end of modify() */
  621.  
  622.  
  623. /* End of class Addressbook */
  624.  
  625. /**
  626.  * Generic backend that all other backends extend
  627.  * @package squirrelmail
  628.  */
  629.  
  630.     /* Variables that all backends must provide. */
  631.     var $btype      = 'dummy';
  632.     var $bname      = 'dummy';
  633.     var $sname      = 'Dummy backend';
  634.  
  635.     /*
  636.      * Variables common for all backends, but that
  637.      * should not be changed by the backends.
  638.      */
  639.     var $bnum       = -1;
  640.     var $error      = '';
  641.     var $writeable  = false;
  642.  
  643.     function set_error($string{
  644.         $this->error = '[' $this->sname . '] ' $string;
  645.         return false;
  646.     }
  647.  
  648.  
  649.     /* ========================== Public ======================== */
  650.  
  651.     function search($expression{
  652.         $this->set_error('search not implemented');
  653.         return false;
  654.     }
  655.  
  656.     function lookup($value$field=SM_ABOOK_FIELD_NICKNAME{
  657.         $this->set_error('lookup not implemented');
  658.         return false;
  659.     }
  660.  
  661.     function list_addr({
  662.         $this->set_error('list_addr not implemented');
  663.         return false;
  664.     }
  665.  
  666.     function add($userdata{
  667.         $this->set_error('add not implemented');
  668.         return false;
  669.     }
  670.  
  671.     function remove($alias{
  672.         $this->set_error('delete not implemented');
  673.         return false;
  674.     }
  675.  
  676.     function modify($alias$newuserdata{
  677.         $this->set_error('modify not implemented');
  678.         return false;
  679.     }
  680.  
  681. }
  682.  
  683. /*
  684.   PHP 5 requires that the class be made first, which seems rather
  685.   logical, and should have been the way it was generated the first time.
  686. */
  687.  
  688. require_once(SM_PATH 'functions/abook_local_file.php');
  689. require_once(SM_PATH 'functions/abook_ldap_server.php');
  690.  
  691. /* Only load database backend if database is configured */
  692. if((isset($addrbook_dsn&& !empty($addrbook_dsn)) ||
  693.  (isset($addrbook_global_dsn&& !empty($addrbook_global_dsn))) {
  694.   include_once(SM_PATH 'functions/abook_database.php');
  695. }
  696.  
  697. /*
  698.  * hook allows adding different address book classes.
  699.  * class must follow address book class coding standards.
  700.  *
  701.  * see addressbook_backend class and functions/abook_*.php files.
  702.  * @since 1.5.1 and 1.4.5
  703.  */
  704. do_hook('abook_add_class');

Documentation generated on Sat, 01 Nov 2014 04:20:20 +0100 by phpDocumentor 1.4.3