Вход Регистрация
Файл: apwa/includes/editor.php
Строк: 1741
<?php
/**
*
* @package automod
* @version $Id$
* @copyright (c) 2008 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
*
*/

/**
*/
if (!defined('IN_PHPBB'))
{
    exit;
}

// Constant Defines for actions
//define('AFTER',        1);
//define('BEFORE',    2);

/**
* Editor Class
* Runs through file sequential, ie new finds must come after previous finds
* Handles placing the files after being edited
* @package automod
* @todo: implement some string checkin, way too much can go wild here
*/
class editor
{
    
/**
    * Holds contents of file
    */
    
var $file_contents '';

    
/**
    * Holds the filename of the currently open file
    */
    
var $open_filename '';

    
/**
    * Full action array with complete finds
    */
    
var $mod_actions = array();

    
/**
    * One of the three constants defined in functions_mods.php
    */
    
var $write_method 0;

    
/**
    * Keeps finds sequential, plus loop optimization
    */
    
var $start_index 0;

    
/*
    * Keeps inline find sequential
    */
    
var $last_string_offset 0;

    
/*
    * Only apply string offset to the line to which it belongs
    */
    
var $last_inline_ary_offset 0;

    
/**
    * Time when MOD was installed
    */
    
var $install_time 0;

    
/**
    * Only used when board has templates stored in the database
    */
    
var $template_id 0;

    
/**
    * Store the  current action & most recent action to aid the uninstall building process
    */
    
var $last_action = array();
    var 
$curr_action = array();

    
/**
    * Constructor method
    * This is not called directly in AutoMOD
    */
    
function editor()
    {

    }

    
/**
    * Make all line endings the same - UNIX
    */
    
function normalize($string)
    {
        
$string str_replace(array("rn""r"), "n"$string);
        return 
$string;
    }

    
/**
    * Open a file with IO, for processing
    *
    * @param string $filename - relative path from phpBB Root to the file to open
    *         e.g. viewtopic.php, styles/prosilver/templates/index_body.html
    */
    
function open_file($filename$backup_path '')
    {
        global 
$phpbb_root_path$db$user;

        
$this->file_contents = @file($phpbb_root_path $filename);

        if (
$this->file_contents === false)
        {
            return 
$user->lang['FILE_EMPTY'];
        }

        
$this->file_contents $this->normalize($this->file_contents);

        
// Check for file contents in the database if this is a template file
        // this will overwrite the @file call if it exists in the DB.
        
if (strpos($filename'template/') !== false)
        {
            
// grab template name and filename
            //preg_match('#styles/([a-z0-9_]+)/template/([a-z0-9_]+.[a-z]+)#i', $filename, $match);
            
preg_match('#styles/([a-z0-9_-]+)/template/([a-z0-9_-]+.[a-z]+)#i'$filename$match);

            
$sql 'SELECT d.template_data, d.template_id
                FROM ' 
STYLES_TEMPLATE_DATA_TABLE ' d, ' STYLES_TEMPLATE_TABLE " t
                WHERE d.template_filename = '" 
$db->sql_escape($match[2]) . "'
                    AND t.template_id = d.template_id
                    AND t.template_storedb = 1
                    AND t.template_name = '" 
$db->sql_escape($match[1]) . "'";
            
$result $db->sql_query($sql);

            if (
$row $db->sql_fetchrow($result))
            {
                
$this->file_contents explode("n"$this->normalize($row['template_data']));

                
// emulate the behavior of file()
                
$lines sizeof($this->file_contents);
                for (
$i 0$i $lines$i++)
                {
                    
$this->file_contents[$i] .= "n";
                }

                
$this->template_id $row['template_id'];
            }
            else
            {
                
$this->template_id 0;
            }
        }

        
/*
        * If the file does not exist, or is empty, die.
        * Non existant files cannot be edited, and empty files will have no
        * finds
        */
        
if (!sizeof($this->file_contents))
        {
            global 
$user;
            
trigger_error(sprintf($user->lang['MOD_OPEN_FILE_FAIL'], "$phpbb_root_path$filename"), E_USER_WARNING);
        }

        
$this->start_index 0;
        
$this->open_filename $filename;

        
// Should we backup this file?
        
if ($backup_path)
        {
            
$this->backup_file($backup_path);
        }
    }

    
/**
    * Checks if a find is present
    * Keep in mind partial finds and multi-line finds
    *
    * @param string $find - string to find
    * @return mixed : array with position information if $find is found; false otherwise
    */
    
function find($find)
    {
        if (
$find == '')
        {
            
// Can't find a empty string,
            
return(false);
        }

        
$find_success 0;

        
$find $this->normalize($find);
        
$find_ary explode("n"rtrim($find"n"));

        
$total_lines sizeof($this->file_contents);
        
$find_lines sizeof($find_ary);

        
$mode = array('''trim');

        foreach (
$mode as $function)
        {
            
// we process the file sequentially ... so we keep track of indices
            
for ($i $this->start_index$i $total_lines$i++)
            {
                for (
$j 0$j $find_lines$j++)
                {
                    if (
$function)
                    {
                        
$find_ary[$j] = $function($find_ary[$j]);
                    }

                    
// if we've reached the EOF, the find failed.
                    
if (!isset($this->file_contents[$i $j]))
                    {
                        return 
false;
                    }

                    if (!
trim($find_ary[$j]))
                    {
                        
// line is blank.  Assume we can find a blank line, and continue on
                        
$find_success += 1;
                    }
                    
// using $this->file_contents[$i + $j] to keep the array pointer where I want it
                    // if the first line of the find (index 0) is being looked at, $i + $j = $i.
                    // if $j is > 0, we look at the next line of the file being inspected
                    // hopefully, this is a decent performer.
                    
else if (strpos($this->file_contents[$i $j], $find_ary[$j]) !== false)
                    {
                        
// we found this part of the find
                        
$find_success += 1;
                    }
                    
// we might have an increment operator, which requires a regular expression match
                    
else if (strpos($find_ary[$j], '{%:') !== false)
                    {
                        
$regex preg_replace('#{%:(d+)}#''(d+)'$find_ary[$j]);

                        if (
preg_match('#' $regex '#is'$this->file_contents[$i $j]))
                        {
                            
$find_success += 1;
                        }
                        else
                        {
                            
$find_success 0;
                        }
                    }
                    else
                    {
                        
// the find failed.  Reset $find_success
                        
$find_success 0;

                        
// skip to next iteration of outer loop, that is, skip to the next line
                        
break;
                    }

                    if (
$find_success == $find_lines)
                    {
                        
// we found the proper number of lines
                        
$this->start_index $i;

                        
// return our array offsets
                        
return array(
                            
'start' => $i,
                            
'end' => $i $j,
                        );
                    }

                }
            }
        }

        
// if return has not been previously invoked, the find failed.
        
return false;
    }

    
/**
    * This function is used to determine when an edit has ended, so we know that
    * the current line will not be looked at again.  This fixes some former bugs.
    */
    
function close_edit()
    {
        
$this->start_index++;
        
$this->last_action = array();
        
$this->last_string_offset 0;
    }

    
/*
    * In-line analog to close_edit(), above.
    * Advance the pointer one character
    */
    
function close_inline_edit()
    {
        
$this->last_string_offset++;
    }

    
/**
    * Find a string within a given line
    *
    * @param string $find Complete find - narrows the scope of the inline search
    * @param string $inline_find - the substring to find
    * @param int $start_offset - the line number where $find starts
    * @param int $end_offset - the line number where $find ends
    *
    * @return mixed array on success or false on failure of find
    */
    
function inline_find($find$inline_find$start_offset false$end_offset false)
    {
        if (
$inline_find == '')
        {
            
// Can't find a empty string,
            
return(false);
        }

        
$find $this->normalize($find);

        if (
$start_offset === false || $end_offset === false)
        {
            
$offsets $this->find($find);

            if (!
$offsets)
            {
                
// the find failed, so no further action can occur.
                
return false;
            }

            
$start_offset $offsets['start'];
            
$end_offset $offsets['end'];

            unset(
$offsets);
        }

        
// cast is required in case someone tries to find a number
        // Often done in colspan="7" type inline operations
        
$inline_find = (string) $inline_find;

        
// similar method to find().  Just much more limited scope
        
for ($i $start_offset$i <= $end_offset$i++)
        {
            if (
$this->last_string_offset && ($this->last_inline_ary_offset == || $this->last_inline_ary_offset == $i))
            {
                
$string_offset strpos(substr($this->file_contents[$i], $this->last_string_offset), $inline_find);

                if (
$string_offset !== false)
                {
                    
$string_offset += $this->last_string_offset;
                }
            }
            else
            {
                
$string_offset strpos($this->file_contents[$i], $inline_find);
            }

            if (
$string_offset !== false)
            {
                
$this->last_string_offset $string_offset;
                
$this->last_inline_ary_offset $i;

                
// if we find something, return the line number, string offset, and find length
                
return array(
                    
'array_offset'    => $i,
                    
'string_offset'    => $string_offset,
                    
'find_length'    => strlen($inline_find),
                );
            }
        }

        
// if the previous failed, trim() the find and try again
        
for ($i $start_offset$i <= $end_offset$i++)
        {
            
$inline_find trim($inline_find);
            if (
$this->last_string_offset && ($this->last_inline_ary_offset == || $this->last_inline_ary_offset == $i))
            {
                
$string_offset strpos(substr($this->file_contents[$i], $this->last_string_offset), $inline_find);

                if (
$string_offset !== false)
                {
                    
$string_offset += $this->last_string_offset;
                }
            }
            else
            {
                
$string_offset strpos($this->file_contents[$i], $inline_find);
            }

            if (
$string_offset !== false)
            {
                
$this->last_string_offset $string_offset;

                
// if we find something, return the line number, string offset, and find length
                
return array(
                    
'array_offset'    => $i,
                    
'string_offset'    => $string_offset,
                    
'find_length'    => strlen($inline_find),
                );
            }
        }

        return 
false;
    }


    
/**
    * Add a string to the file, BEFORE/AFTER the given find string
    * @param string $find - Complete find - narrows the scope of the inline search
    * @param string $add - The string to be added before or after $find
    * @param string $pos - BEFORE or AFTER
    * @param int $start_offset - First line in the FIND
    * @param int $end_offset - Last line in the FIND
    *
    * @return bool success or failure of add
    */
    
function add_string($find$add$pos$start_offset false$end_offset false)
    {
        
// this seems pretty simple...throughly test
        
$add $this->normalize($add);

        if (
$start_offset === false || $end_offset === false)
        {
            
$offsets $this->find($find);

            if (!
$offsets)
            {
                
// the find failed, so the add cannot occur.
                
return false;
            }

            
$start_offset $offsets['start'];
            
$end_offset $offsets['end'];

            unset(
$offsets);
        }

        
$full_find = array();
        for (
$i $start_offset$i <= $end_offset$i++)
        {
            
$full_find[] = $this->file_contents[$i];
        }

        
$full_find[0] = ltrim($full_find[0], "n");
        
$full_find[sizeof($full_find) - 1] = rtrim($full_find[sizeof($full_find) - 1], "n");

        
// make sure our new lines are correct
        
$add "n" trim($add"n") . "n";

        if (
$pos == 'AFTER')
        {
            
$this->file_contents[$end_offset] = rtrim($this->file_contents[$end_offset], "n") . $add;
        }

        if (
$pos == 'BEFORE')
        {
            
$this->file_contents[$start_offset] = $add ltrim($this->file_contents[$start_offset], "n");
        }

        
$this->curr_action func_get_args();
        
$this->build_uninstall(implode(""$full_find), NULLstrtolower($pos) . ' add'$add);

        return 
true;
    }

    
/**
    * Increment (or perform other mathematical operation) on the given wildcard
    * Support multiple wildcards {%:1}, {%:2} etc...
    * This method is a variation on the inline find and replace methods
    *
    * @param string $find - Complete find - contains $inline_find
    * @param string $inline_find - contains tokens to be replaced
    * @param string $operation - tokens to do some math
    * @param int $start_offset - First line in the FIND
    * @param int $end_offset - Last line in the FIND
    *
    * @return bool
    */
    
function inc_string($find$inline_find$operation$start_offset false$end_offset false)
    {
        if (
$start_offset === false || $end_offset === false)
        {
            
$offsets $this->find($find);

            if (!
$offsets)
            {
                
// the find failed, so the add cannot occur.
                
return false;
            }

            
$start_offset $offsets['start'];
            
$end_offset $offsets['end'];

            unset(
$offsets);
        }

        
// $inline_find is optional
        
if (!$inline_find)
        {
            
$inline_find $find;
        }

        
// parse the MODX operator
        // let's explain this regex a bit:
        // - literal %: followed by a number.  optional space
        // - plus or minus operator. optional space
        // - number to increment by.  optional
        
preg_match('#{%:(d+)} ?([+-]) ?(d*)#'$operation$action);
        
// make sure there is actually a number here
        
$action[2] = (isset($action[2])) ? $action[2] : '+';
        
$action[3] = (isset($action[3])) ? $action[3] : 1;

        
$matches 0;
        
// $start_offset _should_ equal $end_offset, but we allow other cases
        
for ($i $start_offset$i <= $end_offset$i++)
        {
            
// This is intended.  We turn the MODX token into something PCRE can
            // understand.
            
$inline_find preg_replace('#{%:(d+)}#''(d+)'$inline_find);

            if (
preg_match('#' $inline_find '#is'$this->file_contents[$i], $find_contents))
            {
                
// now we can do some math
                // $find_contents[1] is the original number, $action[2] is the operator
                
$new_number = eval('return ' . ((int) $find_contents[1]) . $action[2] . ((int) $action[3]) . ';');

                
// now we replace
                
$new_contents str_replace($find_contents[1], $new_number$find_contents[0]);

                
$this->file_contents[$i] = str_replace($find_contents[0], $new_contents$this->file_contents[$i]);

                
$matches += 1;
            }
        }

        if (!
$matches)
        {
            return 
false;
        }

        return 
true;
    }


    
/**
    * Replace a string - replaces the entirety of $find with $replace
    *
    * @param string $find - Complete find - contains $inline_find
    * @param string $replace - Will replace $find
    * @param int $start_offset - First line in the FIND
    * @param int $end_offset - Last line in the FIND
    *
    * @return bool
    */
    
function replace_string($find$replace$start_offset false$end_offset false)
    {
        
$replace $this->normalize($replace);

        if (
$start_offset === false || $end_offset === false)
        {
            
$offsets $this->find($find);

            if (!
$offsets)
            {
                return 
false;
            }

            
$start_offset $offsets['start'];
            
$end_offset $offsets['end'];
            unset(
$offsets);
        }

        
// remove each line from the file, but add it to $full_find
        
$full_find = array();
        for (
$i $start_offset$i <= $end_offset$i++)
        {
            
$full_find[] = $this->file_contents[$i];
            
$this->file_contents[$i] = '';
        }

        
$this->file_contents[$start_offset] = rtrim($replace) . "n";

        
$this->curr_action func_get_args();
        
$this->build_uninstall(implode(""$full_find), NULL'replace-with'$replace);

        return 
true;
    }

    
/*
    * Replace $inline_find with $inline_replace
    * Arguments are very similar to inline_add, below
    */
    
function inline_replace($find$inline_find$inline_replace$array_offset false$string_offset false$length false)
    {
        if (
$string_offset === false || $length === false)
        {
            
// look for the inline find
            
$inline_offsets $this->inline_find($find$inline_find);

            if (!
$inline_offsets)
            {
                return 
false;
            }

            
$array_offset $inline_offsets['array_offset'];
            
$string_offset $inline_offsets['string_offset'];
            
$length $inline_offsets['find_length'];
            unset(
$inline_offsets);
        }

        
$this->file_contents[$array_offset] = substr_replace($this->file_contents[$array_offset], $inline_replace$string_offset$length);

        
$this->last_string_offset += strlen($inline_replace) - 1;

        
$this->curr_action func_get_args();

        
// This isn't a full find, but it is the closest we can get
        
$this->build_uninstall($this->file_contents[$array_offset], $inline_find'in-line-replace'$inline_replace);

        return 
true;
    }

    
/**
    * Adds a string inline before or after a given find
    *
    * @param string $find Complete find - narrows the scope of the inline search
    * @param string $inline_find - the string to add before or after
    * @param string $inline_add - added before or after $inline_find
    * @param string $pos - 'BEFORE' or 'AFTER'
    * @param int $array_offset - line number where $inline_find may be found (optional)
    * @param int $string_offset - location within the line where $inline_find begins (optional)
    * @param int $length - essentially strlen($inline_find) (optional)
    *
    * @return bool success or failure of action
    */
    
function inline_add($find$inline_find$inline_add$pos$array_offset false$string_offset false$length false)
    {
        if (
$string_offset === false || $length === false)
        {
            
// look for the inline find
            
$inline_offsets $this->inline_find($find$inline_find);

            if (!
$inline_offsets)
            {
                return 
false;
            }

            
$array_offset $inline_offsets['array_offset'];
            
$string_offset $inline_offsets['string_offset'];
            
$length $inline_offsets['find_length'];
            unset(
$inline_offsets);
        }

        if (
$string_offset $length strlen($this->file_contents[$array_offset]))
        {
            
// we have an invalid string offset.  rats.
            
return false;
        }

        if (
$pos == 'AFTER')
        {
            
$this->file_contents[$array_offset] = substr_replace($this->file_contents[$array_offset], $inline_add$string_offset $length0);
            
$this->last_string_offset += strlen($inline_add) + $length 1;
        }
        else if (
$pos == 'BEFORE')
        {
            
$this->file_contents[$array_offset] = substr_replace($this->file_contents[$array_offset], $inline_add$string_offset0);
            
$this->last_string_offset += (strlen($inline_add) - 1);
        }

        
$this->curr_action func_get_args();

        
$this->build_uninstall($this->file_contents[$array_offset], $inline_find'in-line-' strtolower($pos) . '-add'$inline_add);

        return 
true;
    }

    
/**
    * Function to build full edits such that uninstall will work more often
    *
    * @param $find - The largest find we can put together -- sometimes this
    *         comes from the file itself, other times from the MODX file
    * @param $inline_find - Subset of $find or NULL
    * @param $action_type - Name of the MODX action being taken
    * @param $action - The code which is being inserted into the file
    * @return void
    */
    
function build_uninstall($find$inline_find$action_type$action)
    {
        
$find trim($find"n");
        
$inline_find trim($inline_find"n");
        
$action trim($action"n");

        
/*
        * This if statement finds out if we are in the special case where
        * a MOD specifies a before action and an after action on the same
        * find.  If this is the case, the uninstaller must see a replace
        * rather than an add
        */
        
if (!empty($this->last_action) && $this->last_action[0] == $this->curr_action[0] &&
            ((
$this->last_action[2] == 'AFTER' && $this->curr_action[2] == 'BEFORE')
            || (
$this->last_action[2] == 'BEFORE' && $this->curr_action[2] == 'AFTER')))
        {
            
array_pop($this->mod_actions[$this->open_filename]);

            
$action_type 'REPLACE';

            
// Remove the add from the find -- this is an effect of the way the
            // add method works, putting the new lines in the same array element
            // as the find
            
$find str_replace(trim($this->last_action[1]), ''$find);

            if (
$this->last_action[2] == 'AFTER')
            {
                
$action $this->curr_action[1] . "n" $this->curr_action[0] . "n" $this->last_action[1];
            }
            else 
// implicit if ($this->last_action[2] == 'BEFORE')
            
{
                
$action $this->last_action[1] . "n" $this->curr_action[0] . "n" $this->curr_action[1];
            }
        }

        
// Build another complex array of MOD Actions
        // This approach is rather memory-intensive ... it might behoove us
        // to think of something else
        
if (!$inline_find)
        {
            
$this->mod_actions[$this->open_filename][] = array(
                
$find => array(
                    
$action_type => $action,
                )
            );
        }
        else
        {
            
// Do we have preceding in-line-edit(s) on the same complete find or line?
            
if (!empty($this->last_action) && $this->last_action[0] == $this->curr_action[0])
            {
                
$prev_inline_edits array_pop($this->mod_actions[$this->open_filename]);
                
$prev_find key($prev_inline_edits);

                
// Add our current in-line-edit
                
$prev_inline_edits[$prev_find]['in-line-edit'][] = array(
                    
$inline_find    => array(
                        
$action_type    => array($action),
                    ),
                );

                
// Add the new set of in-line-edit's to our MOD Actions array, w/ the updated $find
                
$this->mod_actions[$this->open_filename][] = array(
                    
$find => $prev_inline_edits[$prev_find]
                );
            }
            else
            {
                
$this->mod_actions[$this->open_filename][] = array(
                    
$find => array(
                        
'in-line-edit'    => array(
                            array(
                                
$inline_find    => array(
                                    
$action_type    => array($action),
                                ),
                            ),
                        ),
                    ),
                );
            }
        }

        
$this->last_action $this->curr_action;
    }

    function 
clear_actions()
    {
        
// free some memory
        
$this->mod_actions = array();
    }
}

/**
* @package automod
* class editor_direct will alter files by using the local file access functions
* such as fopen and fwrite.  This is typically only useful in Windows environments
* due to permissions settings.
*/
class editor_direct extends editor
{
    function 
editor_direct()
    {
        
$this->write_method WRITE_DIRECT;
        
$this->install_time time();
    }

    
/**
    * Copies files or complete directories
    *
    * @param $from string Can be a file or a directory. Will copy either the file or all files within the directory
    * @param $to string Where to copy the file(s) to. If not specified then will get copied to the phpbb root directory
    * @return mixed: Bool true on success, error string on failure, NULL if no action was taken
    *
    * NOTE: function should preferably not return in case of failure on only one file.
    *     The current method makes error handling difficult
    */
    
function copy_content($from$to '')
    {
        global 
$phpbb_root_path$user$config;

        if (
strpos($from$phpbb_root_path) !== 0)
        {
            
$from $phpbb_root_path $from;
        }

        if (
strpos($to$phpbb_root_path) !== 0)
        {
            
$to $phpbb_root_path $to;
        }

        
$files = array();
        if (
is_dir($from))
        {
            
$files find_files($from'.*');
            
$dirname_check $to;
            
$to_is_dir true;
        }
        else if (
is_file($from))
        {
            
$files = array($from);
            
$dirname_check dirname($to);
            
$to_is_dir false;
        }

        if (empty(
$files))
        {
            return 
false;
        }
        else if (!
is_dir($dirname_check) && $this->recursive_mkdir($dirname_check) === false)
        {
            return 
sprintf($user->lang['MODS_MKDIR_FAILED'], $dirname_check);
        }

        foreach (
$files as $file)
        {
            if (
$to_is_dir)
            {
                
$dest str_replace($from$to$file);

                
$dest_parent_dir dirname($dest);
                if (!
file_exists($dest_parent_dir))
                {
                    
$this->recursive_mkdir($dest_parent_dir);
                }
            }
            else
            {
                
$dest $to;
            }

            if (!@
copy($file$dest))
            {
                return 
sprintf($user->lang['MODS_COPY_FAILURE'], $dest);
            }
            @
chmod($destoctdec($config['am_file_perms']));
        }

        return 
true;
    }

    function 
close_file($new_filename)
    {
        global 
$phpbb_root_path$config$mod_installed$mod_uninstalled$force_install;
        global 
$db$user;

        if (!
is_dir($new_filename) && !file_exists(dirname($new_filename)))
        {
            if (
$this->recursive_mkdir(dirname($new_filename)) === false)
            {
                return 
sprintf($user->lang['MODS_MKDIR_FAILED'], dirname($new_filename));
            }
        }

        
$file_contents implode(''$this->file_contents);

        if (
file_exists($new_filename) && !is_writable($new_filename))
        {
            return 
sprintf($user->lang['WRITE_DIRECT_FAIL'], $new_filename);
        }

        if (
$this->template_id && ($mod_installed || $mod_uninstalled || $force_install))
        {
            
update_database_template($new_filename$this->template_id$file_contents$this->install_time);
        }

        
// If we are not looking at a file stored in the database, use local file functions
        
$fr = @fopen($new_filename'wb');
        
$length_written = @fwrite($fr$file_contents);
        @
chmod($new_filenameoctdec($config['am_file_perms']));

        
// This appears to be correct even with multibyte encodings.  strlen and
        // fwrite both return the number of bytes written, not the number of chars
        
if ($length_written strlen($file_contents))
        {
            return 
sprintf($user->lang['WRITE_DIRECT_TOO_SHORT'], $new_filename);
        }

        if (!@
fclose($fr))
        {
            return 
sprintf($user->lang['WRITE_DIRECT_FAIL'], $new_filename);
        }

        return 
true;
    }

    
/**
    * Creates a backup of the currently open file before AutoMOD makes any changes
    */
    
function backup_file($backup_dir)
    {
        return 
$this->close_file($backup_dir $this->open_filename);
    }

    
/**
    * @author Michal Nazarewicz (from the php manual)
    * Creates all non-existant directories in a path
    * @param $path - path to create
    * @param $mode - CHMOD the new dir to these permissions
    * @return bool
    */
    
function recursive_mkdir($path$mode false)
    {
        if (!
$mode)
        {
            global 
$config;
            
$mode octdec($config['am_dir_perms']);
        }

        
$dirs explode('/'$path);
        
$count sizeof($dirs);
        
$path '.';
        for (
$i 0$i $count$i++)
        {
            
$path .= '/' $dirs[$i];

            if (!
is_dir($path))
            {
                @
mkdir($path$mode);
                @
chmod($path$mode);

                if (!
is_dir($path))
                {
                    return 
false;
                }
            }
        }
        return 
true;
    }

    function 
commit_changes($source$destination)
    {
        return 
$this->copy_content($source$destination);
    }

    function 
commit_changes_final($source$destination)
    {
        return 
NULL;
    }

    function 
create_edited_root($dir)
    {
        return 
$this->recursive_mkdir($dir);
    }

    
/**
     * Removes a file or a directory (optionally recursively)
     *
     * @param    $path        string (required)    - Filename/Directory path to remove
     * @param    $recursive    bool (optional)        - Recursively delete directories
     * @author jasmineaura
     */
    
function remove($path$recursive false)
    {
        global 
$phpbb_root_path$user;

        if (
strpos($path$phpbb_root_path) !== 0)
        {
            
$path $phpbb_root_path $path;
        }

        if (
is_dir($path))
        {
            if (
$recursive)
            {
                
// recursively delete (in functions_mod.php)
                
return recursive_unlink($path);
            }
            else
            {
                if (
check_empty_dir($path))
                {
                    
// No error message.
                    // If the directory is not empty it is probably not an error.

                    
if (!rmdir($path))
                    {
                        return 
sprintf($user->lang['MODS_RMDIR_FAILURE'], $path);
                    }
                }
            }
        }
        else
        {
            if (!
unlink($path))
            {
                return 
sprintf($user->lang['MODS_RMFILE_FAILURE'], $path);
            }
        }

        return 
true;
    }
}

class 
editor_ftp extends editor
{
    var 
$transfer;

    function 
editor_ftp()
    {
        global 
$config$user;

        
$this->write_method WRITE_FTP;
        
$this->install_time time();

        if (!
class_exists('transfer'))
        {
            global 
$phpbb_root_path$phpEx;
            include(
$phpbb_root_path 'includes/functions_transfer.' $phpEx);
        }

        
$this->transfer = new $config['ftp_method']($config['ftp_host'], $config['ftp_username'], request_var('password'''), $config['ftp_root_path'], $config['ftp_port'], $config['ftp_timeout']);
        
$error $this->transfer->open_session();

        
// Use the permissions settings specified in the AutoMOD configuration
        
$this->transfer->dir_perms octdec($config['am_dir_perms']);
        
$this->transfer->file_perms octdec($config['am_file_perms']);

