This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /* |
||
4 | * This file is part of EC-CUBE |
||
5 | * |
||
6 | * Copyright(c) 2000-2015 LOCKON CO.,LTD. All Rights Reserved. |
||
7 | * |
||
8 | * http://www.lockon.co.jp/ |
||
9 | * |
||
10 | * This program is free software; you can redistribute it and/or |
||
11 | * modify it under the terms of the GNU General Public License |
||
12 | * as published by the Free Software Foundation; either version 2 |
||
13 | * of the License, or (at your option) any later version. |
||
14 | * |
||
15 | * This program is distributed in the hope that it will be useful, |
||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
18 | * GNU General Public License for more details. |
||
19 | * |
||
20 | * You should have received a copy of the GNU General Public License |
||
21 | * along with this program; if not, write to the Free Software |
||
22 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
||
23 | */ |
||
24 | |||
25 | namespace Eccube\Service; |
||
26 | |||
27 | use Eccube\Common\Constant; |
||
28 | use Eccube\Exception\PluginException; |
||
29 | use Eccube\Util\Cache; |
||
30 | use Eccube\Util\Str; |
||
31 | use Symfony\Component\Filesystem\Filesystem; |
||
32 | use Symfony\Component\Yaml\Yaml; |
||
33 | |||
34 | class PluginService |
||
35 | { |
||
36 | const CONFIG_YML = 'config.yml'; |
||
37 | const EVENT_YML = 'event.yml'; |
||
38 | private $app; |
||
39 | |||
40 | 150 | public function __construct($app) |
|
41 | { |
||
42 | 150 | $this->app = $app; |
|
43 | } |
||
44 | |||
45 | 9 | public function install($path, $source = 0) |
|
46 | { |
||
47 | 9 | $pluginBaseDir = null; |
|
48 | 9 | $tmp = null; |
|
49 | |||
50 | try { |
||
51 | 9 | $this->app->removePluginConfigCache(); |
|
52 | 9 | Cache::clear($this->app, false); |
|
53 | 9 | $tmp = $this->createTempDir(); |
|
54 | |||
55 | 9 | $this->unpackPluginArchive($path, $tmp); //一旦テンポラリに展開 |
|
56 | 9 | $this->checkPluginArchiveContent($tmp); |
|
57 | |||
58 | 7 | $config = $this->readYml($tmp.'/'.self::CONFIG_YML); |
|
59 | 7 | $event = $this->readYml($tmp.'/'.self::EVENT_YML); |
|
60 | 7 | $this->deleteFile($tmp); // テンポラリのファイルを削除 |
|
61 | |||
62 | 7 | $this->checkSamePlugin($config['code']); // 重複していないかチェック |
|
63 | |||
64 | 7 | $pluginBaseDir = $this->calcPluginDir($config['code']); |
|
65 | 7 | $this->createPluginDir($pluginBaseDir); // 本来の置き場所を作成 |
|
66 | |||
67 | 7 | $this->unpackPluginArchive($path, $pluginBaseDir); // 問題なければ本当のplugindirへ |
|
68 | |||
69 | 7 | $this->registerPlugin($config, $event, $source); // dbにプラグイン登録 |
|
70 | 6 | $this->app->writePluginConfigCache(); |
|
71 | 4 | } catch (PluginException $e) { |
|
72 | 4 | $this->deleteDirs(array($tmp, $pluginBaseDir)); |
|
73 | 4 | throw $e; |
|
74 | } catch (\Exception $e) { // インストーラがどんなExceptionを上げるかわからないので |
||
75 | |||
76 | $this->deleteDirs(array($tmp, $pluginBaseDir)); |
||
77 | throw $e; |
||
78 | } |
||
79 | |||
80 | 6 | return true; |
|
81 | } |
||
82 | |||
83 | 9 | public function createTempDir() |
|
84 | { |
||
85 | 9 | @mkdir($this->app['config']['plugin_temp_realdir']); |
|
86 | 9 | $d = ($this->app['config']['plugin_temp_realdir'].'/'.sha1(Str::random(16))); |
|
87 | |||
88 | 9 | if (!mkdir($d, 0777)) { |
|
89 | throw new PluginException($php_errormsg.$d); |
||
90 | } |
||
91 | |||
92 | 9 | return $d; |
|
93 | } |
||
94 | |||
95 | 4 | public function deleteDirs($arr) |
|
96 | { |
||
97 | 4 | foreach ($arr as $dir) { |
|
98 | 4 | if (file_exists($dir)) { |
|
99 | 3 | $fs = new Filesystem(); |
|
100 | 4 | $fs->remove($dir); |
|
101 | } |
||
102 | } |
||
103 | } |
||
104 | |||
105 | 9 | public function unpackPluginArchive($archive, $dir) |
|
106 | { |
||
107 | 9 | $extension = pathinfo($archive, PATHINFO_EXTENSION); |
|
108 | try { |
||
109 | 9 | if ($extension == 'zip') { |
|
110 | $zip = new \ZipArchive(); |
||
111 | $zip->open($archive); |
||
112 | $zip->extractTo($dir); |
||
113 | $zip->close(); |
||
114 | } else { |
||
115 | 9 | $phar = new \PharData($archive); |
|
116 | 9 | $phar->extractTo($dir, null, true); |
|
117 | } |
||
118 | } catch (\Exception $e) { |
||
119 | throw new PluginException('アップロードに失敗しました。圧縮ファイルを確認してください。'); |
||
120 | } |
||
121 | } |
||
122 | |||
123 | 150 | public function checkPluginArchiveContent($dir, array $config_cache = array()) |
|
124 | { |
||
125 | try { |
||
126 | 150 | if (!empty($config_cache)) { |
|
127 | 142 | $meta = $config_cache; |
|
128 | } else { |
||
129 | 150 | $meta = $this->readYml($dir . '/config.yml'); |
|
130 | } |
||
131 | } catch (\Symfony\Component\Yaml\Exception\ParseException $e) { |
||
132 | throw new PluginException($e->getMessage(), $e->getCode(), $e); |
||
133 | } |
||
134 | |||
135 | 150 | if (!is_array($meta)) { |
|
136 | 2 | throw new PluginException('config.yml not found or syntax error'); |
|
137 | } |
||
138 | 148 | View Code Duplication | if (!isset($meta['code']) || !$this->checkSymbolName($meta['code'])) { |
0 ignored issues
–
show
|
|||
139 | throw new PluginException('config.yml code empty or invalid_character(\W)'); |
||
140 | } |
||
141 | 148 | if (!isset($meta['name'])) { |
|
142 | // nameは直接クラス名やPATHに使われるわけではないため文字のチェックはなしし |
||
143 | 1 | throw new PluginException('config.yml name empty'); |
|
144 | } |
||
145 | 147 | View Code Duplication | if (isset($meta['event']) && !$this->checkSymbolName($meta['event'])) { // eventだけは必須ではない |
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository.
Loading history...
|
|||
146 | throw new PluginException('config.yml event empty or invalid_character(\W) '); |
||
147 | } |
||
148 | 147 | if (!isset($meta['version'])) { |
|
149 | // versionは直接クラス名やPATHに使われるわけではないため文字のチェックはなしし |
||
150 | throw new PluginException('config.yml version invalid_character(\W) '); |
||
151 | } |
||
152 | 147 | if (isset($meta['orm.path'])) { |
|
153 | if (!is_array($meta['orm.path'])) { |
||
154 | throw new PluginException('config.yml orm.path invalid_character(\W) '); |
||
155 | } |
||
156 | } |
||
157 | 147 | if (isset($meta['service'])) { |
|
158 | if (!is_array($meta['service'])) { |
||
159 | throw new PluginException('config.yml service invalid_character(\W) '); |
||
160 | } |
||
161 | } |
||
162 | } |
||
163 | |||
164 | 10 | public function readYml($yml) |
|
165 | { |
||
166 | 10 | if (file_exists($yml)) { |
|
167 | 8 | return Yaml::parse(file_get_contents($yml)); |
|
168 | } |
||
169 | |||
170 | 7 | return false; |
|
171 | } |
||
172 | |||
173 | 148 | public function checkSymbolName($string) |
|
174 | { |
||
175 | 148 | return strlen($string) < 256 && preg_match('/^\w+$/', $string); |
|
176 | // plugin_nameやplugin_codeに使える文字のチェック |
||
177 | // a-z A-Z 0-9 _ |
||
178 | // ディレクトリ名などに使われれるので厳しめ |
||
179 | } |
||
180 | |||
181 | 7 | public function deleteFile($path) |
|
182 | { |
||
183 | 7 | $f = new Filesystem(); |
|
184 | 7 | $f->remove($path); |
|
185 | } |
||
186 | |||
187 | 7 | public function checkSamePlugin($code) |
|
188 | { |
||
189 | 7 | $repo = $this->app['eccube.repository.plugin']->findOneBy(array('code' => $code)); |
|
190 | 7 | if ($repo) { |
|
191 | 1 | throw new PluginException('plugin already installed.'); |
|
192 | } |
||
193 | } |
||
194 | |||
195 | 7 | public function calcPluginDir($name) |
|
196 | { |
||
197 | 7 | return $this->app['config']['plugin_realdir'].'/'.$name; |
|
198 | } |
||
199 | |||
200 | 7 | public function createPluginDir($d) |
|
201 | { |
||
202 | 7 | $b = @mkdir($d); |
|
203 | 7 | if (!$b) { |
|
204 | throw new PluginException($php_errormsg); |
||
205 | } |
||
206 | } |
||
207 | |||
208 | 7 | public function registerPlugin($meta, $event_yml, $source = 0) |
|
209 | { |
||
210 | 7 | $em = $this->app['orm.em']; |
|
211 | 7 | $em->getConnection()->beginTransaction(); |
|
212 | try { |
||
213 | 7 | $p = new \Eccube\Entity\Plugin(); |
|
214 | // インストール直後はプラグインは有効にしない |
||
215 | 7 | $p->setName($meta['name']) |
|
216 | 7 | ->setEnable(Constant::DISABLED) |
|
217 | 7 | ->setClassName(isset($meta['event']) ? $meta['event'] : '') |
|
218 | 7 | ->setVersion($meta['version']) |
|
219 | 7 | ->setDelflg(Constant::DISABLED) |
|
220 | 7 | ->setSource($source) |
|
221 | 7 | ->setCode($meta['code']); |
|
222 | |||
223 | 7 | $em->persist($p); |
|
224 | 7 | $em->flush(); |
|
225 | |||
226 | 7 | if (is_array($event_yml)) { |
|
227 | 2 | foreach ($event_yml as $event => $handlers) { |
|
228 | 2 | foreach ($handlers as $handler) { |
|
229 | 2 | if (!$this->checkSymbolName($handler[0])) { |
|
230 | throw new PluginException('Handler name format error'); |
||
231 | } |
||
232 | 2 | $peh = new \Eccube\Entity\PluginEventHandler(); |
|
233 | 2 | $peh->setPlugin($p) |
|
234 | 2 | ->setEvent($event) |
|
235 | 2 | ->setdelFlg(Constant::DISABLED) |
|
236 | 2 | ->setHandler($handler[0]) |
|
237 | 2 | ->setHandlerType($handler[1]) |
|
238 | 2 | ->setPriority($this->app['eccube.repository.plugin_event_handler']->calcNewPriority($event, $handler[1])); |
|
239 | 2 | $em->persist($peh); |
|
240 | 2 | $em->flush(); |
|
241 | } |
||
242 | } |
||
243 | } |
||
244 | |||
245 | 7 | $em->persist($p); |
|
246 | |||
247 | 7 | $this->callPluginManagerMethod($meta, 'install'); |
|
248 | |||
249 | 6 | $em->flush(); |
|
250 | 6 | $em->getConnection()->commit(); |
|
251 | 1 | } catch (\Exception $e) { |
|
252 | 1 | $em->getConnection()->rollback(); |
|
253 | 1 | throw new PluginException($e->getMessage()); |
|
254 | } |
||
255 | |||
256 | 6 | return $p; |
|
257 | } |
||
258 | |||
259 | 7 | public function callPluginManagerMethod($meta, $method) |
|
260 | { |
||
261 | 7 | $class = '\\Plugin'.'\\'.$meta['code'].'\\'.'PluginManager'; |
|
262 | 7 | if (class_exists($class)) { |
|
263 | 3 | $installer = new $class(); // マネージャクラスに所定のメソッドがある場合だけ実行する |
|
264 | 3 | if (method_exists($installer, $method)) { |
|
265 | 3 | $installer->$method($meta, $this->app); |
|
266 | } |
||
267 | } |
||
268 | } |
||
269 | |||
270 | 5 | public function uninstall(\Eccube\Entity\Plugin $plugin) |
|
271 | { |
||
272 | 5 | $pluginDir = $this->calcPluginDir($plugin->getCode()); |
|
273 | 5 | $this->app->removePluginConfigCache(); |
|
274 | 5 | Cache::clear($this->app, false); |
|
275 | 5 | $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), 'disable'); |
|
276 | 5 | $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), 'uninstall'); |
|
277 | 5 | $this->unregisterPlugin($plugin); |
|
278 | 5 | $this->deleteFile($pluginDir); |
|
279 | 5 | $this->app->writePluginConfigCache(); |
|
280 | 5 | return true; |
|
281 | } |
||
282 | |||
283 | 5 | public function unregisterPlugin(\Eccube\Entity\Plugin $p) |
|
284 | { |
||
285 | try { |
||
286 | 5 | $em = $this->app['orm.em']; |
|
287 | 5 | $em->getConnection()->beginTransaction(); |
|
288 | |||
289 | 5 | $p->setDelFlg(Constant::ENABLED)->setEnable(Constant::DISABLED); |
|
290 | |||
291 | 5 | foreach ($p->getPluginEventHandlers()->toArray() as $peh) { |
|
292 | 5 | $peh->setDelFlg(Constant::ENABLED); |
|
293 | } |
||
294 | |||
295 | 5 | $em->persist($p); |
|
296 | 5 | $em->flush(); |
|
297 | 5 | $em->getConnection()->commit(); |
|
298 | } catch (\Exception $e) { |
||
299 | $em->getConnection()->rollback(); |
||
300 | throw $e; |
||
301 | } |
||
302 | } |
||
303 | |||
304 | 2 | public function disable(\Eccube\Entity\Plugin $plugin) |
|
305 | { |
||
306 | 2 | return $this->enable($plugin, false); |
|
307 | } |
||
308 | |||
309 | 3 | public function enable(\Eccube\Entity\Plugin $plugin, $enable = true) |
|
310 | { |
||
311 | 3 | $em = $this->app['orm.em']; |
|
312 | try { |
||
313 | 3 | $this->app->removePluginConfigCache(); |
|
314 | 3 | Cache::clear($this->app, false); |
|
315 | 3 | $pluginDir = $this->calcPluginDir($plugin->getCode()); |
|
316 | 3 | $em->getConnection()->beginTransaction(); |
|
317 | 3 | $plugin->setEnable($enable ? Constant::ENABLED : Constant::DISABLED); |
|
318 | 3 | $em->persist($plugin); |
|
319 | 3 | $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), $enable ? 'enable' : 'disable'); |
|
320 | 2 | $em->flush(); |
|
321 | 2 | $em->getConnection()->commit(); |
|
322 | 2 | $this->app->writePluginConfigCache(); |
|
323 | 1 | } catch (\Exception $e) { |
|
324 | 1 | $em->getConnection()->rollback(); |
|
325 | 1 | throw $e; |
|
326 | } |
||
327 | |||
328 | 2 | return true; |
|
329 | } |
||
330 | |||
331 | 1 | public function update(\Eccube\Entity\Plugin $plugin, $path) |
|
332 | { |
||
333 | 1 | $pluginBaseDir = null; |
|
334 | 1 | $tmp = null; |
|
335 | try { |
||
336 | 1 | $this->app->removePluginConfigCache(); |
|
337 | 1 | Cache::clear($this->app, false); |
|
338 | 1 | $tmp = $this->createTempDir(); |
|
339 | |||
340 | 1 | $this->unpackPluginArchive($path, $tmp); //一旦テンポラリに展開 |
|
341 | 1 | $this->checkPluginArchiveContent($tmp); |
|
342 | |||
343 | 1 | $config = $this->readYml($tmp.'/'.self::CONFIG_YML); |
|
344 | 1 | $event = $this->readYml($tmp.'/event.yml'); |
|
345 | |||
346 | 1 | if ($plugin->getCode() != $config['code']) { |
|
347 | throw new PluginException('new/old plugin code is different.'); |
||
348 | } |
||
349 | |||
350 | 1 | $pluginBaseDir = $this->calcPluginDir($config['code']); |
|
351 | 1 | $this->deleteFile($tmp); // テンポラリのファイルを削除 |
|
352 | |||
353 | 1 | $this->unpackPluginArchive($path, $pluginBaseDir); // 問題なければ本当のplugindirへ |
|
354 | |||
355 | 1 | $this->updatePlugin($plugin, $config, $event); // dbにプラグイン登録 |
|
356 | 1 | $this->app->writePluginConfigCache(); |
|
357 | } catch (PluginException $e) { |
||
358 | foreach (array($tmp) as $dir) { |
||
359 | if (file_exists($dir)) { |
||
360 | $fs = new Filesystem(); |
||
361 | $fs->remove($dir); |
||
362 | } |
||
363 | } |
||
364 | throw $e; |
||
365 | } |
||
366 | |||
367 | 1 | return true; |
|
368 | } |
||
369 | |||
370 | 1 | public function updatePlugin(\Eccube\Entity\Plugin $plugin, $meta, $event_yml) |
|
371 | { |
||
372 | try { |
||
373 | 1 | $em = $this->app['orm.em']; |
|
374 | 1 | $em->getConnection()->beginTransaction(); |
|
375 | 1 | $plugin->setVersion($meta['version']) |
|
376 | 1 | ->setName($meta['name']); |
|
377 | |||
378 | 1 | if (isset($meta['event'])) { |
|
379 | 1 | $plugin->setClassName($meta['event']); |
|
380 | } |
||
381 | |||
382 | 1 | $rep = $this->app['eccube.repository.plugin_event_handler']; |
|
383 | |||
384 | 1 | if (is_array($event_yml)) { |
|
385 | 1 | foreach ($event_yml as $event => $handlers) { |
|
386 | 1 | foreach ($handlers as $handler) { |
|
387 | 1 | if (!$this->checkSymbolName($handler[0])) { |
|
388 | throw new PluginException('Handler name format error'); |
||
389 | } |
||
390 | // updateで追加されたハンドラかどうか調べる |
||
391 | 1 | $peh = $rep->findBy(array('del_flg' => Constant::DISABLED, |
|
392 | 1 | 'plugin_id' => $plugin->getId(), |
|
393 | 1 | 'event' => $event, |
|
394 | 1 | 'handler' => $handler[0], |
|
395 | 1 | 'handler_type' => $handler[1],)); |
|
396 | |||
397 | 1 | if (!$peh) { // 新規にevent.ymlに定義されたハンドラなのでinsertする |
|
398 | 1 | $peh = new \Eccube\Entity\PluginEventHandler(); |
|
399 | 1 | $peh->setPlugin($plugin) |
|
400 | 1 | ->setEvent($event) |
|
401 | 1 | ->setdelFlg(Constant::DISABLED) |
|
402 | 1 | ->setHandler($handler[0]) |
|
403 | 1 | ->setHandlerType($handler[1]) |
|
404 | 1 | ->setPriority($rep->calcNewPriority($event, $handler[1])); |
|
405 | 1 | $em->persist($peh); |
|
406 | 1 | $em->flush(); |
|
407 | } |
||
408 | } |
||
409 | } |
||
410 | |||
411 | # アップデート後のevent.ymlで削除されたハンドラをdtb_plugin_event_handlerから探して削除 |
||
412 | 1 | foreach ($rep->findBy(array('del_flg' => Constant::DISABLED, 'plugin_id' => $plugin->getId())) as $peh) { |
|
413 | 1 | if (!isset($event_yml[$peh->getEvent()])) { |
|
414 | $em->remove($peh); |
||
415 | $em->flush(); |
||
416 | } else { |
||
417 | 1 | $match = false; |
|
418 | 1 | foreach ($event_yml[$peh->getEvent()] as $handler) { |
|
419 | 1 | if ($peh->getHandler() == $handler[0] && $peh->getHandlerType() == $handler[1]) { |
|
420 | 1 | $match = true; |
|
421 | } |
||
422 | } |
||
423 | 1 | if (!$match) { |
|
424 | 1 | $em->remove($peh); |
|
425 | 1 | $em->flush(); |
|
426 | } |
||
427 | } |
||
428 | } |
||
429 | } |
||
430 | |||
431 | 1 | $em->persist($plugin); |
|
432 | 1 | $this->callPluginManagerMethod($meta, 'update'); |
|
433 | 1 | $em->flush(); |
|
434 | 1 | $em->getConnection()->commit(); |
|
435 | } catch (\Exception $e) { |
||
436 | $em->getConnection()->rollback(); |
||
437 | throw $e; |
||
438 | } |
||
439 | } |
||
440 | } |
||
441 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.