/**
  *
  *
  * @param $Path
  * @param $InfoPaths
  * @param bool $TmpPath
  * @param bool $ThrowError
  * @return array|bool
  * @throws Exception
  */
 protected static function _GetInfoZip($Path, $InfoPaths, $TmpPath = false, $ThrowError = true)
 {
     // Extract the zip file so we can make sure it has appropriate information.
     $Zip = null;
     if (class_exists('ZipArchive', false)) {
         $Zip = new ZipArchive();
         $ZipOpened = $Zip->open($Path);
         if ($ZipOpened !== true) {
             $Zip = null;
         }
     }
     if (!$Zip) {
         require_once PATH_LIBRARY . "/vendors/pclzip/class.pclzipadapter.php";
         $Zip = new PclZipAdapter();
         $ZipOpened = $Zip->open($Path);
     }
     if ($ZipOpened !== true) {
         if ($ThrowError) {
             $Errors = array(ZIPARCHIVE::ER_EXISTS => 'ER_EXISTS', ZIPARCHIVE::ER_INCONS => 'ER_INCONS', ZIPARCHIVE::ER_INVAL => 'ER_INVAL', ZIPARCHIVE::ER_MEMORY => 'ER_MEMORY', ZIPARCHIVE::ER_NOENT => 'ER_NOENT', ZIPARCHIVE::ER_NOZIP => 'ER_NOZIP', ZIPARCHIVE::ER_OPEN => 'ER_OPEN', ZIPARCHIVE::ER_READ => 'ER_READ', ZIPARCHIVE::ER_SEEK => 'ER_SEEK');
             throw new Exception(t('Could not open addon file. Addons must be zip files.') . ' (' . $Path . ' ' . GetValue($ZipOpened, $Errors, 'Unknown Error') . ')' . $Worked, 400);
         }
         return false;
     }
     if ($TmpPath === false) {
         $TmpPath = dirname($Path) . '/' . basename($Path, '.zip') . '/';
     }
     if (file_exists($TmpPath)) {
         Gdn_FileSystem::RemoveFolder($TmpPath);
     }
     $Result = array();
     for ($i = 0; $i < $Zip->numFiles; $i++) {
         $Entry = $Zip->statIndex($i);
         if (preg_match('#(\\.\\.[\\/])#', $Entry['name'])) {
             throw new Gdn_UserException("Invalid path in zip file: " . htmlspecialchars($Entry['name']));
         }
         $Name = '/' . ltrim($Entry['name'], '/');
         foreach ($InfoPaths as $InfoPath) {
             $Preg = '`(' . str_replace(array('.', '*'), array('\\.', '.*'), $InfoPath) . ')$`';
             if (preg_match($Preg, $Name, $Matches)) {
                 $Base = trim(substr($Name, 0, -strlen($Matches[1])), '/');
                 if (strpos($Base, '/') !== false) {
                     continue;
                     // file nested too deep.
                 }
                 if (!file_exists($TmpPath)) {
                     mkdir($TmpPath, 0777, true);
                 }
                 $Zip->extractTo($TmpPath, $Entry['name']);
                 $Result[] = array('Name' => $Matches[1], 'Path' => $TmpPath . rtrim($Entry['name'], '/'), 'Base' => $Base);
             }
         }
     }
     return $Result;
 }