        if (
is_string($error))
        {
            
// FTP login failed
            
trigger_error(sprintf($user->lang['MODS_FTP_CONNECT_FAILURE'], $user->lang[$error]), E_USER_ERROR);
        }
    }

    
/**
    * Copies files or complete directories
    *
    * @param $from string Can be a file or a directory. Will copy either the file or all files within the directory
    * @param $to string Where to copy the file(s) to. If not specified then will get copied to the phpbb root directory
    * @return mixed: Bool true on success, error string on failure, NULL if no action was taken
    *
    * NOTE: function should preferably not return in case of failure on only one file.
    *     The current method makes error handling difficult
    */
    
function copy_content($from$to '')
    {
        global 
$phpbb_root_path$user;

        
// Prefix $from with $phpbb_root_path
        
if (strpos($from$phpbb_root_path) !== 0)
        {
            
$from $phpbb_root_path $from;
        }

        if (
strpos($to$phpbb_root_path) !== 0)
        {
            
$to $phpbb_root_path $to;
        }

        
$files = array();
        if (
is_dir($from))
        {
            
$files find_files($from'.*');
            
$dirname_check $to;
            
$to_is_dir true;
        }
        else if (
is_file($from))
        {
            
$files = array($from);
            
$dirname_check dirname($to);
            
$to_is_dir false;
        }

        if (empty(
$files))
        {
            return 
false;
        }
        else if (!
is_dir($dirname_check) && $this->recursive_mkdir($dirname_check) === false)
        {
            return 
sprintf($user->lang['MODS_MKDIR_FAILED'], $dirname_check);
        }

        foreach (
$files as $file)
        {
            if (
$to_is_dir)
            {
                
$dest str_replace($from$to$file);

                
$dest_parent_dir dirname($dest);
                if (!
file_exists($dest_parent_dir))
                {
                    
$this->recursive_mkdir($dest_parent_dir);
                }
            }
            else
            {
                
$dest $to;
            }

            
// Per functions_transfer.php, overwrite_file() (which we'll use here) is the only
            // transfer method that doesn't strip out phpbb_root_path, because it is called
            // by other methods (write_file, copy_file) that already do so before calling it.
            // The ftp class prepends the real (ftp) $root_path !
            
$dest str_replace($phpbb_root_path''$dest);

            if (!
$this->transfer->overwrite_file($file$dest))
            {
                return 
sprintf($user->lang['MODS_FTP_FAILURE'], $dest);
            }
        }

        return 
true;
    }

    
