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

Documentation generated on Sat, 07 Oct 2006 16:29:47 +0300 by phpDocumentor 1.3.0RC6