From 7311c377a04c1d713fcf090e120a6ac4fa93940a Mon Sep 17 00:00:00 2001 From: tokul Date: Tue, 27 Jun 2006 10:08:53 +0000 Subject: [PATCH] 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). git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@11311 7612ce4b-ef26-0410-bec9-ea0150e637f0 --- ChangeLog | 4 + config/conf.pl | 38 ++++++++++ config/config_default.php | 18 +++++ functions/abook_local_file.php | 135 ++++++++++++++++++++++++--------- functions/addressbook.php | 6 +- 5 files changed, 162 insertions(+), 39 deletions(-) diff --git a/ChangeLog b/ChangeLog index 17eeb0de..6588c2bd 100644 --- 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) -------------------------------------- diff --git a/config/conf.pl b/config/conf.pl index bf20d6a8..3ec71ea5 100755 --- a/config/conf.pl +++ b/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 = ; + $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 = ; + } + } +} + 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"; diff --git a/config/config_default.php b/config/config_default.php index 249d23d1..30faa13a 100644 --- a/config/config_default.php +++ b/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 * diff --git a/functions/abook_local_file.php b/functions/abook_local_file.php index 63b6a4a7..fb215e27 100644 --- a/functions/abook_local_file.php +++ b/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 { diff --git a/functions/addressbook.php b/functions/addressbook.php index e6f2e844..9b10a3cc 100644 --- a/functions/addressbook.php +++ b/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); -- 2.25.1