/**
    * Write & close file
    */
    
function close_file($new_filename)
    {
        global 
$phpbb_root_path$edited_root$mod_installed$mod_uninstalled$force_install;
        global 
$db$user;

        if (!
is_dir($new_filename) && !file_exists(dirname($new_filename)))
        {
            if (
$this->recursive_mkdir(dirname($new_filename)) === false)
            {
                return 
sprintf($user->lang['MODS_MKDIR_FAILED'], dirname($new_filename));
            }
        }

        
$file_contents implode(''$this->file_contents);

        if (
$this->template_id && ($mod_installed || $mod_uninstalled || $force_install))
        {
            
update_database_template($new_filename$this->template_id$file_contents$this->install_time);
        }

        if (!
$this->transfer->write_file($new_filename$file_contents))
        {
            return 
sprintf($user->lang['MODS_FTP_FAILURE'], $new_filename);
        }

        return 
true;
    }

    
/**
    * Creates a backup of the currently open file before AutoMOD makes any changes
    */
    
function backup_file($backup_dir)
    {
        return 
$this->close_file($backup_dir $this->open_filename);
    }

    
/**
    * @ignore
    */
    
function recursive_mkdir($path$mode false)
    {
        if (
$mode)
        {
            
$this->transfer->dir_perms $mode;
        }

        return 
$this->transfer->make_dir($path);
    }

    function 
