Fix simple E_STRICT notice
[squirrelmail.git] / functions / addressbook.php
index 8c7648770ad6ec0b523eae86f97e3697702b09cd..0559c8fa7f3bea49e34f74dd9e97868031e51c1f 100644 (file)
@@ -1,21 +1,16 @@
 <?php
-
 /**
  * functions/addressbook.php - Functions and classes for the addressbook system
  *
  * Functions require SM_PATH and support of forms.php functions
  *
- * @copyright &copy; 1999-2006 The SquirrelMail Project Team
+ * @copyright 1999-2012 The SquirrelMail Project Team
  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  * @version $Id$
  * @package squirrelmail
  * @subpackage addressbook
  */
 
-/** required includes */
-// FIXME, NO display code in functions files
-include_once(SM_PATH . 'templates/util_global.php');
-
 /**
  * Create and initialize an addressbook object.
  * @param boolean $showerr display any address book init errors. html page header
@@ -59,6 +54,7 @@ function addressbook_init($showerr = true, $onlylocal = false) {
         /* File */
         $filename = getHashedFile($username, $data_dir, "$username.abook");
         $r = $abook->add_backend('local_file', Array('filename' => $filename,
+                                                     'umask' => 0077,
                                                      'line_length' => $abook_file_line_length,
                                                      'create'   => true));
         if(!$r && $showerr) {
@@ -90,7 +86,7 @@ function addressbook_init($showerr = true, $onlylocal = false) {
         }
 
         $r = $abook->add_backend('local_file',array('filename'=>$abook_global_filename,
-                                                    'name' => _("Global address book"),
+                                                    'name' => _("Global Address Book"),
                                                     'detect_writeable' => false,
                                                     'line_length' => $abook_file_line_length,
                                                     'writeable'=> $abook_global_file_writeable,
@@ -112,7 +108,7 @@ function addressbook_init($showerr = true, $onlylocal = false) {
       $r = $abook->add_backend('database',
                                Array('dsn' => $addrbook_global_dsn,
                                      'owner' => 'global',
-                                     'name' => _("Global address book"),
+                                     'name' => _("Global Address Book"),
                                      'writeable' => $addrbook_global_writeable,
                                      'listing' => $addrbook_global_listing,
                                      'table' => $addrbook_global_table));
@@ -130,10 +126,13 @@ function addressbook_init($showerr = true, $onlylocal = false) {
      * Since 1.5.2 hook sends third ($onlylocal) argument to address book
      * plugins in order to allow detection of local address book init.
      * @since 1.5.1 and 1.4.5
+     * Since 1.5.2, the plugin arguments are passed inside an array
+     * and by reference, so plugins hooking in here need to accept arguments
+     * in an array and change those values as needed instead of returning
+     * the changed values.
      */
-    $hookReturn = do_hook('abook_init', $abook, $r, $onlylocal);
-    $abook = $hookReturn[1];
-    $r = $hookReturn[2];
+    $temp = array(&$abook, &$r, &$onlylocal);
+    do_hook('abook_init', $temp);
     if (!$r && $showerr) {
         if ($abook_init_error!='') $abook_init_error.="\n";
         $abook_init_error.=_("Error initializing other address books.") . "\n" . $abook->error;
@@ -171,27 +170,58 @@ function addressbook_init($showerr = true, $onlylocal = false) {
 }
 
 /**
- * Display the "new address" form
+ * Constructs the "new address" form
+ *
+ * NOTE!  The form is not closed - the caller
+ *        must add the closing form tag itself.
  *
- * Form is not closed and you must add closing form tag.
  * @since 1.5.1
- * @param string $form_url form action url
- * @param string $name form name
- * @param string $title form title
- * @param string $button form button name
- * @param array $defdata values of form fields
+ *
+ * @param string $form_url Form action url
+ * @param string $name     Form name
+ * @param string $title    Form title
+ * @param string $button   Form button name
+ * @param int    $backend  The current backend being displayed
+ * @param array  $defdata  Values of form fields
+ *
+ * @return string The desired address form display code
+ *
  */
-function abook_create_form($form_url,$name,$title,$button,$defdata=array()) {
-    global $color;
-    echo addForm($form_url, 'post', 'f_add').
-        html_tag( 'table',
-                  html_tag( 'tr',
-                            html_tag( 'td', "\n". '<strong>' . $title . '</strong>' . "\n",
-                                      'center', $color[0]
-                                      )
-                            )
-                  , 'center', '', 'width="90%"' ) ."\n";
-    address_form($name, $button, $defdata);
+function abook_create_form($form_url, $name, $title, $button,
+                           $backend, $defdata=array()) {
+
+    global $oTemplate;
+
+    $output = addForm($form_url, 'post', 'f_add', '', '', array(), TRUE);
+
+    if ($button == _("Update address")) {
+        $edit = true;
+        $backends = NULL;
+    } else {
+        $edit = false;
+        $backends = getWritableBackends();
+    }
+    
+    $fields = array (
+                        'nickname'  => 'NickName',
+                        'firstname' => 'FirstName',
+                        'lastname'  => 'LastName',
+                        'email'     => 'Email',
+                        'label'     => 'Info',
+                    );
+    $values = array();
+    foreach ($fields as $sqm=>$template) {
+        $values[$template] = isset($defdata[$sqm]) ? $defdata[$sqm] : '';
+    }
+    
+    $oTemplate->assign('writable_backends', $backends);
+    $oTemplate->assign('values', $values);
+    $oTemplate->assign('edit', $edit);
+    $oTemplate->assign('current_backend', $backend);
+    
+    $output .= $oTemplate->fetch('addrbook_addedit.tpl');
+
+    return $output;
 }
 
 
@@ -213,106 +243,21 @@ function addressbook_cmp($a,$b) {
 }
 
 /**
- * Make an input field
- * @param string $label
- * @param string $field
- * @param string $name
- * @param string $size
- * @param array $values
- * @param string $add
+ * Retrieve a list of writable backends
+ * @since 1.5.2
  */
-function addressbook_inp_field($label, $field, $name, $size, $values, $add='') {
-    global $color;
-    $value = ( isset($values[$field]) ? $values[$field] : '');
-
-    if (is_array($value)) {
-        $td_str = addSelect($name.'['.$field.']', $value);
-    } else {
-        $td_str = addInput($name.'['.$field.']', $value, $size);
-    }
-    $td_str .= $add ;
-
-    return html_tag( 'tr' ,
-            html_tag( 'td', '<label for="'.$name.'_'.$field.'_'.'">' .
-                $label . '</label>:', 'right', $color[4]) .
-            html_tag( 'td', $td_str, 'left', $color[4])
-            )
-        . "\n";
-}
-
-/**
- * Output form to add and modify address data
- */
-function address_form($name, $submittext, $values = array()) {
-    global $color, $squirrelmail_language;
-
-    if ($squirrelmail_language == 'ja_JP') {
-        echo html_tag( 'table',
-                addressbook_inp_field(_("Nickname"),     'nickname', $name, 15, $values,
-                    ' <small>' . _("Must be unique") . '</small>') .
-                addressbook_inp_field(_("E-mail address"),  'email', $name, 45, $values, '') .
-                addressbook_inp_field(_("Last name"),    'lastname', $name, 45, $values, '') .
-                addressbook_inp_field(_("First name"),  'firstname', $name, 45, $values, '') .
-                addressbook_inp_field(_("Additional info"), 'label', $name, 45, $values, '') .
-                list_writable_backends($name) .
-                html_tag( 'tr',
-                    html_tag( 'td',
-                        addSubmit($submittext, $name.'[SUBMIT]'),
-                        'center', $color[4], 'colspan="2"')
-                    )
-                , 'center', '', 'border="0" cellpadding="1" width="90%"') ."\n";
-    } else {
-        echo html_tag( 'table',
-                addressbook_inp_field(_("Nickname"),     'nickname', $name, 15, $values,
-                    ' <small>' . _("Must be unique") . '</small>') .
-                addressbook_inp_field(_("E-mail address"),  'email', $name, 45, $values, '') .
-                addressbook_inp_field(_("First name"),  'firstname', $name, 45, $values, '') .
-                addressbook_inp_field(_("Last name"),    'lastname', $name, 45, $values, '') .
-                addressbook_inp_field(_("Additional info"), 'label', $name, 45, $values, '') .
-                list_writable_backends($name) .
-                html_tag( 'tr',
-                    html_tag( 'td',
-                        addSubmit($submittext, $name.'[SUBMIT]') ,
-                        'center', $color[4], 'colspan="2"')
-                    )
-                , 'center', '', 'border="0" cellpadding="1" width="90%"') ."\n";
-    }
-}
-
-/**
- * Provides list of writeable backends.
- * Works only when address is added ($name='addaddr')
- * @param string $name name of form
- * @return string html formated backend field (select or hidden)
- */
-function list_writable_backends($name) {
-    global $color, $abook;
-    if ( $name != 'addaddr' ) { return; }
-    $writeable_abook = 1;
-    if ( $abook->numbackends > 1 ) {
-        $backends = $abook->get_backend_list();
-        $writeable_abooks=array();
-        while (list($undef,$v) = each($backends)) {
-            if ($v->writeable) {
-                // add each backend to array
-                $writeable_abooks[$v->bnum]=$v->sname;
-                // save backend number
-                $writeable_abook=$v->bnum;
-            }
-        }
-        if (count($writeable_abooks)>1) {
-            // we have more than one writeable backend
-            $ret=addSelect('backend',$writeable_abooks,null,true);
-            return html_tag( 'tr',
-                             html_tag( 'td', _("Add to:"),'right', $color[4] ) .
-                             html_tag( 'td', $ret, 'left', $color[4] )) . "\n";
+function getWritableBackends () {
+    global $abook;
+    
+    $write = array();
+    $backends = $abook->get_backend_list();
+    while (list($undef,$v) = each($backends)) {
+        if ($v->writeable) {
+            $write[$v->bnum]=$v->sname;
         }
     }
-    // Only one backend exists or is writeable.
-    return html_tag( 'tr',
-                     html_tag( 'td',
-                               addHidden('backend', $writeable_abook),
-                               'center', $color[4], 'colspan="2"')) . "\n";
+
+    return $write;
 }
 
 /**
@@ -384,13 +329,24 @@ function get_abook_sort() {
 /**
  * This function shows the address book sort button.
  *
- * @param integer $abook_sort_order current sort value
- * @param string $alt_tag alt tag value (string visible to text only browsers)
- * @param integer $Down sort value when list is sorted ascending
- * @param integer $Up sort value when list is sorted descending
+ * @param integer $abook_sort_order Current sort value
+ * @param string  $alt_tag          The alt tag value (string
+ *                                  visible to text only browsers)
+ * @param integer $Down             Sort value when list is sorted
+ *                                  ascending
+ * @param integer $Up               Sort value when list is sorted
+ *                                  descending
+ * @param array   $uri_extra        Any additional parameters to add
+ *                                  to the button's link, as an
+ *                                  associative array of key/value pairs
+ *                                  (OPTIONAL; default none)
+ *
  * @return string html code with sorting images and urls
+ *
  */
-function show_abook_sort_button($abook_sort_order, $alt_tag, $Down, $Up ) {
+function show_abook_sort_button($abook_sort_order, $alt_tag,
+                                $Down, $Up, $uri_extra=array() ) {
+
     global $form_url, $icon_theme_path;
 
      /* Figure out which image we want to use. */
@@ -408,11 +364,16 @@ function show_abook_sort_button($abook_sort_order, $alt_tag, $Down, $Up ) {
         $which = 8;
     }
 
+    $uri_extra['abook_sort_order'] = $which;
+    $uri = set_uri_vars($form_url, $uri_extra, FALSE);
+
     /* Now that we have everything figured out, show the actual button. */
-    return '&nbsp;<a href="' . $form_url .'?abook_sort_order=' . $which .
-           '" style="text-decoration:none" title="'.$alt_tag.'">' .
-           getIcon($icon_theme_path, $img, $text_icon, $alt_tag) .
-           '</a>';
+    return create_hyperlink($uri,
+                            getIcon($icon_theme_path, $img, $text_icon, $alt_tag),
+                            '', '', '', '', '',
+                            array('style' => 'text-decoration:none',
+                                  'title' => $alt_tag),
+                            FALSE);
 }
 
 
@@ -467,7 +428,7 @@ class AddressBook {
      * Constructor function.
      */
     function AddressBook() {
-        $this->localbackendname = _("Personal address book");
+        $this->localbackendname = _("Personal Address Book");
     }
 
     /**
@@ -512,7 +473,8 @@ class AddressBook {
               * NB: Because the backend files are included from within this function they DO NOT have access to
               * vars in the global scope. This function is the global scope for the included backend !!!
               */
-            $aBackend = do_hook('abook_add_class');
+            global $null;
+            $aBackend = do_hook('abook_add_class', $null);
             if (isset($aBackend) && is_array($aBackend) && isset($aBackend[$backend])) {
                 require_once($aBackend[$backend]);
             } else {
@@ -553,15 +515,27 @@ class AddressBook {
      * @return string email address with real name prepended
      */
     function full_address($row) {
-        global $addrsrch_fullname, $data_dir, $username;
-        $prefix = getPref($data_dir, $username, 'addrsrch_fullname');
-        if (($prefix != "" || (isset($addrsrch_fullname) &&
-            $prefix == $addrsrch_fullname)) && $prefix != 'noprefix') {
-            $name = ($prefix == 'nickname' ? $row['nickname'] : $row['name']);
-            return $name . ' <' . trim($row['email']) . '>';
-        } else {
-            return trim($row['email']);
+        global $data_dir, $username, $addrsrch_fullname;
+
+        // allow multiple addresses in one row (poor person's grouping - bah)
+        // (separate with commas)
+        //
+        $return = '';
+        $addresses = explode(',', $row['email']);
+        foreach ($addresses as $address) {
+            
+            if (!empty($return)) $return .= ', ';
+
+            if ($addrsrch_fullname == 'fullname')
+                $return .= '"' . $row['name'] . '" <' . trim($address) . '>';
+            else if ($addrsrch_fullname == 'nickname')
+                $return .= '"' . $row['nickname'] . '" <' . trim($address) . '>';
+            else // "noprefix"
+                $return .= trim($address);
+
         }
+
+        return $return;
     }
 
     /**
@@ -598,7 +572,11 @@ class AddressBook {
                 $ret = FALSE;
             }
 
-        }  else {
+        } elseif (! isset($this->backends[$bnum])) {
+            /* make sure that backend exists */
+            $this->error = _("Unknown address book backend");
+            $ret = false;
+        } else {
 
             /* Search only one backend */
 
@@ -630,18 +608,38 @@ class AddressBook {
 
 
     /**
-     * Lookup an address by alias.
+     * Lookup an address by the indicated field.
+     *
      * Only possible in local backends.
-     * @param string $alias
-     * @param integer backend number
-     * @return array lookup results. False, if not found.
+     *
+     * @param string  $value The value to look up
+     * @param integer $bnum  The number of the backend to
+     *                       look within (OPTIONAL; defaults 
+     *                       to look in all local backends)
+     * @param integer $field The field to look in, should be one
+     *                       of the SM_ABOOK_FIELD_* constants
+     *                       defined in include/constants.php
+     *                       (OPTIONAL; defaults to nickname field)
+     *                       NOTE: uniqueness is only guaranteed
+     *                       when the nickname field is used here;
+     *                       otherwise, the first matching address
+     *                       is returned.
+     *
+     * @return mixed Array with lookup results when the value
+     *               was found, an empty array if the value was
+     *               not found, or false if an error occured.
+     *
      */
-    function lookup($alias, $bnum = -1) {
+    function lookup($value, $bnum = -1, $field = SM_ABOOK_FIELD_NICKNAME) {
 
         $ret = array();
 
         if ($bnum > -1) {
-            $res = $this->backends[$bnum]->lookup($alias);
+            if (!isset($this->backends[$bnum])) {
+                $this->error = _("Unknown address book backend");
+                return false;
+            }
+            $res = $this->backends[$bnum]->lookup($value, $field);
             if (is_array($res)) {
                return $res;
             } else {
@@ -654,13 +652,18 @@ class AddressBook {
         for ($i = 0 ; $i < sizeof($sel) ; $i++) {
             $backend = &$sel[$i];
             $backend->error = '';
-            $res = $backend->lookup($alias);
+            $res = $backend->lookup($value, $field);
+
+            // return an address if one is found
+            // (empty array means lookup concluded
+            // but no result found - in this case,
+            // proceed to next backend)
+            //
             if (is_array($res)) {
-               if(!empty($res))
-              return $res;
+                if (!empty($res)) return $res;
             } else {
-               $this->error = $backend->error;
-               return false;
+                $this->error = $backend->error;
+                return false;
             }
         }
 
@@ -671,13 +674,17 @@ class AddressBook {
     /**
      * Return all addresses
      * @param integer $bnum backend number
-     * @return array search results
+     * @return mixed array with search results or boolean false on error.
      */
     function list_addr($bnum = -1) {
         $ret = array();
 
         if ($bnum == -1) {
             $sel = $this->get_backend_list('');
+        } elseif (! isset($this->backends[$bnum])) {
+            /* make sure that backend exists */
+            $this->error = _("Unknown address book backend");
+            $ret = false;
         } else {
             $sel = array(0 => &$this->backends[$bnum]);
         }
@@ -724,11 +731,17 @@ class AddressBook {
         }
 
         /* Blocks use of space, :, |, #, " and ! in nickname */
-        if (eregi('[ \\:\\|\\#\\"\\!]', $userdata['nickname'])) {
+        if (preg_match('/[ :|#"!]/', $userdata['nickname'])) {
             $this->error = _("Nickname contains illegal characters");
             return false;
         }
 
+        /* make sure that backend exists */
+        if (! isset($this->backends[$bnum])) {
+            $this->error = _("Unknown address book backend");
+            return false;
+        }
+
         /* Check that specified backend accept new entries */
         if (!$this->backends[$bnum]->writeable) {
             $this->error = _("Address book is read-only");
@@ -766,6 +779,12 @@ class AddressBook {
             $alias = array(0 => $alias);
         }
 
+        /* make sure that backend exists */
+        if (! isset($this->backends[$bnum])) {
+            $this->error = _("Unknown address book backend");
+            return false;
+        }
+
         /* Check that specified backend is writable */
         if (!$this->backends[$bnum]->writeable) {
             $this->error = _("Address book is read-only");
@@ -812,7 +831,7 @@ class AddressBook {
             return false;
         }
 
-        if (eregi('[\\: \\|\\#"\\!]', $userdata['nickname'])) {
+        if (preg_match('/[: |#"!]/', $userdata['nickname'])) {
             $this->error = _("Nickname contains illegal characters");
             return false;
         }
@@ -821,6 +840,12 @@ class AddressBook {
             $userdata['nickname'] = $userdata['email'];
         }
 
+        /* make sure that backend exists */
+        if (! isset($this->backends[$bnum])) {
+            $this->error = _("Unknown address book backend");
+            return false;
+        }
+
         /* Check that specified backend is writable */
         if (!$this->backends[$bnum]->writeable) {
             $this->error = _("Address book is read-only");;
@@ -916,11 +941,23 @@ class addressbook_backend {
     }
 
     /**
-     * Find entry in backend by alias
-     * @param string $alias name used for id
-     * @return bool
+     * Find entry in backend by the indicated field
+     *
+     * @param string  $value The value to look up
+     * @param integer $field The field to look in, should be one
+     *                       of the SM_ABOOK_FIELD_* constants
+     *                       defined in include/constants.php
+     *                       NOTE: uniqueness is only guaranteed
+     *                       when the nickname field is used here;
+     *                       otherwise, the first matching address
+     *                       is returned.
+     *
+     * @return mixed Array with lookup results when the value
+     *               was found, an empty array if the value was
+     *               not found, or false if an error occured.
+     *
      */
-    function lookup($alias) {
+    function lookup($value, $field=SM_ABOOK_FIELD_NICKNAME) {
         $this->set_error('lookup is not implemented');
         return false;
     }
@@ -971,18 +1008,21 @@ class addressbook_backend {
     /**
      * Creates full name from given name and surname
      *
-     * Handles name order differences
+     * Handles name order differences. Function always runs in SquirrelMail gettext domain.
+     * Plugins don't have to switch domains before calling this function.
      * @param string $firstname given name
      * @param string $lastname surname
      * @return string full name
      * @since 1.5.2
      */
     function fullname($firstname,$lastname) {
-        global $squirrelmail_language;
-        if ($squirrelmail_language=='ja_JP') {
-            return trim($lastname . ' ' . $firstname);
-        } else {
-            return trim($firstname . ' ' . $lastname);
-        }
+        // i18n: allows to control fullname layout in address book listing
+        // first %s is for first name, second %s is for last name.
+        // Translate it to '%2$s %1$s', if surname must be displayed first in your language.
+        // Please note that variables can be set to empty string and extra formating 
+        // (for example '%2$s, %1$s' as in 'Smith, John') might break. Use it only for 
+        // setting name and surname order. scripts will remove all prepended and appended
+        // whitespace.
+        return trim(sprintf(dgettext('squirrelmail',"%s %s"),$firstname,$lastname));
     }
 }