Beispiel #2
0
 /**
  * Check an addon's file to extract the addon information out of it.
  *
  * @param string $Path The path to the file.
  * @param bool $Fix Whether or not to fix files that have been zipped incorrectly.
  * @return array An array of addon information.
  */
 public static function AnalyzeAddon($Path, $Fix = FALSE, $ThrowError = TRUE)
 {
     $Result = array();
     // Extract the zip file so we can make sure it has appropriate information.
     $Zip = NULL;
     if (class_exists('ZipArchive', FALSE)) {
         $Zip = new ZipArchive();
         $ZipOpened = $Zip->open($Path);
         if ($ZipOpened !== TRUE) {
             $Zip = NULL;
         }
     }
     if (!$Zip) {
         require_once PATH_LIBRARY . "/vendors/pclzip/class.pclzipadapter.php";
         $Zip = new PclZipAdapter();
         $ZipOpened = $Zip->open($Path);
     }
     if ($ZipOpened !== TRUE) {
         if ($ThrowError) {
             $Errors = array(ZIPARCHIVE::ER_EXISTS => 'ER_EXISTS', ZIPARCHIVE::ER_INCONS => 'ER_INCONS', ZIPARCHIVE::ER_INVAL => 'ER_INVAL', ZIPARCHIVE::ER_MEMORY => 'ER_MEMORY', ZIPARCHIVE::ER_NOENT => 'ER_NOENT', ZIPARCHIVE::ER_NOZIP => 'ER_NOZIP', ZIPARCHIVE::ER_OPEN => 'ER_OPEN', ZIPARCHIVE::ER_READ => 'ER_READ', ZIPARCHIVE::ER_SEEK => 'ER_SEEK');
             throw new Exception(T('Could not open addon file. Addons must be zip files.') . ' (' . $Path . ' ' . GetValue($ZipOpened, $Errors, 'Unknown Error') . ')' . $Worked, 400);
         }
         return FALSE;
     }
     $Entries = array();
     for ($i = 0; $i < $Zip->numFiles; $i++) {
         $Entries[] = $Zip->statIndex($i);
     }
     // Figure out which system files to delete.
     $Deletes = array();
     foreach ($Entries as $Index => $Entry) {
         $Name = $Entry['name'];
         $Delete = strpos($Name, '__MACOSX') !== FALSE | strpos($Name, '.DS_Store') !== FALSE | strpos($Name, 'thumbs.db') !== FALSE | strpos($Name, '.gitignore') !== FALSE;
         if ($Delete) {
             $Deletes[] = $Entry;
             unset($Entries[$Index]);
         }
     }
     // Get a folder ready for checking the addon.
     $FolderPath = dirname($Path) . '/' . basename($Path, '.zip') . '/';
     if (file_exists($FolderPath)) {
         Gdn_FileSystem::RemoveFolder($FolderPath);
     }
     // Figure out what kind of addon this is.
     $Root = '';
     $NewRoot = '';
     $Addon = FALSE;
     foreach ($Entries as $Entry) {
         $Name = '/' . ltrim($Entry['name'], '/');
         $Filename = basename($Name);
         $Folder = substr($Name, 0, -strlen($Filename));
         $NewRoot = '';
         // Check to see if the entry is a plugin file.
         if ($Filename == 'default.php' || StringEndsWith($Filename, '.plugin.php')) {
             if (count(explode('/', $Folder)) > 3) {
                 // The file is too deep to be a plugin file.
                 continue;
             }
             // This could be a plugin file, but we have to examine its info array.
             $Zip->extractTo($FolderPath, $Entry['name']);
             $FilePath = CombinePaths(array($FolderPath, $Name));
             $Info = self::ParseInfoArray($FilePath, 'PluginInfo');
             Gdn_FileSystem::RemoveFolder(dirname($FilePath));
             if (!is_array($Info) || !count($Info)) {
                 continue;
             }
             // Check to see if the info array conforms to a plugin spec.
             $Key = key($Info);
             $Info = $Info[$Key];
             $Root = trim($Folder, '/');
             $Valid = TRUE;
             // Make sure the key matches the folder name.
             if ($Root && strcasecmp($Root, $Key) != 0) {
                 $Result[] = "{$Name}: The plugin's key is not the same as its folder name.";
                 $Valid = FALSE;
             } else {
                 $NewRoot = $Root;
             }
             if (!GetValue('Description', $Info)) {
                 $Result[] = $Name . ': ' . sprintf(T('ValidateRequired'), T('Description'));
                 $Valid = FALSE;
             }
             if (!GetValue('Version', $Info)) {
                 $Result[] = $Name . ': ' . sprintf(T('ValidateRequired'), T('Version'));
                 $Valid = FALSE;
             }
             if ($Valid) {
                 // The plugin was confirmed.
                 $Addon = array('AddonKey' => $Key, 'AddonTypeID' => ADDON_TYPE_PLUGIN, 'Name' => GetValue('Name', $Info) ? $Info['Name'] : $Key, 'Description' => $Info['Description'], 'Version' => $Info['Version'], 'Path' => $Path);
                 break;
             }
             continue;
         }
         // Check to see if the entry is an application file.
         if (StringEndsWith($Name, '/settings/about.php')) {
             if (count(explode('/', $Folder)) > 4) {
                 $Result[] = "{$Name}: The application's info array was not in the correct location.";
                 // The file is too deep to be a plugin file.
                 continue;
             }
             // This could be a plugin file, but we have to examine its info array.
             $Zip->extractTo($FolderPath, $Entry['name']);
             $FilePath = CombinePaths(array($FolderPath, $Name));
             $Info = self::ParseInfoArray($FilePath, 'ApplicationInfo');
             Gdn_FileSystem::RemoveFolder(dirname($FilePath));
             if (!is_array($Info) || !count($Info)) {
                 $Result[] = "{$Name}: The application's info array could not be parsed.";
                 continue;
             }
             $Key = key($Info);
             $Info = $Info[$Key];
             $Root = trim(substr($Name, 0, -strlen('/settings/about.php')), '/');
             $Valid = TRUE;
             // Make sure the key matches the folder name.
             if ($Root && strcasecmp($Root, $Key) != 0) {
                 $Result[] = "{$Name}: The application's key is not the same as its folder name.";
                 $Valid = FALSE;
             } else {
                 $NewRoot = $Root;
             }
             if (!GetValue('Description', $Info)) {
                 $Result[] = $Name . ': ' . sprintf(T('ValidateRequired'), T('Description'));
                 $Valid = FALSE;
             }
             if (!GetValue('Version', $Info)) {
                 $Result[] = $Name . ': ' . sprintf(T('ValidateRequired'), T('Version'));
                 $Valid = FALSE;
             }
             if ($Valid) {
                 // The application was confirmed.
                 $Addon = array('AddonKey' => $Key, 'AddonTypeID' => ADDON_TYPE_APPLICATION, 'Name' => GetValue('Name', $Info) ? $Info['Name'] : $Key, 'Description' => $Info['Description'], 'Version' => $Info['Version'], 'Path' => $Path);
                 break;
             }
             continue;
         }
         // Check to see if the entry is a theme file.
         if (StringEndsWith($Name, '/about.php')) {
             if (count(explode('/', $Folder)) > 3) {
                 // The file is too deep to be a plugin file.
                 continue;
             }
             // This could be a theme file, but we have to examine its info array.
             $Zip->extractTo($FolderPath, $Entry['name']);
             $FilePath = CombinePaths(array($FolderPath, $Name));
             $Info = self::ParseInfoArray($FilePath, 'ThemeInfo');
             Gdn_FileSystem::RemoveFolder(dirname($FilePath));
             if (!is_array($Info) || !count($Info)) {
                 continue;
             }
             $Key = key($Info);
             $Info = $Info[$Key];
             $Valid = TRUE;
             $Root = trim(substr($Name, 0, -strlen('/about.php')), '/');
             // Make sure the theme is at least one folder deep.
             if (strlen($Root) == 0) {
                 $Result[] = $Name . ': The theme must be in a folder.';
                 $Valid = FALSE;
             }
             if (!GetValue('Description', $Info)) {
                 $Result[] = $Name . ': ' . sprintf(T('ValidateRequired'), T('Description'));
                 $Valid = FALSE;
             }
             if (!GetValue('Version', $Info)) {
                 $Result[] = $Name . ': ' . sprintf(T('ValidateRequired'), T('Version'));
                 $Valid = FALSE;
             }
             if ($Valid) {
                 // The application was confirmed.
                 $Addon = array('AddonKey' => $Key, 'AddonTypeID' => ADDON_TYPE_THEME, 'Name' => GetValue('Name', $Info) ? $Info['Name'] : $Key, 'Description' => $Info['Description'], 'Version' => $Info['Version'], 'Path' => $Path);
                 break;
             }
         }
         if (StringEndsWith($Name, '/definitions.php')) {
             if (count(explode('/', $Folder)) > 3) {
                 // The file is too deep to be a plugin file.
                 continue;
             }
             // This could be a locale pack, but we have to examine its info array.
             $Zip->extractTo($FolderPath, $Entry['name']);
             $FilePath = CombinePaths(array($FolderPath, $Name));
             $Info = self::ParseInfoArray($FilePath, 'LocaleInfo');
             Gdn_FileSystem::RemoveFolder(dirname($FilePath));
             if (!is_array($Info) || !count($Info)) {
                 continue;
             }
             $Key = key($Info);
             $Info = $Info[$Key];
             $Valid = TRUE;
             $Root = trim(substr($Name, 0, -strlen('/definitions.php')), '/');
             // Make sure the locale is at least one folder deep.
             if ($Root != $Key) {
                 $Result[] = $Name . ': The locale pack\'s key must be the same as its folder name.';
                 $Valid = FALSE;
             }
             if (!GetValue('Locale', $Info)) {
                 $Result[] = $Name . ': ' . sprintf(T('ValidateRequired'), T('Locale'));
                 $Valud = FALSE;
             } elseif (strcasecmp($Info['Locale'], $Key) == 0) {
                 $Result[] = $Name . ': ' . T('The locale\'s key cannot be the same as the name of the locale.');
                 $Valid = FALSE;
             }
             if (!GetValue('Description', $Info)) {
                 $Result[] = $Name . ': ' . sprintf(T('ValidateRequired'), T('Description'));
                 $Valid = FALSE;
             }
             if (!GetValue('Version', $Info)) {
                 $Result[] = $Name . ': ' . sprintf(T('ValidateRequired'), T('Version'));
                 $Valid = FALSE;
             }
             if ($Valid) {
                 // The locale pack was confirmed.
                 $Addon = array('AddonKey' => $Key, 'AddonTypeID' => ADDON_TYPE_LOCALE, 'Name' => GetValue('Name', $Info) ? $Info['Name'] : $Key, 'Description' => $Info['Description'], 'Version' => $Info['Version'], 'Path' => $Path);
                 break;
             }
         }
         // Check to see if the entry is a core file.
         if (StringEndsWith($Name, '/index.php')) {
             if (count(explode('/', $Folder)) != 3) {
                 // The file is too deep to be the core's index.php
                 continue;
             }
             // This could be a theme file, but we have to examine its info array.
             $Zip->extractTo($FolderPath, $Entry['name']);
             $FilePath = CombinePaths(array($FolderPath, $Name));
             // Get the version number from the core.
             $Version = self::ParseCoreVersion($FilePath);
             if (!$Version) {
                 continue;
             }
             // The application was confirmed.
             $Addon = array('AddonKey' => 'vanilla', 'AddonTypeID' => ADDON_TYPE_CORE, 'Name' => 'Vanilla', 'Description' => 'Vanilla is an open-source, standards-compliant, multi-lingual, fully extensible discussion forum for the web. Anyone who has web-space that meets the requirements can download and use Vanilla for free!', 'Version' => $Version, 'Path' => $Path);
             $Info = array();
             break;
         }
     }
     if ($Addon) {
         // Add the requirements.
         $Requirements = ArrayTranslate($Info, array('RequiredApplications' => 'Applications', 'RequiredPlugins' => 'Plugins', 'RequiredThemes' => 'Themes'));
         foreach ($Requirements as $Type => $Items) {
             if (!is_array($Items)) {
                 unset($Requirements[$Type]);
             }
         }
         $Addon['Requirements'] = serialize($Requirements);
         $Addon['Checked'] = TRUE;
         $UploadsPath = PATH_ROOT . '/uploads/';
         if (StringBeginsWith($Addon['Path'], $UploadsPath)) {
             $Addon['File'] = substr($Addon['Path'], strlen($UploadsPath));
         }
         if ($Fix) {
             // Delete extraneous files.
             foreach ($Deletes as $Delete) {
                 $Zip->deleteName($Delete['name']);
             }
         }
     }
     $Zip->close();
     if (file_exists($FolderPath)) {
         Gdn_FileSystem::RemoveFolder($FolderPath);
     }
     if ($Addon) {
         $Addon['MD5'] = md5_file($Path);
         return $Addon;
     } else {
         if ($ThrowError) {
             $Msg = implode("\n", $Result);
             throw new Exception($Msg, 400);
         } else {
             return FALSE;
         }
     }
 }