commit_changes($source$destination)
    {
        
// Move edited files back
        
return $this->copy_content($source$destination);
    }

    function 
commit_changes_final($source$destionation)
    {
        return 
NULL;
    }

    function 
create_edited_root($dir)
    {
        return 
$this->recursive_mkdir($dir);
    }

    
/**
     * Removes a file or a directory (optionally recursively)
     *
     * @param    $path        string (required)    - Filename/Directory path to remove
     * @param    $recursive    bool (optional)        - Recursively delete directories
     * @author jasmineaura
     */
    
function remove($path$recursive false)
    {
        global 
$phpbb_root_path$phpEx$user;

        
// No need to strip phpbb_root_path afterwards, as the transfer class
        // functions - remove_dir() and delete_file() - already do that for us
        
if (strpos($path$phpbb_root_path) !== 0)
        {
            
$path $phpbb_root_path $path;
        }

        if (
is_dir($path))
        {
            
// Recursive delete:
            // remove_dir() in functions_transfer.php still says "todo remove child directories?"
            // It's really easy to recursively delete in ftp, but unfortunately what exposes access to
            // ftp_nlist() (or NLST); "_ls", is private to the transfer class, so we can't use it yet
            
if ($recursive)
            {
                
// Insurance - this should never really happen
                
if ($path == $phpbb_root_path || is_file("$path/common.$phpEx"))
                {
                    return 
false;
                }

                
// Get all of the files in the source directory
                
$files find_files($path'.*');
                
// Get all of the sub-directories in the source directory
                
$subdirs find_files($path'.*'20true);

                
// Delete all the files
                
foreach ($files as $file)
                {
                    if (!
$this->transfer->delete_file($file))
                    {
                        return 
sprintf($user->lang['MODS_RMFILE_FAILURE'], $file);
                    }
                }

                
// Delete all the sub-directories, in _reverse_ order (array_pop)
                
for ($i=0$cnt count($subdirs); $i $cnt$i++)
                {
                    
$subdir array_pop($subdirs);
                    if (!
$this->transfer->remove_dir($subdir))
                    {
                        return 
sprintf($user->lang['MODS_RMDIR_FAILURE'], $subdir);
                    }
                }

                
// Finally, delete the directory itself
                
if (!$this->transfer->remove_dir($path))
                {
                    return 
sprintf($user->lang['MODS_RMDIR_FAILURE'], $path);
                }
            }
            else if (!
$this->transfer->remove_dir($path))
            {
                return 
sprintf($user->lang['MODS_RMDIR_FAILURE'], $path);
            }
        }
        else if (!
$this->transfer->delete_file($path))
        {
            return 
sprintf($user->lang['MODS_RMFILE_FAILURE'], $path);
        }

        return 
true;
    }
}

