Address book file backend will break with error message, if required
authortokul <tokul@7612ce4b-ef26-0410-bec9-ea0150e637f0>
Tue, 27 Jun 2006 10:08:53 +0000 (10:08 +0000)
committertokul <tokul@7612ce4b-ef26-0410-bec9-ea0150e637f0>
Tue, 27 Jun 2006 10:08:53 +0000 (10:08 +0000)
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).

git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@11311 7612ce4b-ef26-0410-bec9-ea0150e637f0

ChangeLog
config/conf.pl
config/config_default.php
functions/abook_local_file.php
functions/addressbook.php

index 17eeb0d..6588c2b 100644 (file)
--- a/ChangeLog
+++ b/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)
 --------------------------------------
index bf20d6a..3ec71ea 100755 (executable)
@@ -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";
 
index 249d23d..30faa13 100644 (file)
@@ -918,6 +918,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
  *
  * This is a message that is displayed immediately after a user logs in.
index 63b6a4a..fb215e2 100644 (file)
@@ -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 {
index e6f2e84..9b10a3c 100644 (file)
@@ -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);