1<?php 2/** 3* 4* This file is part of the phpBB Forum Software package. 5* 6* @copyright (c) phpBB Limited <https://www.phpbb.com> 7* @license GNU General Public License, version 2 (GPL-2.0) 8* 9* For full copyright and license information, please see 10* the docs/CREDITS.txt file. 11* 12*/ 13 14namespace phpbb\extension; 15 16/** 17* The extension metadata manager validates and gets meta-data for extensions 18*/ 19class metadata_manager 20{ 21 /** 22 * Name (including vendor) of the extension 23 * @var string 24 */ 25 protected $ext_name; 26 27 /** 28 * Metadata from the composer.json file 29 * @var array 30 */ 31 protected $metadata; 32 33 /** 34 * Link (including root path) to the metadata file 35 * @var string 36 */ 37 protected $metadata_file; 38 39 /** 40 * Creates the metadata manager 41 * 42 * @param string $ext_name Name (including vendor) of the extension 43 * @param string $ext_path Path to the extension directory including root path 44 */ 45 public function __construct($ext_name, $ext_path) 46 { 47 $this->ext_name = $ext_name; 48 $this->metadata = array(); 49 $this->metadata_file = $ext_path . 'composer.json'; 50 } 51 52 /** 53 * Processes and gets the metadata requested 54 * 55 * @param string $element All for all metadata that it has and is valid, otherwise specify which section you want by its shorthand term. 56 * @return array Contains all of the requested metadata, throws an exception on failure 57 */ 58 public function get_metadata($element = 'all') 59 { 60 // Fetch and clean the metadata if not done yet 61 if ($this->metadata === array()) 62 { 63 $this->fetch_metadata_from_file(); 64 } 65 66 switch ($element) 67 { 68 case 'all': 69 default: 70 $this->validate(); 71 return $this->metadata; 72 break; 73 74 case 'version': 75 case 'name': 76 $this->validate($element); 77 return $this->metadata[$element]; 78 break; 79 80 case 'display-name': 81 return (isset($this->metadata['extra']['display-name'])) ? $this->metadata['extra']['display-name'] : $this->get_metadata('name'); 82 break; 83 } 84 } 85 86 /** 87 * Gets the metadata file contents and cleans loaded file 88 * 89 * @throws \phpbb\extension\exception 90 */ 91 private function fetch_metadata_from_file() 92 { 93 if (!file_exists($this->metadata_file)) 94 { 95 throw new \phpbb\extension\exception('FILE_NOT_FOUND', array($this->metadata_file)); 96 } 97 98 if (!($file_contents = file_get_contents($this->metadata_file))) 99 { 100 throw new \phpbb\extension\exception('FILE_CONTENT_ERR', array($this->metadata_file)); 101 } 102 103 if (($metadata = json_decode($file_contents, true)) === null) 104 { 105 throw new \phpbb\extension\exception('FILE_JSON_DECODE_ERR', array($this->metadata_file)); 106 } 107 108 array_walk_recursive($metadata, array($this, 'sanitize_json')); 109 $this->metadata = $metadata; 110 } 111 112 /** 113 * Sanitize input from JSON array using htmlspecialchars() 114 * 115 * @param mixed $value Value of array row 116 * @param string $key Key of array row 117 */ 118 public function sanitize_json(&$value, $key) 119 { 120 $value = htmlspecialchars($value, ENT_COMPAT); 121 } 122 123 /** 124 * Validate fields 125 * 126 * @param string $name ("all" for display and enable validation 127 * "display" for name, type, and authors 128 * "name", "type") 129 * @return Bool True if valid, throws an exception if invalid 130 * @throws \phpbb\extension\exception 131 */ 132 public function validate($name = 'display') 133 { 134 // Basic fields 135 $fields = array( 136 'name' => '#^[a-zA-Z0-9_\x7f-\xff]{2,}/[a-zA-Z0-9_\x7f-\xff]{2,}$#', 137 'type' => '#^phpbb-extension$#', 138 'license' => '#.+#', 139 'version' => '#.+#', 140 ); 141 142 switch ($name) 143 { 144 case 'all': 145 $this->validate_enable(); 146 // no break 147 148 case 'display': 149 foreach ($fields as $field => $data) 150 { 151 $this->validate($field); 152 } 153 154 $this->validate_authors(); 155 break; 156 157 default: 158 if (isset($fields[$name])) 159 { 160 if (!isset($this->metadata[$name])) 161 { 162 throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array($name)); 163 } 164 165 if (!preg_match($fields[$name], $this->metadata[$name])) 166 { 167 throw new \phpbb\extension\exception('META_FIELD_INVALID', array($name)); 168 } 169 } 170 break; 171 } 172 173 return true; 174 } 175 176 /** 177 * Validates the contents of the authors field 178 * 179 * @return boolean True when passes validation, throws exception if invalid 180 * @throws \phpbb\extension\exception 181 */ 182 public function validate_authors() 183 { 184 if (empty($this->metadata['authors'])) 185 { 186 throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('authors')); 187 } 188 189 foreach ($this->metadata['authors'] as $author) 190 { 191 if (!isset($author['name'])) 192 { 193 throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('author name')); 194 } 195 } 196 197 return true; 198 } 199 200 /** 201 * This array handles the verification that this extension can be enabled on this board 202 * 203 * @return bool True if validation succeeded, throws an exception if invalid 204 * @throws \phpbb\extension\exception 205 */ 206 public function validate_enable() 207 { 208 // Check for valid directory & phpBB, PHP versions 209 return $this->validate_dir() && $this->validate_require_phpbb() && $this->validate_require_php(); 210 } 211 212 /** 213 * Validates the most basic directory structure to ensure it follows <vendor>/<ext> convention. 214 * 215 * @return boolean True when passes validation, throws an exception if invalid 216 * @throws \phpbb\extension\exception 217 */ 218 public function validate_dir() 219 { 220 if (substr_count($this->ext_name, '/') !== 1 || $this->ext_name != $this->get_metadata('name')) 221 { 222 throw new \phpbb\extension\exception('EXTENSION_DIR_INVALID'); 223 } 224 225 return true; 226 } 227 228 229 /** 230 * Validates the contents of the phpbb requirement field 231 * 232 * @return boolean True when passes validation, throws an exception if invalid 233 * @throws \phpbb\extension\exception 234 */ 235 public function validate_require_phpbb() 236 { 237 if (!isset($this->metadata['extra']['soft-require']['phpbb/phpbb'])) 238 { 239 throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('soft-require')); 240 } 241 242 return true; 243 } 244 245 /** 246 * Validates the contents of the php requirement field 247 * 248 * @return boolean True when passes validation, throws an exception if invalid 249 * @throws \phpbb\extension\exception 250 */ 251 public function validate_require_php() 252 { 253 if (!isset($this->metadata['require']['php'])) 254 { 255 throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('require php')); 256 } 257 258 return true; 259 } 260} 261