class 
editor_manual extends editor
{
    function 
editor_manual()
    {
        global 
$config$phpbb_root_path;

        
$this->write_method WRITE_MANUAL;
        
$this->install_time time();

        if (!
class_exists('compress'))
        {
            global 
$phpEx;
            include(
$phpbb_root_path 'includes/functions_compress.' $phpEx);
        }

        
// Ugly regular expression to extract "tar" from "tar.gz" or "tar.bz2"
        // Made ugly because it does nothing with "zip"
        
preg_match('#.(w{3}).?.*#'$config['compress_method'], $match);
        
$class 'compress_' $match[1];

        
$this->compress = new $class('w'$phpbb_root_path 'store/mod_' $this->install_time $config['compress_method'], $config['compress_method']);
    }

    function 
copy_content($from$to '')
    {
        global 
$phpbb_root_path$user;

        if (
strpos($from$phpbb_root_path) !== 0)
        {
            
$from $phpbb_root_path $from;
        }

        if (
strpos($to$phpbb_root_path) !== 0)
        {
            
$to $phpbb_root_path $to;
        }

        
// Note: phpBB's compression class does support adding a whole directory at a time.
        // However, I chose not to use that function because it would not allow AutoMOD's
        // error handling to work the same as for FTP & Direct methods.
        
$files = array();
        if (
is_dir($from))
        {
            
$files find_files($from'.*');
            
$to_is_dir true;
        }
        else if (
is_file($from))
        {
            
$files = array($from);
            
$to_is_dir false;
        }

        if (empty(
$files))
        {
            return 
false;
        }

        foreach (
$files as $file)
        {
            if (
$to_is_dir)
            {
                
$dest str_replace($from$to$file);
            }
            else
            {
                
$dest $to;
            }

            
// Replace root path with the "files/" directory that goes in the zip
            
$dest str_replace($phpbb_root_path'files/'$dest);

            if (!
$this->compress->add_custom_file($file$dest))
            {
                return 
sprintf($user->lang['WRITE_MANUAL_FAIL'], $dest);
            }
        }

        
// return true since we are now taking an action - NULL implies no action
        
return true;
    }

    
/**
    * Write & close file
    */
    
function close_file($new_filename)
    {
        global 
$phpbb_root_path$edited_root$mod_installed$mod_uninstalled$force_install;
        global 
$db$user;

        
$file_contents implode(''$this->file_contents);

        if (
$this->template_id && ($mod_installed || $mod_uninstalled || $force_install))
        {
            
update_database_template($new_filename$this->template_id$file_contents$this->install_time);
        }

        
// don't include extra dirs in zip file
        
$strip_position strpos($new_filename'_edited') + 8// want the end of the string

        
$new_filename 'files/' substr($new_filename$strip_position);

        if (!
$this->compress->add_data($file_contents$new_filename))
        {
            return 
sprintf($user->lang['WRITE_MANUAL_FAIL'], $new_filename);
        }

        return 
true;
    }

    
/**
    * Backup is undefined when creating a compressed file.
    */
    
function backup_file($backup_dir)
    {
        return 
NULL;
    }

    function 
recursive_mkdir($path$mode 0777)
    {
        return 
NULL;
    }

    function 
commit_changes($source$destination)
    {
        global 
$template$user$phpbb_admin_path;

        
$download_url append_sid("{$phpbb_admin_path}index.php"'i=mods&amp;mode=frontend&amp;action=download&amp;time=' $this->install_time);

        
$template->assign_vars(array(
            
'S_MANUAL_INSTRUCTIONS'        => true,
            
'L_AM_MANUAL_INSTRUCTIONS'    => sprintf($user->lang['AM_MANUAL_INSTRUCTIONS'], '<a href="' $download_url '">''</a>'),
        ));

        
meta_refresh(3$download_url);

        
$this->compress->close();
        return 
true;
    }

    function 
commit_changes_final($source$destination)
    {
        return 
NULL;
    }

    function 
create_edited_root($dir)
    {
        return 
NULL;
    }

    function 
remove($file$recursive false)
    {
        return 
NULL;
    }
}

?>
Онлайн: 3
Реклама