瀏覽代碼

Address book file backend will break with error message, if required
address book fields are not available. Prevents address book corruption
and address book format violations that can cause PHP notices.

Added line length setting in local_file address book backend (#1181561).

tokul 19 年之前
父節點
當前提交
94c3c393b0
共有 5 個文件被更改,包括 162 次插入39 次删除
  1. 4 0
      ChangeLog
  2. 38 0
      config/conf.pl
  3. 18 0
      config/config_default.php
  4. 98 37
      functions/abook_local_file.php
  5. 4 2
      functions/addressbook.php

+ 4 - 0
ChangeLog

@@ -84,6 +84,10 @@ Version 1.5.2 - CVS
     identities is disabled.
   - Configuration utility does not allow 8bit symbols in IMAP folder names 
     (#1485501).
+  - Address book file backend will break with error message, if required 
+    address book fields are not available. Prevents address book corruption
+    and address book format violations that can cause PHP notices.
+  - Added line length setting in local_file address book backend (#1181561).
 
 Version 1.5.1 (branched on 2006-02-12)
 --------------------------------------

+ 38 - 0
config/conf.pl

@@ -421,6 +421,9 @@ $use_smtp_tls = 1                      if ( $use_smtp_tls eq 'true');
 $disable_thread_sort = 'false'         if ( !$disable_thread_sort );
 $disable_server_sort = 'false'         if ( !$disable_server_sort );
 
+# since 1.5.2
+$abook_file_line_length = 2048         if ( !$abook_file_line_length );
+
 if ( $ARGV[0] eq '--install-plugin' ) {
     print "Activating plugin " . $ARGV[1] . "\n";
     push @plugins, $ARGV[1];
@@ -621,6 +624,7 @@ while ( ( $command ne "q" ) && ( $command ne "Q" ) && ( $command ne ":q" ) ) {
         print "3.  Global address book file                    : $WHT$abook_global_file$NRM\n";
         print "4.  Allow writing into global file address book : $WHT$abook_global_file_writeable$NRM\n";
         print "5.  Allow listing of global file address book   : $WHT$abook_global_file_listing$NRM\n";
+        print "6.  Allowed address book line length            : $WHT$abook_file_line_length$NRM\n";
         print "\n";
         print "R   Return to Main Menu\n";
     } elsif ( $menu == 7 ) {
@@ -839,6 +843,7 @@ while ( ( $command ne "q" ) && ( $command ne "Q" ) && ( $command ne ":q" ) ) {
             elsif ( $command == 3 ) { $abook_global_file=command63(); }
             elsif ( $command == 4 ) { command64(); }
             elsif ( $command == 5 ) { command65(); }
+            elsif ( $command == 6 ) { command_abook_file_line_length(); }
         } elsif ( $menu == 7 ) {
             if ( $command == 1 ) { $motd = command71(); }
         } elsif ( $menu == 8 ) {
@@ -3322,6 +3327,37 @@ sub command65 {
     return $abook_global_file_listing;
 }
 
+# controls $abook_file_line_length setting
+sub command_abook_file_line_length {
+    print "This setting controls space allocated to file based address book records.\n";
+    print "End users will be unable to save address book entry, if total entry size \n";
+    print "(quoted address book fields + 4 delimiters + linefeed) exceeds allowed \n";
+    print "address book length size.\n";
+    print "\n";
+    print "Same setting is applied to personal and global file based address books.\n";
+    print "\n";
+    print "It is strongly recommended to keep default setting value. Change it only\n";
+    print "if you really want to store address book entries that are bigger than two\n";
+    print "kilobytes (2048).\n";
+    print "\n";
+
+    print "Enter allowed address book line length [$abook_file_line_length]: ";
+    my $tmp = <STDIN>;
+    $tmp = trim($tmp);
+    # value is not modified, if user hits Enter or enters space
+    if ($tmp ne '') {
+        # make sure that input is numeric
+        if ($tmp =~ /^\d+$/) {
+            $abook_file_line_length = $tmp;
+        } else {
+            print "If you want to change this setting, you must enter number.\n";
+            print "If you want to keep original setting - enter space.\n\n";
+            print "Press Enter to continue...";
+            $tmp = <STDIN>;
+        }
+    }
+}
+
 sub command91 {
     print "If you want to store your users address book details in a database then\n";
     print "you need to set this DSN to a valid value. The format for this is:\n";
@@ -4094,6 +4130,8 @@ sub save_data {
         print CF "\$abook_global_file_writeable = $abook_global_file_writeable;\n\n";
         # boolean
         print CF "\$abook_global_file_listing = $abook_global_file_listing;\n\n";
+        # integer
+        print CF "\$abook_file_line_length = $abook_file_line_length;\n\n";
         # boolean
         print CF "\$no_list_for_subscribe = $no_list_for_subscribe;\n";
 

+ 18 - 0
config/config_default.php

@@ -917,6 +917,24 @@ $abook_global_file_writeable = false;
  */
 $abook_global_file_listing = true;
 
+/**
+ * Controls file based address book entry size
+ * 
+ * This setting controls space allocated to file based address book records.
+ * End users will be unable to save address book entry, if total entry size 
+ * (quoted address book fields + 4 delimiters + linefeed) exceeds allowed
+ * address book length size.
+ *
+ * Same setting is applied to personal and global file based address books.
+ *
+ * It is strongly recommended to keep default setting value. Change it only
+ * if you really want to store address book entries that are bigger than two
+ * kilobytes (2048).
+ * @global integer $abook_file_line_length
+ * @since 1.5.2
+ */
+$abook_file_line_length = 2048;
+
 /**
  * MOTD
  *

+ 98 - 37
functions/abook_local_file.php

@@ -81,6 +81,13 @@ class abook_local_file extends addressbook_backend {
      * @var string
      */
     var $umask;
+    /**
+     * Sets max entry size (number of bytes used for all address book fields 
+     * (including escapes) + 4 delimiters + 1 linefeed)
+     * @var integer
+     * @since 1.5.2
+     */
+    var $line_length = 2048;
 
     /* ========================== Private ======================= */
 
@@ -122,6 +129,9 @@ class abook_local_file extends addressbook_backend {
             if(isset($param['listing'])) {
                 $this->listing = $param['listing'];
             }
+            if(isset($param['line_length']) && ! empty($param['line_length'])) {
+                $this->line_length = (int) $param['line_length'];
+            }
 
             $this->open(true);
         } else {
@@ -274,22 +284,32 @@ class abook_local_file extends addressbook_backend {
         }
         @rewind($this->filehandle);
 
-        while ($row = @fgetcsv($this->filehandle, 2048, '|')) {
-            $line = join(' ', $row);
-            /**
-             * TODO: regexp search is supported only in local_file backend.
-             * Do we check format of regexp or ignore errors?
-             */
-            // errors on eregi call are suppressed in order to prevent display of regexp compilation errors
-            if(@eregi($expr, $line)) {
-                array_push($res, array('nickname'  => $row[0],
-                    'name'      => $this->fullname($row[1], $row[2]),
-                    'firstname' => $row[1],
-                    'lastname'  => $row[2],
-                    'email'     => $row[3],
-                    'label'     => $row[4],
-                    'backend'   => $this->bnum,
-                    'source'    => &$this->sname));
+        while ($row = @fgetcsv($this->filehandle, $this->line_length, '|')) {
+            if (count($row)<5) {
+                /**
+                 * address book is corrupted.
+                 */
+                global $oTemplate;
+                error_box(_("Address book is corrupted. Required fields are missing."));
+                $oTemplate->display('footer.tpl');
+                die();
+            } else {
+                $line = join(' ', $row);
+                /**
+                 * TODO: regexp search is supported only in local_file backend.
+                 * Do we check format of regexp or ignore errors?
+                 */
+                // errors on eregi call are suppressed in order to prevent display of regexp compilation errors
+                if(@eregi($expr, $line)) {
+                    array_push($res, array('nickname'  => $row[0],
+                        'name'      => $this->fullname($row[1], $row[2]),
+                        'firstname' => $row[1],
+                        'lastname'  => $row[2],
+                        'email'     => $row[3],
+                        'label'     => $row[4],
+                        'backend'   => $this->bnum,
+                        'source'    => &$this->sname));
+                }
             }
         }
 
@@ -311,16 +331,26 @@ class abook_local_file extends addressbook_backend {
         $this->open();
         @rewind($this->filehandle);
 
-        while ($row = @fgetcsv($this->filehandle, 2048, '|')) {
-            if(strtolower($row[0]) == $alias) {
-                return array('nickname'  => $row[0],
-                  'name'      => $this->fullname($row[1], $row[2]),
-                  'firstname' => $row[1],
-                  'lastname'  => $row[2],
-                  'email'     => $row[3],
-                  'label'     => $row[4],
-                  'backend'   => $this->bnum,
-                  'source'    => &$this->sname);
+        while ($row = @fgetcsv($this->filehandle, $this->line_length, '|')) {
+            if (count($row)<5) {
+                /**
+                 * address book is corrupted.
+                 */
+                global $oTemplate;
+                error_box(_("Address book is corrupted. Required fields are missing."));
+                $oTemplate->display('footer.tpl');
+                die();
+            } else {
+                if(strtolower($row[0]) == $alias) {
+                   return array('nickname'  => $row[0],
+                      'name'      => $this->fullname($row[1], $row[2]),
+                      'firstname' => $row[1],
+                      'lastname'  => $row[2],
+                      'email'     => $row[3],
+                      'label'     => $row[4],
+                      'backend'   => $this->bnum,
+                      'source'    => &$this->sname);
+                }
             }
         }
 
@@ -341,15 +371,26 @@ class abook_local_file extends addressbook_backend {
         $this->open();
         @rewind($this->filehandle);
 
-        while ($row = @fgetcsv($this->filehandle, 2048, '|')) {
-            array_push($res, array('nickname'  => $row[0],
-                'name'      => $this->fullname($row[1], $row[2]),
-                'firstname' => $row[1],
-                'lastname'  => $row[2],
-                'email'     => $row[3],
-                'label'     => $row[4],
-                'backend'   => $this->bnum,
-                'source'    => &$this->sname));
+        while ($row = @fgetcsv($this->filehandle, $this->line_length, '|')) {
+            if (count($row)<5) {
+                /**
+                 * address book is corrupted. Don't be nice to people that 
+                 * violate address book formating.
+                 */
+                global $oTemplate;
+                error_box(_("Address book is corrupted. Required fields are missing."));
+                $oTemplate->display('footer.tpl');
+                die();
+            } else {
+                array_push($res, array('nickname'  => $row[0],
+                    'name'      => $this->fullname($row[1], $row[2]),
+                    'firstname' => $row[1],
+                    'lastname'  => $row[2],
+                    'email'     => $row[3],
+                    'label'     => $row[4],
+                    'backend'   => $this->bnum,
+                    'source'    => &$this->sname));
+            }
         }
         return $res;
     }
@@ -379,6 +420,15 @@ class abook_local_file extends addressbook_backend {
 
         /* Strip linefeeds */
         $data = ereg_replace("[\r\n]", ' ', $data);
+
+        /**
+         * Make sure that entry fits into allocated record space.
+         * One byte is reserved for linefeed
+         */
+        if (strlen($data) >= $this->line_length) {
+            return $this->set_error(_("Address book entry is too big"));
+        }
+
         /* Add linefeed at end */
         $data = $data . "\n";
 
@@ -429,7 +479,7 @@ class abook_local_file extends addressbook_backend {
         @rewind($this->filehandle);
         $i = 0;
         $rows = array();
-        while($row = @fgetcsv($this->filehandle, 2048, '|')) {
+        while($row = @fgetcsv($this->filehandle, $this->line_length, '|')) {
             if(!in_array($row[0], $alias)) {
                 $rows[$i++] = $row;
             }
@@ -469,13 +519,24 @@ class abook_local_file extends addressbook_backend {
             return $this->set_error(_("Could not lock datafile"));
         }
 
+        /* calculate userdata size */
+        $data = $this->quotevalue($userdata['nickname']) . '|'
+            . $this->quotevalue($userdata['firstname']) . '|'
+            . $this->quotevalue((!empty($userdata['lastname'])?$userdata['lastname']:'')) . '|'
+            . $this->quotevalue($userdata['email']) . '|'
+            . $this->quotevalue((!empty($userdata['label'])?$userdata['label']:''));
+        /* make sure that it fits into allocated space */
+        if (strlen($data) >= $this->line_length) {
+            return $this->set_error(_("Address book entry is too big"));
+        }
+        
         /* Read file into memory, modifying the data for the
          * user identified by $alias */
         $this->open(true);
         @rewind($this->filehandle);
         $i = 0;
         $rows = array();
-        while($row = @fgetcsv($this->filehandle, 2048, '|')) {
+        while($row = @fgetcsv($this->filehandle, $this->line_length, '|')) {
             if(strtolower($row[0]) != strtolower($alias)) {
                 $rows[$i++] = $row;
             } else {

+ 4 - 2
functions/addressbook.php

@@ -31,6 +31,7 @@ function addressbook_init($showerr = true, $onlylocal = false) {
     global $addrbook_dsn, $addrbook_table;
     global $abook_global_file, $abook_global_file_writeable, $abook_global_file_listing;
     global $addrbook_global_dsn, $addrbook_global_table, $addrbook_global_writeable, $addrbook_global_listing;
+    global $abook_file_line_length;
 
     /* Create a new addressbook object */
     $abook = new AddressBook;
@@ -58,7 +59,8 @@ function addressbook_init($showerr = true, $onlylocal = false) {
         /* File */
         $filename = getHashedFile($username, $data_dir, "$username.abook");
         $r = $abook->add_backend('local_file', Array('filename' => $filename,
-                              'create'   => true));
+                                                     'line_length' => $abook_file_line_length,
+                                                     'create'   => true));
         if(!$r && $showerr) {
             // no need to use $abook->error, because message explains error.
             $abook_init_error.=sprintf( _("Error opening file %s"), $filename );
@@ -90,6 +92,7 @@ function addressbook_init($showerr = true, $onlylocal = false) {
         $r = $abook->add_backend('local_file',array('filename'=>$abook_global_filename,
                                                     'name' => _("Global address book"),
                                                     'detect_writeable' => false,
+                                                    'line_length' => $abook_file_line_length,
                                                     'writeable'=> $abook_global_file_writeable,
                                                     'listing' => $abook_global_file_listing));
 
@@ -136,7 +139,6 @@ function addressbook_init($showerr = true, $onlylocal = false) {
         $abook_init_error.=_("Error initializing other address books.") . "\n" . $abook->error;
     }
 
-
     /* Load configured LDAP servers (if PHP has LDAP support) */
     if (isset($ldap_server) && is_array($ldap_server)) {
         reset($ldap_server);