Lib_Mage - Version 1.6.0.0

Version Notes

1.6.0.0

Download this release

Release Info

Developer Magento Core Team
Extension Lib_Mage
Version 1.6.0.0
Comparing to
See all releases


Version 1.6.0.0

Files changed (64) hide show
  1. lib/Mage/Archive.php +222 -0
  2. lib/Mage/Archive/Abstract.php +84 -0
  3. lib/Mage/Archive/Bz.php +79 -0
  4. lib/Mage/Archive/Gz.php +77 -0
  5. lib/Mage/Archive/Interface.php +53 -0
  6. lib/Mage/Archive/Tar.php +372 -0
  7. lib/Mage/Autoload/Simple.php +52 -0
  8. lib/Mage/Connect/Channel/Generator.php +63 -0
  9. lib/Mage/Connect/Channel/Parser.php +25 -0
  10. lib/Mage/Connect/Channel/VO.php +113 -0
  11. lib/Mage/Connect/Command.php +390 -0
  12. lib/Mage/Connect/Command/Channels.php +189 -0
  13. lib/Mage/Connect/Command/Channels_Header.php +105 -0
  14. lib/Mage/Connect/Command/Config.php +211 -0
  15. lib/Mage/Connect/Command/Config_Header.php +100 -0
  16. lib/Mage/Connect/Command/Install.php +448 -0
  17. lib/Mage/Connect/Command/Install_Header.php +237 -0
  18. lib/Mage/Connect/Command/Package.php +134 -0
  19. lib/Mage/Connect/Command/Package_Header.php +67 -0
  20. lib/Mage/Connect/Command/Registry.php +175 -0
  21. lib/Mage/Connect/Command/Registry_Header.php +68 -0
  22. lib/Mage/Connect/Command/Remote.php +226 -0
  23. lib/Mage/Connect/Command/Remote_Header.php +88 -0
  24. lib/Mage/Connect/Config.php +265 -0
  25. lib/Mage/Connect/Converter.php +337 -0
  26. lib/Mage/Connect/Frontend.php +251 -0
  27. lib/Mage/Connect/Frontend/CLI.php +441 -0
  28. lib/Mage/Connect/Ftp.php +494 -0
  29. lib/Mage/Connect/Loader.php +51 -0
  30. lib/Mage/Connect/Loader/Ftp.php +121 -0
  31. lib/Mage/Connect/Package.php +1486 -0
  32. lib/Mage/Connect/Package/Extension.php +1 -0
  33. lib/Mage/Connect/Package/Hotfix.php +137 -0
  34. lib/Mage/Connect/Package/Maintainer.php +0 -0
  35. lib/Mage/Connect/Package/Reader.php +150 -0
  36. lib/Mage/Connect/Package/Target.php +126 -0
  37. lib/Mage/Connect/Package/VO.php +96 -0
  38. lib/Mage/Connect/Package/Writer.php +209 -0
  39. lib/Mage/Connect/Packager.php +662 -0
  40. lib/Mage/Connect/Repository.php +0 -0
  41. lib/Mage/Connect/Repository/Abstract.php +0 -0
  42. lib/Mage/Connect/Repository/Channel.php +0 -0
  43. lib/Mage/Connect/Repository/Channel/Abstract.php +0 -0
  44. lib/Mage/Connect/Repository/Channel/Commercial.php +0 -0
  45. lib/Mage/Connect/Repository/Channel/Community.php +0 -0
  46. lib/Mage/Connect/Repository/Channel/Core.php +0 -0
  47. lib/Mage/Connect/Repository/Local.php +0 -0
  48. lib/Mage/Connect/Rest.php +364 -0
  49. lib/Mage/Connect/Singleconfig.php +865 -0
  50. lib/Mage/Connect/Structures/Graph.php +248 -0
  51. lib/Mage/Connect/Structures/Node.php +257 -0
  52. lib/Mage/Connect/Validator.php +417 -0
  53. lib/Mage/DB/Exception.php +36 -0
  54. lib/Mage/DB/Mysqli.php +532 -0
  55. lib/Mage/Exception.php +35 -0
  56. lib/Mage/HTTP/Client.php +84 -0
  57. lib/Mage/HTTP/Client/Curl.php +499 -0
  58. lib/Mage/HTTP/Client/Socket.php +537 -0
  59. lib/Mage/HTTP/IClient.php +145 -0
  60. lib/Mage/System/Args.php +102 -0
  61. lib/Mage/System/Dirs.php +82 -0
  62. lib/Mage/Xml/Generator.php +90 -0
  63. lib/Mage/Xml/Parser.php +91 -0
  64. package.xml +18 -0
lib/Mage/Archive.php ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Archive
35
+ {
36
+
37
+ /**
38
+ * Archiver is used for compress.
39
+ */
40
+ const DEFAULT_ARCHIVER = 'gz';
41
+
42
+ /**
43
+ * Default packer for directory.
44
+ */
45
+ const TAPE_ARCHIVER = 'tar';
46
+
47
+ /**
48
+ * Current archiver is used for compress.
49
+ *
50
+ * @var Mage_Archiver_Tar|Mage_Archiver_Gz|Mage_Archiver_Bz
51
+ */
52
+ protected $_archiver=null;
53
+
54
+ /**
55
+ * Accessible formats for compress.
56
+ *
57
+ * @var array
58
+ */
59
+ protected $_formats = array(
60
+ 'tar' => 'tar',
61
+ 'gz' => 'gz',
62
+ 'gzip' => 'gz',
63
+ 'tgz' => 'tar.gz',
64
+ 'tgzip' => 'tar.gz',
65
+ 'bz' => 'bz',
66
+ 'bzip' => 'bz',
67
+ 'bzip2' => 'bz',
68
+ 'bz2' => 'bz',
69
+ 'tbz' => 'tar.bz',
70
+ 'tbzip' => 'tar.bz',
71
+ 'tbz2' => 'tar.bz',
72
+ 'tbzip2' => 'tar.bz');
73
+
74
+ /**
75
+ * Create object of current archiver by $extension.
76
+ *
77
+ * @param string $extension
78
+ * @return Mage_Archiver_Tar|Mage_Archiver_Gz|Mage_Archiver_Bz
79
+ */
80
+ protected function _getArchiver($extension)
81
+ {
82
+ if(array_key_exists(strtolower($extension), $this->_formats)) {
83
+ $format = $this->_formats[$extension];
84
+ } else {
85
+ $format = self::DEFAULT_ARCHIVER;
86
+ }
87
+ $class = 'Mage_Archive_'.ucfirst($format);
88
+ $this->_archiver = new $class();
89
+ return $this->_archiver;
90
+ }
91
+
92
+ /**
93
+ * Split current format to list of archivers.
94
+ *
95
+ * @param string $source
96
+ * @return array
97
+ */
98
+ protected function _getArchivers($source)
99
+ {
100
+ $ext = pathinfo($source, PATHINFO_EXTENSION);
101
+ if(!isset($this->_formats[$ext])) {
102
+ return array();
103
+ }
104
+ $format = $this->_formats[$ext];
105
+ if ($format) {
106
+ $archivers = explode('.', $format);
107
+ return $archivers;
108
+ }
109
+ return array();
110
+ }
111
+
112
+ /**
113
+ * Pack file or directory to archivers are parsed from extension.
114
+ *
115
+ * @param string $source
116
+ * @param string $destination
117
+ * @param boolean $skipRoot skip first level parent
118
+ * @return string Path to file
119
+ */
120
+ public function pack($source, $destination='packed.tgz', $skipRoot=false)
121
+ {
122
+ $archivers = $this->_getArchivers($destination);
123
+ $interimSource = '';
124
+ for ($i=0; $i<count($archivers); $i++ ) {
125
+ if ($i == (count($archivers) - 1)) {
126
+ $packed = $destination;
127
+ } else {
128
+ $packed = dirname($destination) . DS . '~tmp-'. microtime(true) . $archivers[$i] . '.' . $archivers[$i];
129
+ }
130
+ $source = $this->_getArchiver($archivers[$i])->pack($source, $packed, $skipRoot);
131
+ if ($interimSource && $i < count($archivers)) {
132
+ unlink($interimSource);
133
+ }
134
+ $interimSource = $source;
135
+ }
136
+ return $source;
137
+ }
138
+
139
+ /**
140
+ * Unpack file from archivers are parsed from extension.
141
+ * If $tillTar == true unpack file from archivers till
142
+ * meet TAR archiver.
143
+ *
144
+ * @param string $source
145
+ * @param string $destination
146
+ * @param boolean $tillTar
147
+ * @return string Path to file
148
+ */
149
+ public function unpack($source, $destination='.', $tillTar=false, $clearInterm = true)
150
+ {
151
+ $archivers = $this->_getArchivers($source);
152
+ $interimSource = '';
153
+ for ($i=count($archivers)-1; $i>=0; $i--) {
154
+ if ($tillTar && $archivers[$i] == self::TAPE_ARCHIVER) {
155
+ break;
156
+ }
157
+ if ($i == 0) {
158
+ $packed = rtrim($destination, DS) . DS;
159
+ } else {
160
+ $packed = rtrim($destination, DS) . DS . '~tmp-'. microtime(true) . $archivers[$i-1] . '.' . $archivers[$i-1];
161
+ }
162
+ $source = $this->_getArchiver($archivers[$i])->unpack($source, $packed);
163
+
164
+ //var_dump($packed, $source);
165
+
166
+ if ($clearInterm && $interimSource && $i >= 0) {
167
+ unlink($interimSource);
168
+ }
169
+ $interimSource = $source;
170
+ }
171
+ return $source;
172
+ }
173
+
174
+ /**
175
+ * Extract one file from TAR (Tape Archiver).
176
+ *
177
+ * @param string $file
178
+ * @param string $source
179
+ * @param string $destination
180
+ * @return string Path to file
181
+ */
182
+ public function extract($file, $source, $destination='.')
183
+ {
184
+ $tarFile = $this->unpack($source, $destination, true);
185
+ $resFile = $this->_getArchiver(self::TAPE_ARCHIVER)->extract($file, $tarFile, $destination);
186
+ if (!$this->isTar($source)) {
187
+ unlink($tarFile);
188
+ }
189
+ return $resFile;
190
+ }
191
+
192
+ /**
193
+ * Check file is archive.
194
+ *
195
+ * @param string $file
196
+ * @return boolean
197
+ */
198
+ public function isArchive($file)
199
+ {
200
+ $archivers = $this->_getArchivers($file);
201
+ if (count($archivers)) {
202
+ return true;
203
+ }
204
+ return false;
205
+ }
206
+
207
+ /**
208
+ * Check file is TAR.
209
+ *
210
+ * @param mixed $file
211
+ * @return boolean
212
+ */
213
+ public function isTar($file)
214
+ {
215
+ $archivers = $this->_getArchivers($file);
216
+ if (count($archivers)==1 && $archivers[0] == self::TAPE_ARCHIVER) {
217
+ return true;
218
+ }
219
+ return false;
220
+ }
221
+
222
+ }
lib/Mage/Archive/Abstract.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Archive_Abstract
35
+ {
36
+ /**
37
+ * Write data to file. If file can't be opened,
38
+ *
39
+ * @param string $destination
40
+ * @param string $data
41
+ * @return boolean
42
+ */
43
+ protected function _writeFile($destination, $data)
44
+ {
45
+ if(false === file_put_contents($destination, $data)) {
46
+ throw new Mage_Exception("Can't write to file: " . $destination);
47
+ }
48
+ return true;
49
+ }
50
+
51
+ /**
52
+ * Read data from file. If file can't be opened, throw to exception.
53
+ *
54
+ * @param string $source
55
+ * @return string
56
+ */
57
+ protected function _readFile($source)
58
+ {
59
+ $data = '';
60
+ if (is_file($source) && is_readable($source)) {
61
+ $data = @file_get_contents($source);
62
+ if ($data === false) {
63
+ throw new Mage_Exception("Can't get contents from: " . $source);
64
+ }
65
+ }
66
+ return $data;
67
+ }
68
+
69
+ /**
70
+ * Get file name from source (URI) without last extension.
71
+ *
72
+ * @param string $source
73
+ * @return string
74
+ */
75
+ public function getFilename($source, $withExtension=false)
76
+ {
77
+ $file = str_replace(dirname($source) . DS, '', $source);
78
+ if (!$withExtension) {
79
+ $file = substr($file, 0, strrpos($file, '.'));
80
+ }
81
+ return $file;
82
+ }
83
+
84
+ }
lib/Mage/Archive/Bz.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with bzip2 archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Archive_Bz extends Mage_Archive_Abstract implements Mage_Archive_Interface
35
+ {
36
+
37
+ /**
38
+ * Pack file by BZIP2 compressor.
39
+ *
40
+ * @param string $source
41
+ * @param string $destination
42
+ * @return string
43
+ */
44
+ public function pack($source, $destination)
45
+ {
46
+ $data = $this->_readFile($source);
47
+ $bzData = bzcompress($data, 9);
48
+ $this->_writeFile($destination, $bzData);
49
+ return $destination;
50
+ }
51
+
52
+ /**
53
+ * Unpack file by BZIP2 compressor.
54
+ *
55
+ * @param string $source
56
+ * @param string $destination
57
+ * @return string
58
+ */
59
+ public function unpack($source, $destination)
60
+ {
61
+ $data = '';
62
+ $bzPointer = bzopen($source, 'r' );
63
+ if (empty($bzPointer)) {
64
+ throw new Exception('Can\'t open BZ archive : ' . $source);
65
+ }
66
+ while (!feof($bzPointer)) {
67
+ $data .= bzread($bzPointer, 131072);
68
+ }
69
+ bzclose($bzPointer);
70
+ if (is_dir($destination)) {
71
+ $file = $this->getFilename($source);
72
+ $destination = $destination . $file;
73
+ }
74
+ echo $destination;
75
+ $this->_writeFile($destination, $data);
76
+ return $destination;
77
+ }
78
+
79
+ }
lib/Mage/Archive/Gz.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with gz archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Archive_Gz extends Mage_Archive_Abstract implements Mage_Archive_Interface
35
+ {
36
+ /**
37
+ * Pack file by GZ compressor.
38
+ *
39
+ * @param string $source
40
+ * @param string $destination
41
+ * @return string
42
+ */
43
+ public function pack($source, $destination)
44
+ {
45
+ $data = $this->_readFile($source);
46
+ $gzData = gzencode($data, 9);
47
+ $this->_writeFile($destination, $gzData);
48
+ return $destination;
49
+ }
50
+
51
+ /**
52
+ * Unpack file by GZ compressor.
53
+ *
54
+ * @param string $source
55
+ * @param string $destination
56
+ * @return string
57
+ */
58
+ public function unpack($source, $destination)
59
+ {
60
+ $gzPointer = gzopen($source, 'r' );
61
+ if (empty($gzPointer)) {
62
+ throw new Mage_Exception('Can\'t open GZ archive : ' . $source);
63
+ }
64
+ $data = '';
65
+ while (!gzeof($gzPointer)) {
66
+ $data .= gzread($gzPointer, 131072);
67
+ }
68
+ gzclose($gzPointer);
69
+ if (is_dir($destination)) {
70
+ $file = $this->getFilename($source);
71
+ $destination = $destination . $file;
72
+ }
73
+ $this->_writeFile($destination, $data);
74
+ return $destination;
75
+ }
76
+
77
+ }
lib/Mage/Archive/Interface.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Interface for work with archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ interface Mage_Archive_Interface
35
+ {
36
+ /**
37
+ * Pack file or directory.
38
+ *
39
+ * @param string $source
40
+ * @param string $destination
41
+ * @return string
42
+ */
43
+ public function pack($source, $destination);
44
+
45
+ /**
46
+ * Unpack file or directory.
47
+ *
48
+ * @param string $source
49
+ * @param string $destination
50
+ * @return string
51
+ */
52
+ public function unpack($source, $destination);
53
+ }
lib/Mage/Archive/Tar.php ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Archive
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with tar archives
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Archive
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Archive_Tar extends Mage_Archive_Abstract implements Mage_Archive_Interface
35
+ {
36
+ /**
37
+ * Constant is used for parse tar's header.
38
+ */
39
+ const FORMAT_PARSE_HEADER = 'a100name/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1type/a100symlink/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor/a155prefix/a12closer';
40
+
41
+ /**
42
+ * Keep file or directory for packing.
43
+ *
44
+ * @var string
45
+ */
46
+ protected $_currentFile;
47
+
48
+ /**
49
+ * Keep path to file or directory for packing.
50
+ *
51
+ * @var mixed
52
+ */
53
+ protected $_currentPath;
54
+
55
+ /**
56
+ * Skip first level parent directory. Example:
57
+ * use test/fip.php instead test/test/fip.php;
58
+ *
59
+ * @var mixed
60
+ */
61
+ protected $_skipRoot;
62
+
63
+ /**
64
+ * Set option that define ability skip first catalog level.
65
+ *
66
+ * @param mixed $skipRoot
67
+ * @return Mage_Archive_Tar
68
+ */
69
+ protected function _setSkipRoot($skipRoot)
70
+ {
71
+ $this->_skipRoot = $skipRoot;
72
+ return $this;
73
+ }
74
+
75
+ /**
76
+ * Set file which is packing.
77
+ *
78
+ * @param string $file
79
+ * @return Mage_Archive_Tar
80
+ */
81
+ protected function _setCurrentFile($file)
82
+ {
83
+ $this->_currentFile = $file .((is_dir($file) && substr($file, -1)!=DS)?DS:'');
84
+ return $this;
85
+ }
86
+
87
+ /**
88
+ * Retrieve file which is packing.
89
+ *
90
+ * @return string
91
+ */
92
+ protected function _getCurrentFile()
93
+ {
94
+ return $this->_currentFile;
95
+ }
96
+
97
+ /**
98
+ * Set path to file which is packing.
99
+ *
100
+ * @param string $path
101
+ * @return Mage_Archive_Tar
102
+ */
103
+ protected function _setCurrentPath($path)
104
+ {
105
+ if ($this->_skipRoot && is_dir($path)) {
106
+ $this->_currentPath = $path.(substr($path, -1)!=DS?DS:'');
107
+ } else {
108
+ $this->_currentPath = dirname($path) . DS;
109
+ }
110
+ return $this;
111
+ }
112
+
113
+ /**
114
+ * Retrieve path to file which is packing.
115
+ *
116
+ * @return string
117
+ */
118
+ protected function _getCurrentPath()
119
+ {
120
+ return $this->_currentPath;
121
+ }
122
+
123
+ /**
124
+ * Walk through directory and add to tar file or directory.
125
+ * Result is packed string on TAR format.
126
+ *
127
+ * @param boolean $skipRoot
128
+ * @return string
129
+ */
130
+ protected function _packToTar($skipRoot=false)
131
+ {
132
+ $file = $this->_getCurrentFile();
133
+ $header = '';
134
+ $data = '';
135
+ if (!$skipRoot) {
136
+ $header = $this->_composeHeader();
137
+ $data = $this->_readFile($file);
138
+ $data = str_pad($data, floor(((is_dir($file) ? 0 : filesize($file)) + 512 - 1) / 512) * 512, "\0");
139
+ }
140
+ $sub = '';
141
+ if (is_dir($file)) {
142
+ $treeDir = scandir($file);
143
+ if (empty($treeDir)) {
144
+ throw new Mage_Exception('Can\'t scan dir: ' . $file);
145
+ }
146
+ array_shift($treeDir); /* remove './'*/
147
+ array_shift($treeDir); /* remove '../'*/
148
+ foreach ($treeDir as $item) {
149
+ $sub .= $this->_setCurrentFile($file.$item)->_packToTar(false);
150
+ }
151
+ }
152
+ $tarData = $header . $data . $sub;
153
+ $tarData = str_pad($tarData, floor((strlen($tarData) - 1) / 1536) * 1536, "\0");
154
+ return $tarData;
155
+ }
156
+
157
+ /**
158
+ * Compose header for current file in TAR format.
159
+ * If length of file's name greater 100 characters,
160
+ * method breaks header to two pieces. First conatins
161
+ * header and data with long name. Second contain only header.
162
+ *
163
+ * @param boolean $long
164
+ * @return string
165
+ */
166
+ protected function _composeHeader($long = false)
167
+ {
168
+ $file = $this->_getCurrentFile();
169
+ $path = $this->_getCurrentPath();
170
+ $infoFile = stat($file);
171
+ $nameFile = str_replace($path, '', $file);
172
+ $nameFile = str_replace('\\', '/', $nameFile);
173
+ $packedHeader = '';
174
+ $longHeader = '';
175
+ if (!$long && strlen($nameFile)>100) {
176
+ $longHeader = $this->_composeHeader(true);
177
+ $longHeader .= str_pad($nameFile, floor((strlen($nameFile) + 512 - 1) / 512) * 512, "\0");
178
+ }
179
+ $header = array();
180
+ $header['100-name'] = $long?'././@LongLink':substr($nameFile, 0, 100);
181
+ $header['8-mode'] = $long?' ':str_pad(substr(sprintf("%07o", $infoFile['mode']),-4), 6, '0', STR_PAD_LEFT);
182
+ $header['8-uid'] = $long || $infoFile['uid']==0?"\0\0\0\0\0\0\0":sprintf("%07o", $infoFile['uid']);
183
+ $header['8-gid'] = $long || $infoFile['gid']==0?"\0\0\0\0\0\0\0":sprintf("%07o", $infoFile['gid']);
184
+ $header['12-size'] = $long?sprintf("%011o", strlen($nameFile)):sprintf("%011o", is_dir($file) ? 0 : filesize($file));
185
+ $header['12-mtime'] = $long?'00000000000':sprintf("%011o", $infoFile['mtime']);
186
+ $header['8-check'] = sprintf('% 8s', '');
187
+ $header['1-type'] = $long?'L':(is_link($file) ? 2 : is_dir ($file) ? 5 : 0);
188
+ $header['100-symlink'] = is_link($file) == 2 ? readlink($item) : '';
189
+ $header['6-magic'] = 'ustar ';
190
+ $header['2-version'] = ' ';
191
+ $a=function_exists('posix_getpwuid')?posix_getpwuid (fileowner($file)):array('name'=>'');
192
+ $header['32-uname'] = $a['name'];
193
+ $a=function_exists('posix_getgrgid')?posix_getgrgid (filegroup($file)):array('name'=>'');
194
+ $header['32-gname'] = $a['name'];
195
+ $header['8-devmajor'] = '';
196
+ $header['8-devminor'] = '';
197
+ $header['155-prefix'] = '';
198
+ $header['12-closer'] = '';
199
+
200
+ $packedHeader = '';
201
+ foreach ($header as $key=>$element) {
202
+ $length = explode('-', $key);
203
+ $packedHeader .= pack('a' . $length[0], $element);
204
+ }
205
+
206
+ $checksum = 0;
207
+ for ($i = 0; $i < 512; $i++) {
208
+ $checksum += ord(substr($packedHeader, $i, 1));
209
+ }
210
+ $packedHeader = substr_replace($packedHeader, sprintf("%07o", $checksum)."\0", 148, 8);
211
+
212
+ return $longHeader . $packedHeader;
213
+ }
214
+
215
+ /**
216
+ * Read TAR string from file, and unpacked it.
217
+ * Create files and directories information about discribed
218
+ * in the string.
219
+ *
220
+ * @param string $destination path to file is unpacked
221
+ * @return array list of files
222
+ */
223
+ protected function _unpackCurrentTar($destination)
224
+ {
225
+ $file = $this->_getCurrentFile();
226
+ $pointer = fopen($file, 'r');
227
+ if (empty($pointer)) {
228
+ throw new Mage_Exception('Can\'t open file: ' . $file);
229
+ }
230
+ $list = array();
231
+ while (!feof($pointer)) {
232
+ $header = $this->_parseHeader($pointer);
233
+ if ($header) {
234
+ $currentFile = $destination . $header['name'];
235
+ if ($header['type']=='5' && @mkdir($currentFile, 0777, true)) {
236
+ $list[] = $currentFile . DS;
237
+ } elseif (in_array($header['type'], array("0",chr(0), ''))) {
238
+ $dirname = dirname($currentFile);
239
+ if(!file_exists($dirname)) {
240
+ @mkdir($dirname, 0777, true);
241
+ }
242
+ $this->_writeFile($currentFile, $header['data']);
243
+ $list[] = $currentFile;
244
+ }
245
+ }
246
+ }
247
+ fclose($pointer);
248
+ return $list;
249
+ }
250
+
251
+ /**
252
+ * Get header from TAR string and unpacked it by format.
253
+ *
254
+ * @param resource $pointer
255
+ * @return string
256
+ */
257
+ protected function _parseHeader(&$pointer)
258
+ {
259
+ $firstLine = fread($pointer, 512);
260
+
261
+ if (strlen($firstLine)<512){
262
+ return false;
263
+ }
264
+
265
+ $fmt = self::FORMAT_PARSE_HEADER;
266
+ $header = unpack ($fmt, $firstLine);
267
+
268
+
269
+ $header['mode']=$header['mode']+0;
270
+ $header['uid']=octdec($header['uid']);
271
+ $header['gid']=octdec($header['gid']);
272
+ $header['size']=octdec($header['size']);
273
+ $header['mtime']=octdec($header['mtime']);
274
+ $header['checksum']=octdec($header['checksum']);
275
+
276
+ if ($header['type'] == "5") {
277
+ $header['size'] = 0;
278
+ }
279
+
280
+ $checksum = 0;
281
+ $firstLine = substr_replace($firstLine, ' ', 148, 8);
282
+ for ($i = 0; $i < 512; $i++) {
283
+ $checksum += ord(substr($firstLine, $i, 1));
284
+ }
285
+
286
+ $isUstar = 'ustar' == strtolower(substr($header['magic'], 0, 5));
287
+
288
+ $checksumOk = $header['checksum'] == $checksum;
289
+ if (isset($header['name']) && $checksumOk) {
290
+ if ($header['name'] == '././@LongLink' && $header['type'] == 'L') {
291
+ $realName = substr(fread($pointer, floor(($header['size'] + 512 - 1) / 512) * 512), 0, $header['size']);
292
+ $headerMain = $this->_parseHeader($pointer);
293
+ $headerMain['name'] = $realName;
294
+ return $headerMain;
295
+ } else {
296
+ if ($header['size']>0) {
297
+ $header['data'] = substr(fread($pointer, floor(($header['size'] + 512 - 1) / 512) * 512), 0, $header['size']);
298
+ } else {
299
+ $header['data'] = '';
300
+ }
301
+ return $header;
302
+ }
303
+ }
304
+ return false;
305
+ }
306
+
307
+ /**
308
+ * Pack file to TAR (Tape Archiver).
309
+ *
310
+ * @param string $source
311
+ * @param string $destination
312
+ * @param boolean $skipRoot
313
+ * @return string
314
+ */
315
+ public function pack($source, $destination, $skipRoot=false)
316
+ {
317
+ $this->_setSkipRoot($skipRoot);
318
+ $source = realpath($source);
319
+ $tarData = $this->_setCurrentPath($source)
320
+ ->_setCurrentFile($source)
321
+ ->_packToTar($skipRoot);
322
+ $this->_writeFile($destination, $tarData);
323
+ return $destination;
324
+ }
325
+
326
+ /**
327
+ * Unpack file from TAR (Tape Archiver).
328
+ *
329
+ * @param string $source
330
+ * @param string $destination
331
+ * @return string
332
+ */
333
+ public function unpack($source, $destination)
334
+ {
335
+ $tempFile = $destination . DS . '~tmp-'.microtime(true).'.tar';
336
+ $data = $this->_readFile($source);
337
+ $this->_writeFile($tempFile, $data);
338
+ $this->_setCurrentFile($tempFile)
339
+ ->_setCurrentPath($tempFile)
340
+ ->_unpackCurrentTar($destination);
341
+ unlink($tempFile);
342
+ return $destination;
343
+ }
344
+
345
+ /**
346
+ * Extract one file from TAR (Tape Archiver).
347
+ *
348
+ * @param string $file
349
+ * @param string $source
350
+ * @param string $destination
351
+ * @return string
352
+ */
353
+ public function extract($file, $source, $destination)
354
+ {
355
+ $pointer = fopen($source, 'r');
356
+ if (empty($pointer)) {
357
+ throw new Mage_Exception('Can\'t open file: '.$source);
358
+ }
359
+ $list = array();
360
+ $extractedFile = '';
361
+ while (!feof($pointer)) {
362
+ $header = $this->_parseHeader($pointer);
363
+ if ($header['name'] == $file) {
364
+ $extractedFile = $destination . basename($header['name']);
365
+ $this->_writeFile($extractedFile, $header['data']);
366
+ break;
367
+ }
368
+ }
369
+ fclose($pointer);
370
+ return $extractedFile;
371
+ }
372
+ }
lib/Mage/Autoload/Simple.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ class Mage_Autoload_Simple
28
+ {
29
+ private static $_instance;
30
+
31
+ public static function instance()
32
+ {
33
+ if (!self::$_instance) {
34
+ $class = __CLASS__;
35
+ self::$_instance = new $class();
36
+ }
37
+ return self::$_instance;
38
+ }
39
+
40
+ public static function register()
41
+ {
42
+ spl_autoload_register(array(self::instance(), 'autoload'));
43
+ }
44
+
45
+ public function autoload($class)
46
+ {
47
+ $classFile = str_replace(' ', DIRECTORY_SEPARATOR, ucwords(str_replace('_', ' ', $class)));
48
+ $classFile.= '.php';
49
+ @include $classFile;
50
+ }
51
+
52
+ }
lib/Mage/Connect/Channel/Generator.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ class Mage_Connect_Channel_Generator extends Mage_Xml_Generator
28
+ {
29
+ protected $_file = 'channel.xml';
30
+ protected $_generator = null;
31
+
32
+ public function __construct($file='')
33
+ {
34
+ if ($file) {
35
+ $this->_file = $file;
36
+ }
37
+ return $this;
38
+ }
39
+
40
+ public function getFile()
41
+ {
42
+ return $this->_file;
43
+ }
44
+
45
+ public function getGenerator()
46
+ {
47
+ if (is_null($this->_generator)) {
48
+ $this->_generator = new Mage_Xml_Generator();
49
+ }
50
+ return $this->_generator;
51
+ }
52
+
53
+ /**
54
+ * @param array $content
55
+ */
56
+ public function save($content)
57
+ {
58
+ $xmlContent = $this->getGenerator()
59
+ ->arrayToXml($content)
60
+ ->save($this->getFile());
61
+ return $this;
62
+ }
63
+ }
lib/Mage/Connect/Channel/Parser.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
lib/Mage/Connect/Channel/VO.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+
28
+ class Mage_Connect_Channel_VO implements Iterator
29
+ {
30
+
31
+ private $_validator = null;
32
+
33
+ protected $properties = array(
34
+ 'name' => '',
35
+ 'uri' => '',
36
+ 'summary' => '',
37
+ );
38
+
39
+ public function rewind() {
40
+ reset($this->properties);
41
+ }
42
+
43
+ public function valid() {
44
+ return current($this->properties) !== false;
45
+ }
46
+
47
+ public function key() {
48
+ return key($this->properties);
49
+ }
50
+
51
+ public function current() {
52
+ return current($this->properties);
53
+ }
54
+
55
+ public function next() {
56
+ next($this->properties);
57
+ }
58
+
59
+ public function __get($var)
60
+ {
61
+ if (isset($this->properties[$var])) {
62
+ return $this->properties[$var];
63
+ }
64
+ return null;
65
+ }
66
+
67
+ public function __set($var, $value)
68
+ {
69
+ if (is_string($value)) {
70
+ $value = trim($value);
71
+ }
72
+ if (isset($this->properties[$var])) {
73
+ if ($value === null) {
74
+ $value = '';
75
+ }
76
+ $this->properties[$var] = $value;
77
+ }
78
+ }
79
+
80
+ public function toArray()
81
+ {
82
+ return array('channel' => $this->properties);
83
+ }
84
+
85
+ public function fromArray(array $arr)
86
+ {
87
+ foreach($arr as $k=>$v) {
88
+ $this->$k = $v;
89
+ }
90
+ }
91
+
92
+
93
+ private function validator()
94
+ {
95
+ if(is_null($this->_validator)) {
96
+ $this->_validator = new Mage_Connect_Validator();
97
+ }
98
+ return $this->_validator;
99
+ }
100
+
101
+ /**
102
+ Stub for validation result
103
+ */
104
+ public function validate()
105
+ {
106
+ $v = $this->validator();
107
+ if(!$v->validatePackageName($this->name)) {
108
+ return false;
109
+ }
110
+ return true;
111
+ }
112
+
113
+ }
lib/Mage/Connect/Command.php ADDED
@@ -0,0 +1,390 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+
28
+ class Mage_Connect_Command
29
+ {
30
+ /**
31
+ * All commands list
32
+ * @var array
33
+ */
34
+ protected static $_commandsAll = array();
35
+
36
+ /**
37
+ * Commands list hash (key=class)
38
+ * @var array
39
+ */
40
+ protected static $_commandsByClass = array();
41
+
42
+ /**
43
+ * Frontend object
44
+ * @var Mage_Connect_Fro
45
+ */
46
+ protected static $_frontend = null;
47
+ protected static $_config = null;
48
+ protected static $_registry = null;
49
+ protected static $_validator = null;
50
+ protected static $_rest = null;
51
+ protected static $_sconfig = null;
52
+
53
+ protected $_data;
54
+ protected $_class;
55
+ protected static $_packager = null;
56
+
57
+ protected static $_return = array();
58
+
59
+ /**
60
+ * Constructor
61
+ *
62
+ */
63
+ public function __construct()
64
+ {
65
+ $class = $this->_class = get_class($this);
66
+ if(__CLASS__ == $class) {
67
+ throw new Exception("You shouldn't instantiate {$class} directly!");
68
+ }
69
+ $this->commandsInfo = self::$_commandsByClass[$class];
70
+ }
71
+
72
+
73
+ /**
74
+ * Get command info (static)
75
+ * @param string $name command name
76
+ * @return array/bool
77
+ */
78
+ public static function commandInfo($name)
79
+ {
80
+ $name = strtolower($name);
81
+ if(!isset(self::$_commandsAll[$name])) {
82
+ return false;
83
+ }
84
+ return self::$_commandsAll[$name];
85
+ }
86
+
87
+ /**
88
+ * Get command info for current command object
89
+ * @param string $name
90
+ * @return array/bool
91
+ */
92
+
93
+ public function getCommandInfo($name)
94
+ {
95
+ if(!isset(self::$_commandsByClass[$this->_class][$name])) {
96
+ return false;
97
+ }
98
+ return self::$_commandsByClass[$this->_class][$name];
99
+ }
100
+
101
+ /**
102
+ * Run command
103
+ * @param string $command
104
+ * @param string $options
105
+ * @param string $params
106
+ * @throws Exception if there's no needed method
107
+ * @return mixed
108
+ */
109
+ public function run($command, $options, $params)
110
+ {
111
+ $data = $this->getCommandInfo($command);
112
+ $method = $data['function'];
113
+ if(! method_exists($this, $method)) {
114
+ throw new Exception("$method does't exist in class ".$this->_class);
115
+ }
116
+ return $this->$method($command, $options, $params);
117
+ }
118
+
119
+ /**
120
+ * Static functions
121
+ */
122
+
123
+ /**
124
+ * Static
125
+ * @param $commandName
126
+ * @return unknown_type
127
+ */
128
+ public static function getInstance($commandName)
129
+ {
130
+ if(!isset(self::$_commandsAll[$commandName])) {
131
+ throw new UnexpectedValueException("Cannot find command $commandName");
132
+ }
133
+ $currentCommand = self::$_commandsAll[$commandName];
134
+ return new $currentCommand['class']();
135
+ }
136
+
137
+
138
+ public static function setSconfig($obj)
139
+ {
140
+ self::$_sconfig = $obj;
141
+ }
142
+
143
+ /**
144
+ *
145
+ * @return Mage_Connect_Singleconfig
146
+ */
147
+ public function getSconfig()
148
+ {
149
+ return self::$_sconfig;
150
+ }
151
+
152
+
153
+ /**
154
+ * Sets frontend object for all commands
155
+ *
156
+ * @param Mage_Connect_Frontend $obj
157
+ * @return void
158
+ */
159
+ public static function setFrontendObject($obj)
160
+ {
161
+ self::$_frontend = $obj;
162
+ }
163
+
164
+
165
+ /**
166
+ * Set config object for all commands
167
+ * @param Mage_Connect_Config $obj
168
+ * @return void
169
+ */
170
+ public static function setConfigObject($obj)
171
+ {
172
+ self::$_config = $obj;
173
+ }
174
+
175
+
176
+ /**
177
+ * Non-static getter for config
178
+ * @return Mage_Connect_Config
179
+ */
180
+ public function config()
181
+ {
182
+ return self::$_config;
183
+ }
184
+
185
+ /**
186
+ * Non-static getter for UI
187
+ * @return Mage_Connect_Frontend
188
+ */
189
+ public function ui()
190
+ {
191
+ return self::$_frontend;
192
+ }
193
+
194
+
195
+ /**
196
+ * Get validator object
197
+ * @return Mage_Connect_Validator
198
+ */
199
+ public function validator()
200
+ {
201
+ if(is_null(self::$_validator)) {
202
+ self::$_validator = new Mage_Connect_Validator();
203
+ }
204
+ return self::$_validator;
205
+ }
206
+
207
+ /**
208
+ * Get rest object
209
+ * @return Mage_Connect_Rest
210
+ */
211
+ public function rest()
212
+ {
213
+ if(is_null(self::$_rest)) {
214
+ self::$_rest = new Mage_Connect_Rest(self::config()->protocol);
215
+ }
216
+ return self::$_rest;
217
+ }
218
+
219
+
220
+ /**
221
+ * Get commands list sorted
222
+ * @return array
223
+ */
224
+ public static function getCommands()
225
+ {
226
+ if(!count(self::$_commandsAll)) {
227
+ self::registerCommands();
228
+ }
229
+ ksort(self::$_commandsAll);
230
+ return self::$_commandsAll;
231
+ }
232
+
233
+
234
+ /**
235
+ * Get Getopt args from command definitions
236
+ * and parse them
237
+ * @param $command
238
+ * @return array
239
+ */
240
+ public static function getGetoptArgs($command)
241
+ {
242
+ $commandInfo = self::commandInfo($command);
243
+ $short_args = '';
244
+ $long_args = array();
245
+ if (empty($commandInfo) || empty($commandInfo['options'])) {
246
+ return;
247
+ }
248
+ reset($commandInfo['options']);
249
+ while (list($option, $info) = each($commandInfo['options'])) {
250
+ $larg = $sarg = '';
251
+ if (isset($info['arg'])) {
252
+ if ($info['arg']{0} == '(') {
253
+ $larg = '==';
254
+ $sarg = '::';
255
+ $arg = substr($info['arg'], 1, -1);
256
+ } else {
257
+ $larg = '=';
258
+ $sarg = ':';
259
+ $arg = $info['arg'];
260
+ }
261
+ }
262
+ if (isset($info['shortopt'])) {
263
+ $short_args .= $info['shortopt'] . $sarg;
264
+ }
265
+ $long_args[] = $option . $larg;
266
+ }
267
+ return array($short_args, $long_args);
268
+ }
269
+
270
+ /**
271
+ * Try to register commands automatically
272
+ * @return void
273
+ */
274
+ public static function registerCommands()
275
+ {
276
+ $pathCommands = dirname(__FILE__).DIRECTORY_SEPARATOR.basename(__FILE__, ".php");
277
+ $f = new DirectoryIterator($pathCommands);
278
+ foreach($f as $file) {
279
+ if (! $file->isFile()) {
280
+ continue;
281
+ }
282
+ $pattern = preg_match("/(.*)_Header\.php/imsu", $file->getFilename(), $matches);
283
+ if(! $pattern) {
284
+ continue;
285
+ }
286
+ include($file->getPathname());
287
+ if(! isset($commands)) {
288
+ continue;
289
+ }
290
+ $class = __CLASS__."_".$matches[1];
291
+ foreach ($commands as $k=>$v) {
292
+ $commands[$k]['class'] = $class;
293
+ self::$_commandsAll[$k] = $commands[$k];
294
+ }
295
+ self::$_commandsByClass[$class] = $commands;
296
+ }
297
+ }
298
+
299
+ public function doError($command, $message)
300
+ {
301
+ return $this->ui()->doError($command, $message);
302
+ }
303
+
304
+
305
+ /**
306
+ * Set command return
307
+ * @param string $key
308
+ * @param mixed $val
309
+ * @return void
310
+ */
311
+ public static function setReturn($key, $val)
312
+ {
313
+ self::$_return[$key] = $val;
314
+ }
315
+
316
+ /**
317
+ * Get command return
318
+ * @param $key
319
+ * @param $clear
320
+ * @return mixed
321
+ */
322
+ public static function getReturn($key, $clear = true)
323
+ {
324
+ if(isset(self::$_return[$key])) {
325
+ $out = self::$_return[$key];
326
+ if($clear) {
327
+ unset(self::$_return[$key]);
328
+ }
329
+ return $out;
330
+ }
331
+ return null;
332
+ }
333
+
334
+ /**
335
+ * Cleanup command params from empty strings
336
+ *
337
+ * @param array $params by reference
338
+ */
339
+ public function cleanupParams(array & $params)
340
+ {
341
+ $newParams = array();
342
+ if(!count($params)) {
343
+ return;
344
+ }
345
+ foreach($params as $k=>$v) {
346
+ if(is_string($v)) {
347
+ $v = trim($v);
348
+ if(!strlen($v)) {
349
+ continue;
350
+ }
351
+ }
352
+ $newParams[] = $v;
353
+ }
354
+ $params = $newParams;
355
+ }
356
+
357
+ /**
358
+ * Splits first command argument: channel/package
359
+ * to two arguments if found in top of array
360
+ *
361
+ * @param array $params
362
+ */
363
+ public function splitPackageArgs(array & $params)
364
+ {
365
+ if(!count($params) || !isset($params[0])) {
366
+ return;
367
+ }
368
+ if($this->validator()->validateUrl($params[0])) {
369
+ return;
370
+ }
371
+ if(preg_match("@([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)@ims", $params[0], $subs)) {
372
+ $params[0] = $subs[2];
373
+ array_unshift($params, $subs[1]);
374
+ }
375
+ }
376
+
377
+
378
+ /**
379
+ * Get packager instance
380
+ * @return Mage_Connect_Pacakger
381
+ */
382
+ public function getPackager()
383
+ {
384
+ if(!self::$_packager) {
385
+ self::$_packager = new Mage_Connect_Packager();
386
+ }
387
+ return self::$_packager;
388
+ }
389
+
390
+ }
lib/Mage/Connect/Command/Channels.php ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ final class Mage_Connect_Command_Channels
28
+ extends Mage_Connect_Command
29
+ {
30
+
31
+ /**
32
+ * List available channels
33
+ * @param $command
34
+ * @param $params
35
+ * @param $options
36
+ */
37
+ public function doList($command, $options, $params)
38
+ {
39
+
40
+ try {
41
+ $title = "Available channels:";
42
+ $aliasT = "Available aliases:";
43
+ $packager = $this->getPackager();
44
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
45
+ if($ftp) {
46
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
47
+ $data = $cache->getData();
48
+ @unlink($config->getFilename());
49
+ @unlink($cache->getFilename());
50
+ } else {
51
+ $cache = $this->getSconfig();
52
+ $config = $this->config();
53
+ $data = $cache->getData();
54
+ }
55
+ $out = array($command => array('data'=>$data, 'title'=>$title, 'title_aliases'=>$aliasT));
56
+ $this->ui()->output($out);
57
+ } catch (Exception $e) {
58
+ $this->doError($command, $e->getMessage());
59
+ }
60
+ }
61
+
62
+ /**
63
+ * channel-delete callback method
64
+ * @param string $command
65
+ * @param array $options
66
+ * @param array $params
67
+ */
68
+ public function doDelete($command, $options, $params)
69
+ {
70
+ $this->cleanupParams($params);
71
+ try {
72
+ if(count($params) != 1) {
73
+ throw new Exception("Parameters count should be equal to 1");
74
+ }
75
+ $packager = $this->getPackager();
76
+
77
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
78
+ if($ftp) {
79
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
80
+ $cache->deleteChannel($params[0]);
81
+ $packager->writeToRemoteCache($cache, $ftpObj);
82
+ @unlink($config->getFilename());
83
+ } else {
84
+ $config = $this->config();
85
+ $cache = $this->getSconfig();
86
+ $cache->deleteChannel($params[0]);
87
+ }
88
+ $this->ui()->output("Successfully deleted");
89
+
90
+ } catch (Exception $e) {
91
+ $this->doError($command, $e->getMessage());
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Channel-add callback
97
+ * @param string $command
98
+ * @param array $options
99
+ * @param array $params
100
+ */
101
+ public function doAdd($command, $options, $params)
102
+ {
103
+ $this->cleanupParams($params);
104
+ try {
105
+ if(count($params) != 1) {
106
+ throw new Exception("Parameters count should be equal to 1");
107
+ }
108
+ $url = $params[0];
109
+ $rest = $this->rest();
110
+ $rest->setChannel($url);
111
+ $data = $rest->getChannelInfo();
112
+ $data->url = $url;
113
+
114
+ $packager = $this->getPackager();
115
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
116
+ if($ftp) {
117
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
118
+ $cache->addChannel($data->name, $url);
119
+ $packager->writeToRemoteCache($cache, $ftpObj);
120
+ @unlink($config->getFilename());
121
+ } else {
122
+ $cache = $this->getSconfig();
123
+ $config = $this->config();
124
+ $cache->addChannel($data->name, $url);
125
+ }
126
+
127
+ $this->ui()->output("Successfully added: ".$url);
128
+ } catch (Exception $e) {
129
+ $this->doError($command, $e->getMessage());
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Get information about given channel callback
135
+ * @param string $command
136
+ * @param array $options
137
+ * @param array $params
138
+ */
139
+ public function doInfo($command, $options, $params)
140
+ {
141
+
142
+ }
143
+
144
+ /**
145
+ * channel-alias
146
+ * @param $command
147
+ * @param $options
148
+ * @param $params
149
+ * @return unknown_type
150
+ */
151
+ public function doAlias($command, $options, $params)
152
+ {
153
+ $this->cleanupParams($params);
154
+ try {
155
+ if(count($params) != 2) {
156
+ throw new Exception("Parameters count should be equal to 2");
157
+ }
158
+
159
+ $packager = $this->getPackager();
160
+ $chanUrl = $params[0];
161
+ $alias = $params[1];
162
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
163
+ if($ftp) {
164
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
165
+ $cache->addChannelAlias($chanUrl, $alias);
166
+ $packager->writeToRemoteCache($cache, $ftpObj);
167
+ @unlink($config->getFilename());
168
+ } else {
169
+ $cache = $this->getSconfig();
170
+ $config = $this->config();
171
+ $cache->addChannelAlias($chanUrl, $alias);
172
+ }
173
+ $this->ui()->output("Successfully added: ".$alias);
174
+ } catch (Exception $e) {
175
+ $this->doError($command, $e->getMessage());
176
+ }
177
+ }
178
+
179
+ public function doLogin($command, $options, $params)
180
+ {
181
+
182
+ }
183
+
184
+ public function doLogout($command, $options, $params)
185
+ {
186
+
187
+ }
188
+
189
+ }
lib/Mage/Connect/Command/Channels_Header.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ $commands = array(
28
+ 'list-channels' => array(
29
+ 'summary' => 'List Available Channels',
30
+ 'function' => 'doList',
31
+ 'shortcut' => 'lc',
32
+ 'options' => array(),
33
+ 'doc' => '
34
+ List all available channels for installation.
35
+ ',
36
+ ),
37
+ 'channel-delete' => array(
38
+ 'summary' => 'Remove a Channel From the List',
39
+ 'function' => 'doDelete',
40
+ 'shortcut' => 'cde',
41
+ 'options' => array(),
42
+ 'doc' => '<channel name>
43
+ Delete a channel from the registry. You may not
44
+ remove any channel that has installed packages.
45
+ '
46
+ ),
47
+ 'channel-add' => array(
48
+ 'summary' => 'Add a Channel',
49
+ 'function' => 'doAdd',
50
+ 'shortcut' => 'ca',
51
+ 'options' => array(),
52
+ 'doc' => '<channel.xml>
53
+ Add a private channel to the channel list. Note that all
54
+ public channels should be synced using "update-channels".
55
+ Parameter may be either a local file or remote URL to a
56
+ channel.xml.
57
+ '
58
+ ),
59
+ 'channel-info' => array(
60
+ 'summary' => 'Retrieve Information on a Channel',
61
+ 'function' => 'doInfo',
62
+ 'shortcut' => 'ci',
63
+ 'options' => array(),
64
+ 'doc' => '<package>
65
+ List the files in an installed package.
66
+ '
67
+ ),
68
+ 'channel-alias' => array(
69
+ 'summary' => 'Specify an alias to a channel name',
70
+ 'function' => 'doAlias',
71
+ 'shortcut' => 'cha',
72
+ 'options' => array(),
73
+ 'doc' => '<channel> <alias>
74
+ Specify a specific alias to use for a channel name.
75
+ The alias may not be an existing channel name or
76
+ alias.
77
+ '
78
+ ),
79
+ 'channel-login' => array(
80
+ 'summary' => 'Connects and authenticates to remote channel server',
81
+ 'shortcut' => 'cli',
82
+ 'function' => 'doLogin',
83
+ 'options' => array(),
84
+ 'doc' => '<channel name>
85
+ Log in to a remote channel server. If <channel name> is not supplied,
86
+ the default channel is used. To use remote functions in the installer
87
+ that require any kind of privileges, you need to log in first. The
88
+ username and password you enter here will be stored in your per-user
89
+ PEAR configuration (~/.pearrc on Unix-like systems). After logging
90
+ in, your username and password will be sent along in subsequent
91
+ operations on the remote server.',
92
+ ),
93
+ 'channel-logout' => array(
94
+ 'summary' => 'Logs out from the remote channel server',
95
+ 'shortcut' => 'clo',
96
+ 'function' => 'doLogout',
97
+ 'options' => array(),
98
+ 'doc' => '<channel name>
99
+ Logs out from a remote channel server. If <channel name> is not supplied,
100
+ the default channel is used. This command does not actually connect to the
101
+ remote server, it only deletes the stored username and password from your user
102
+ configuration.',
103
+ ),
104
+ );
105
+
lib/Mage/Connect/Command/Config.php ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+
28
+ class Mage_Connect_Command_Config
29
+ extends Mage_Connect_Command
30
+
31
+ {
32
+ const PARAM_KEY = 0;
33
+ const PARAM_VAL = 1;
34
+
35
+
36
+ /**
37
+ * Show config variable
38
+ * @param string $command
39
+ * @param array $options
40
+ * @param array $params
41
+ * @return void
42
+ */
43
+ public function doConfigShow($command, $options, $params)
44
+ {
45
+ $this->cleanupParams($params);
46
+
47
+ try {
48
+ $values = array();
49
+
50
+ $packager = $this->getPackager();
51
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
52
+ if($ftp) {
53
+ list($config, $ftpObj) = $packager->getRemoteConfig($ftp);
54
+ } else {
55
+ $config = $this->config();
56
+ }
57
+ foreach( $config as $k=>$v ) {
58
+ $values[$k] = $v;
59
+ }
60
+ if($ftp) {
61
+ @unlink($config->getFilename());
62
+ }
63
+ $data = array($command => array('data'=>$values));
64
+ $this->ui()->output($data);
65
+ } catch (Exception $e) {
66
+ if($ftp) {
67
+ @unlink($config->getFilename());
68
+ }
69
+ return $this->doError($command, $e->getMessage());
70
+ }
71
+ }
72
+
73
+
74
+ /**
75
+ * Set config variable
76
+ * @param string $command
77
+ * @param array $options
78
+ * @param array $params
79
+ * @return void
80
+ */
81
+ public function doConfigSet($command, $options, $params)
82
+ {
83
+ $this->cleanupParams($params);
84
+
85
+ try {
86
+ if(count($params) < 2) {
87
+ throw new Exception("Parameters count should be >= 2");
88
+ }
89
+ $key = strtolower($params[self::PARAM_KEY]);
90
+ $val = strval($params[self::PARAM_VAL]);
91
+ $packager = $this->getPackager();
92
+
93
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
94
+ if($ftp) {
95
+ list($config, $ftpObj) = $packager->getRemoteConfig($ftp);
96
+ } else {
97
+ $config = $this->config();
98
+ }
99
+
100
+ if(!$config->hasKey($key)) {
101
+ throw new Exception ("No such config variable: {$key}!");
102
+ }
103
+ if(!$config->validate($key, $val)) {
104
+ $possible = $this->config()->possible($key);
105
+ $type = $this->config()->type($key);
106
+ $errString = "Invalid value specified for $key!";
107
+ throw new Exception($errString);
108
+ }
109
+ if($ftp) {
110
+ $packager->writeToRemoteConfig($config, $ftpObj);
111
+ }
112
+ $this->config()->$key = $val;
113
+ $this->ui()->output('Success');
114
+ } catch (Exception $e) {
115
+ if($ftp) {
116
+ @unlink($config->getFilename());
117
+ }
118
+ return $this->doError($command, $e->getMessage());
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Get config var
124
+ * @param string $command
125
+ * @param array $options
126
+ * @param array $params
127
+ * @return void
128
+ */
129
+ public function doConfigGet($command, $options, $params)
130
+ {
131
+ $this->cleanupParams($params);
132
+
133
+ try {
134
+ if(count($params) < 1) {
135
+ throw new Exception("Parameters count should be >= 1");
136
+ }
137
+ $packager = $this->getPackager();
138
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
139
+ if($ftp) {
140
+ list($config, $ftpObj) = $packager->getRemoteConfig($ftp);
141
+ } else {
142
+ $config = $this->config();
143
+ }
144
+ $key = strtolower($params[self::PARAM_KEY]);
145
+ if(!$config->hasKey($key)) {
146
+ throw new Exception("No such config variable '{$key}'!");
147
+ }
148
+ if($ftp) {
149
+ @unlink($config->getFilename());
150
+ }
151
+ $this->ui()->output($config->$key);
152
+ } catch (Exception $e) {
153
+ if($ftp) {
154
+ @unlink($config->getFilename());
155
+ }
156
+ return $this->doError($command, $e->getMessage());
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Config help
162
+ * @param string $command
163
+ * @param array $options
164
+ * @param array $params
165
+ * @return void
166
+ */
167
+ public function doConfigHelp($command, $options, $params)
168
+ {
169
+ try {
170
+ $this->cleanupParams($params);
171
+ if(count($params) < 1) {
172
+ throw new Exception( "Parameters count should be >= 1");
173
+ }
174
+ $packager = $this->getPackager();
175
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
176
+ if($ftp) {
177
+ list($config, $ftpObj) = $packager->getRemoteConfig($ftp);
178
+ } else {
179
+ $config = $this->config();
180
+ }
181
+
182
+ $key = strtolower($params[self::PARAM_KEY]);
183
+ if(!$this->config()->hasKey($key)) {
184
+ throw new Exception("No such config variable '{$key}'!");
185
+ }
186
+
187
+ $possible = $config->possible($key);
188
+ $type = $config->type($key);
189
+ $doc = $config->doc($key);
190
+ if($ftp) {
191
+ @unlink($config->getFilename());
192
+ }
193
+ $data = array();
194
+ $data[$command]['data'] = array(
195
+ 'name' => array('Variable name', $key),
196
+ 'type' => array('Value type', $type),
197
+ 'possible' => array('Possible values', $possible),
198
+ 'doc' => $doc,
199
+ );
200
+ $this->ui()->output($data);
201
+ } catch (Exception $e) {
202
+ if($ftp) {
203
+ @unlink($config->getFilename());
204
+ }
205
+ return $this->doError($command, $e->getMessage());
206
+ }
207
+ }
208
+
209
+ }
210
+
211
+
lib/Mage/Connect/Command/Config_Header.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ $commands = array(
28
+ 'config-show' => array(
29
+ 'summary' => 'Show All Settings',
30
+ 'function' => 'doConfigShow',
31
+ 'shortcut' => 'csh',
32
+ 'options' => array(
33
+ 'channel' => array(
34
+ 'shortopt' => 'c',
35
+ 'doc' => 'show configuration variables for another channel',
36
+ 'arg' => 'CHAN',
37
+ ),
38
+ ),
39
+ 'doc' => '[layer]
40
+ Displays all configuration values. An optional argument
41
+ may be used to tell which configuration layer to display. Valid
42
+ configuration layers are "user", "system" and "default". To display
43
+ configurations for different channels, set the default_channel
44
+ configuration variable and run config-show again.
45
+ ',
46
+ ),
47
+ 'config-get' => array(
48
+ 'summary' => 'Show One Setting',
49
+ 'function' => 'doConfigGet',
50
+ 'shortcut' => 'cg',
51
+ 'options' => array(
52
+ 'channel' => array(
53
+ 'shortopt' => 'c',
54
+ 'doc' => 'show configuration variables for another channel',
55
+ 'arg' => 'CHAN',
56
+ ),
57
+ ),
58
+ 'doc' => '<parameter> [layer]
59
+ Displays the value of one configuration parameter. The
60
+ first argument is the name of the parameter, an optional second argument
61
+ may be used to tell which configuration layer to look in. Valid configuration
62
+ layers are "user", "system" and "default". If no layer is specified, a value
63
+ will be picked from the first layer that defines the parameter, in the order
64
+ just specified. The configuration value will be retrieved for the channel
65
+ specified by the default_channel configuration variable.
66
+ ',
67
+ ),
68
+ 'config-set' => array(
69
+ 'summary' => 'Change Setting',
70
+ 'function' => 'doConfigSet',
71
+ 'shortcut' => 'cs',
72
+ 'options' => array(
73
+ 'channel' => array(
74
+ 'shortopt' => 'c',
75
+ 'doc' => 'show configuration variables for another channel',
76
+ 'arg' => 'CHAN',
77
+ ),
78
+ ),
79
+ 'doc' => '<parameter> <value> [layer]
80
+ Sets the value of one configuration parameter. The first argument is
81
+ the name of the parameter, the second argument is the new value. Some
82
+ parameters are subject to validation, and the command will fail with
83
+ an error message if the new value does not make sense. An optional
84
+ third argument may be used to specify in which layer to set the
85
+ configuration parameter. The default layer is "user". The
86
+ configuration value will be set for the current channel, which
87
+ is controlled by the default_channel configuration variable.
88
+ ',
89
+ ),
90
+ 'config-help' => array(
91
+ 'summary' => 'Show Information About Setting',
92
+ 'function' => 'doConfigHelp',
93
+ 'shortcut' => 'ch',
94
+ 'options' => array(),
95
+ 'doc' => '[parameter]
96
+ Displays help for a configuration parameter. Without arguments it
97
+ displays help for all configuration parameters.
98
+ ',
99
+ ),
100
+ );
lib/Mage/Connect/Command/Install.php ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+
28
+ final class Mage_Connect_Command_Install
29
+ extends Mage_Connect_Command
30
+ {
31
+
32
+ /**
33
+ * Install action callback
34
+ * @param string $command
35
+ * @param array $options
36
+ * @param array $params
37
+ * @return void
38
+ */
39
+ public function doInstall($command, $options, $params, $objects = array())
40
+ {
41
+ $this->cleanupParams($params);
42
+
43
+ $installFileMode = $command === 'install-file';
44
+
45
+
46
+
47
+
48
+ try {
49
+ $packager = $this->getPackager();
50
+ $forceMode = isset($options['force']);
51
+ $upgradeAllMode = $command == 'upgrade-all';
52
+ $upgradeMode = $command == 'upgrade' || $command == 'upgrade-all';
53
+ $noFilesInstall = isset($options['nofiles']);
54
+ $withDepsMode = !isset($options['nodeps']);
55
+ $ignoreModifiedMode = true || !isset($options['ignorelocalmodification']);
56
+
57
+ $rest = $this->rest();
58
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
59
+ if($ftp) {
60
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
61
+ } else {
62
+ $config = $this->config();
63
+ $cache = $this->getSconfig();
64
+ }
65
+ if($installFileMode) {
66
+ if(count($params) < 1) {
67
+ throw new Exception("Argument should be: filename");
68
+ }
69
+ $filename = $params[0];
70
+ if(!@file_exists($filename)) {
71
+ throw new Exception("File '{$filename}' not found");
72
+ }
73
+ if(!@is_readable($filename)) {
74
+ throw new Exception("File '{$filename}' is not readable");
75
+ }
76
+
77
+ $package = new Mage_Connect_Package($filename);
78
+ $package->validate();
79
+ $errors = $package->getErrors();
80
+ if(count($errors)) {
81
+ throw new Exception("Package file is invalid\n".implode("\n", $errors));
82
+ }
83
+
84
+ $pChan = $package->getChannel();
85
+ $pName = $package->getName();
86
+ $pVer = $package->getVersion();
87
+
88
+
89
+ if(!$cache->isChannel($pChan)) {
90
+ throw new Exception("'{$pChan}' is not installed channel");
91
+ }
92
+
93
+ $conflicts = $cache->hasConflicts($pChan, $pName, $pVer);
94
+
95
+ if(false !== $conflicts) {
96
+ $conflicts = implode(", ",$conflicts);
97
+ if($forceMode) {
98
+ $this->doError($command, "Package {$pChan}/{$pName} {$pVer} conflicts with: ".$conflicts);
99
+ } else {
100
+ throw new Exception("Package {$pChan}/{$pName} {$pVer} conflicts with: ".$conflicts);
101
+ }
102
+ }
103
+
104
+ $conflicts = $package->checkPhpDependencies();
105
+ if(true !== $conflicts) {
106
+ $confilcts = implode(",",$conflicts);
107
+ $err = "Package {$pChan}/{$pName} {$pVer} depends on PHP extensions: ".$conflicts;
108
+ if($forceMode) {
109
+ $this->doError($command, $err);
110
+ } else {
111
+ throw new Exception($err);
112
+ }
113
+ }
114
+
115
+ $conflicts = $package->checkPhpVersion();
116
+ if(true !== $conflicts) {
117
+ $err = "Package {$pChan}/{$pName} {$pVer}: ".$conflicts;
118
+ if($forceMode) {
119
+ $this->doError($command, $err);
120
+ } else {
121
+ throw new Exception($err);
122
+ }
123
+ }
124
+
125
+
126
+ if(!$noFilesInstall) {
127
+ if($ftp) {
128
+ $packager->processInstallPackageFtp($package, $filename, $config, $ftpObj);
129
+ } else {
130
+ $packager->processInstallPackage($package, $filename, $config);
131
+ }
132
+ }
133
+ $cache->addPackage($package);
134
+ $installedDeps = array();
135
+ $installedDepsAssoc = array();
136
+ $installedDepsAssoc[] = array('channel'=>$pChan, 'name'=>$pName, 'version'=>$pVer);
137
+ $installedDeps[] = array($pChan, $pName, $pVer);
138
+
139
+
140
+ $title = isset($options['title']) ? $options['title'] : "Package installed: ";
141
+ $out = array($command => array('data'=>$installedDeps, 'assoc'=>$installedDepsAssoc, 'title'=>$title));
142
+
143
+ if($ftp) {
144
+ $packager->writeToRemoteCache($cache, $ftpObj);
145
+ @unlink($config->getFilename());
146
+ }
147
+
148
+ $this->ui()->output($out);
149
+ return $out[$command]['data'];
150
+ }
151
+
152
+ if(!$upgradeAllMode) {
153
+
154
+ if(count($params) < 2) {
155
+ throw new Exception("Argument should be: channelName packageName");
156
+ }
157
+ $channel = $params[0];
158
+ $package = $params[1];
159
+ $argVersionMax = isset($params[2]) ? $params[2]: false;
160
+ $argVersionMin = false;
161
+
162
+ if($cache->isChannelName($channel)) {
163
+ $uri = $cache->chanUrl($channel);
164
+ } elseif($this->validator()->validateUrl($channel)) {
165
+ $uri = $channel;
166
+ } elseif($channel) {
167
+ $uri = $config->protocol.'://'.$channel;
168
+ } else {
169
+ throw new Exception("'{$channel}' is not existant channel name / valid uri");
170
+ }
171
+
172
+ if($uri && !$cache->isChannel($uri)) {
173
+ $rest->setChannel($uri);
174
+ $data = $rest->getChannelInfo();
175
+ $data->uri = $uri;
176
+ $cache->addChannel($data->name, $uri);
177
+ $this->ui()->output("Successfully added channel: ".$uri);
178
+ }
179
+ $channelName = $cache->chanName($channel);
180
+ //var_dump($channelName);
181
+ $packagesToInstall = $packager->getDependenciesList( $channelName, $package, $cache, $config, $argVersionMax, $argVersionMin, $withDepsMode);
182
+ $packagesToInstall = $packagesToInstall['result'];
183
+ //var_dump($packagesToInstall);
184
+
185
+ } else {
186
+ if(empty($params[0])) {
187
+ $channels = $cache->getChannelNames();
188
+ } else {
189
+ $channel = $params[0];
190
+ if(!$cache->isChannel($channel)) {
191
+ throw new Exception("'{$channel}' is not existant channel name / valid uri");
192
+ }
193
+ $channels = $cache->chanName($channel);
194
+ }
195
+ $packagesToInstall = array();
196
+ $neededToUpgrade = $packager->getUpgradesList($channels, $cache, $config);
197
+ foreach($neededToUpgrade as $chan=>$packages) {
198
+ foreach($packages as $name=>$data) {
199
+ $versionTo = $data['to'];
200
+ $tmp = $packager->getDependenciesList( $chan, $name, $cache, $config, $versionTo, $versionTo, $withDepsMode);
201
+ if(count($tmp['result'])) {
202
+ $packagesToInstall = array_merge($packagesToInstall, $tmp['result']);
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Make installation
210
+ */
211
+ $installedDeps = array();
212
+ $installedDepsAssoc = array();
213
+ $keys = array();
214
+
215
+ foreach($packagesToInstall as $package) {
216
+ try {
217
+ $pName = $package['name'];
218
+ $pChan = $package['channel'];
219
+ $pVer = $package['downloaded_version'];
220
+ $rest->setChannel($cache->chanUrl($pChan));
221
+
222
+ /**
223
+ * Upgrade mode
224
+ */
225
+ if($upgradeMode && $cache->hasPackage($pChan, $pName, $pVer, $pVer)) {
226
+ $this->ui()->output("Already installed: {$pChan}/{$pName} {$pVer}, skipping");
227
+ continue;
228
+ }
229
+
230
+ $conflicts = $cache->hasConflicts($pChan, $pName, $pVer);
231
+
232
+ if(false !== $conflicts) {
233
+ $conflicts = implode(", ",$conflicts);
234
+ if($forceMode) {
235
+ $this->doError($command, "Package {$pChan}/{$pName} {$pVer} conflicts with: ".$conflicts);
236
+ } else {
237
+ throw new Exception("Package {$pChan}/{$pName} {$pVer} conflicts with: ".$conflicts);
238
+ }
239
+ }
240
+
241
+
242
+ /**
243
+ * Modifications
244
+ */
245
+ if ($upgradeMode && !$ignoreModifiedMode) {
246
+ if($ftp) {
247
+ $modifications = $packager->getRemoteModifiedFiles($pChan, $pName, $cache, $config, $ftp);
248
+ } else {
249
+ $modifications = $packager->getLocalModifiedFiles($pChan, $pName, $cache, $config);
250
+ }
251
+ if (count($modifications) > 0) {
252
+ $this->ui()->output('Changed locally: ');
253
+ foreach ($modifications as $row) {
254
+ if(!$ftp) {
255
+ $this->ui()->output($config->magento_root.DS.$row);
256
+ } else {
257
+ $this->ui()->output($row);
258
+ }
259
+ }
260
+ /*$this->ui()->confirm('Do you want rewrite all files?');
261
+ continue;*/
262
+ }
263
+ }
264
+
265
+ $dir = $config->getChannelCacheDir($pChan);
266
+ @mkdir($dir, 0777, true);
267
+ $file = $dir.DIRECTORY_SEPARATOR.$pName."-".$pVer.".tgz";
268
+ if(!@file_exists($file)) {
269
+ $rest->downloadPackageFileOfRelease($pName, $pVer, $file);
270
+ }
271
+ $package = new Mage_Connect_Package($file);
272
+
273
+
274
+
275
+ $conflicts = $package->checkPhpDependencies();
276
+ if(true !== $conflicts) {
277
+ $confilcts = implode(",",$conflicts);
278
+ $err = "Package {$pChan}/{$pName} {$pVer} depends on PHP extensions: ".$conflicts;
279
+ if($forceMode) {
280
+ $this->doError($command, $err);
281
+ } else {
282
+ throw new Exception($err);
283
+ }
284
+ }
285
+
286
+ $conflicts = $package->checkPhpVersion();
287
+ if(true !== $conflicts) {
288
+ $err = "Package {$pChan}/{$pName} {$pVer}: ".$conflicts;
289
+ if($forceMode) {
290
+ $this->doError($command, $err);
291
+ } else {
292
+ throw new Exception($err);
293
+ }
294
+ }
295
+
296
+ if(!$noFilesInstall) {
297
+ if($ftp) {
298
+ $packager->processInstallPackageFtp($package, $file, $config, $ftpObj);
299
+ } else {
300
+ $packager->processInstallPackage($package, $file, $config);
301
+ }
302
+ }
303
+ $cache->addPackage($package);
304
+
305
+ $installedDepsAssoc[] = array('channel'=>$pChan, 'name'=>$pName, 'version'=>$pVer);
306
+ $installedDeps[] = array($pChan, $pName, $pVer);
307
+
308
+ } catch(Exception $e) {
309
+ $this->doError($command, $e->getMessage());
310
+ }
311
+ }
312
+
313
+
314
+
315
+ $title = isset($options['title']) ? $options['title'] : "Package installed: ";
316
+ $out = array($command => array('data'=>$installedDeps, 'assoc'=>$installedDepsAssoc, 'title'=>$title));
317
+
318
+ if($ftp) {
319
+ $packager->writeToRemoteCache($cache, $ftpObj);
320
+ @unlink($config->getFilename());
321
+ }
322
+
323
+ $this->ui()->output($out);
324
+ return $out[$command]['data'];
325
+
326
+ } catch (Exception $e) {
327
+ if($ftp) {
328
+ $packager->writeToRemoteCache($cache, $ftpObj);
329
+ @unlink($config->getFilename());
330
+ }
331
+ return $this->doError($command, $e->getMessage());
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Upgrade action callback
337
+ * @param string $command
338
+ * @param array $options
339
+ * @param array $params
340
+ * @return void
341
+ */
342
+ public function doUpgrade($command, $options, $params)
343
+ {
344
+ $options['title'] = "Package upgraded: ";
345
+ return $this->doInstall($command, $options, $params);
346
+ }
347
+
348
+ /**
349
+ * Updgrade action callback
350
+ * @param string $command
351
+ * @param array $options
352
+ * @param array $params
353
+ * @return void
354
+ */
355
+ public function doUpgradeAll($command, $options, $params)
356
+ {
357
+ $options['title'] = "Package upgraded: ";
358
+ return $this->doInstall($command, $options, $params);
359
+ }
360
+
361
+ /**
362
+ * Uninstall package callback
363
+ * @param string $command
364
+ * @param array $options
365
+ * @param array $params
366
+ * @return unknown_type
367
+ */
368
+ public function doUninstall($command, $options, $params)
369
+ {
370
+ $this->cleanupParams($params);
371
+ //$this->splitPackageArgs($params);
372
+
373
+ try {
374
+ if(count($params) != 2) {
375
+ throw new Exception("Argument count should be = 2");
376
+ }
377
+
378
+ $channel = $params[0];
379
+ $package = $params[1];
380
+ $packager = $this->getPackager();
381
+ $withDepsMode = !isset($options['nodeps']);
382
+ $forceMode = isset($options['force']);
383
+
384
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
385
+ if($ftp) {
386
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
387
+ } else {
388
+ $cache = $this->getSconfig();
389
+ $config = $this->config();
390
+ }
391
+
392
+ $chan = $cache->getChannel($channel);
393
+ $channel = $cache->chanName($channel);
394
+ if(!$cache->hasPackage($channel, $package)) {
395
+ throw new Exception("Package is not installed");
396
+ }
397
+
398
+ $deletedPackages = array();
399
+ $list = $packager->getUninstallList($channel, $package, $cache, $config, $withDepsMode);
400
+ foreach($list['list'] as $packageData) {
401
+ try {
402
+ $reqd = $cache->requiredByOtherPackages($packageData['channel'], $packageData['name'], $list['list']);
403
+ if(count($reqd)) {
404
+ $errMessage = "{$packageData['channel']}/{$packageData['name']} {$packageData['version']} is required by: ";
405
+ $t = array();
406
+ foreach($reqd as $r) {
407
+ $t[] = $r['channel']."/".$r['name']. " ".$r['version'];
408
+ }
409
+ $errMessage .= implode(", ", $t);
410
+ if($forceMode) {
411
+ $this->ui()->output("Warning: ".$errMessage);
412
+ } else {
413
+ throw new Exception($errMessage);
414
+ }
415
+ }
416
+
417
+ list($chan, $pack) = array($packageData['channel'], $packageData['name']);
418
+ if($ftp) {
419
+ $packager->processUninstallPackageFtp($chan, $pack, $cache, $config, $ftp);
420
+ } else {
421
+ $packager->processUninstallPackage($chan, $pack, $cache, $config);
422
+ }
423
+ $cache->deletePackage($chan, $pack);
424
+ $deletedPackages[] = array($chan, $pack);
425
+
426
+ } catch(Exception $e) {
427
+ if($forceMode) {
428
+ $this->doError($command, $e->getMessage());
429
+ } else {
430
+ throw new Exception($e->getMessage());
431
+ }
432
+ }
433
+ }
434
+ if($ftp) {
435
+ $packager->writeToRemoteCache($cache, $ftpObj);
436
+ @unlink($config->getFilename());
437
+ }
438
+ $out = array($command=>array('data'=>$deletedPackages, 'title'=>'Package deleted: '));
439
+ $this->ui()->output($out);
440
+
441
+ } catch (Exception $e) {
442
+ return $this->doError($command, $e->getMessage());
443
+ }
444
+
445
+ }
446
+
447
+ }
448
+
lib/Mage/Connect/Command/Install_Header.php ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ $commands = array(
28
+
29
+ 'install-file' => array(
30
+ 'summary' => 'Install Package Archive File',
31
+ 'function' => 'doInstall',
32
+ 'shortcut' => 'if',
33
+ 'options' => array(),
34
+ 'doc' => '',
35
+ ),
36
+ 'install' => array(
37
+ 'summary' => 'Install Package',
38
+ 'function' => 'doInstall',
39
+ 'shortcut' => 'i',
40
+ 'options' => array(
41
+ 'force' => array(
42
+ 'shortopt' => 'f',
43
+ 'doc' => 'will overwrite newer installed packages',
44
+ ),
45
+ 'loose' => array(
46
+ 'shortopt' => 'l',
47
+ 'doc' => 'do not check for recommended dependency version',
48
+ ),
49
+ 'nodeps' => array(
50
+ 'shortopt' => 'n',
51
+ 'doc' => 'ignore dependencies, install anyway',
52
+ ),
53
+ 'ignore-errors' => array(
54
+ 'doc' => 'force install even if there were errors',
55
+ ),
56
+ 'alldeps' => array(
57
+ 'shortopt' => 'a',
58
+ 'doc' => 'install all required and optional dependencies',
59
+ ),
60
+ 'pretend' => array(
61
+ 'shortopt' => 'p',
62
+ 'doc' => 'Only list the packages that would be downloaded',
63
+ ),
64
+ 'ftp=' => array(
65
+ 'shortopt' => 'r=',
66
+ 'doc' => 'Remote side FTP connect string',
67
+ ),
68
+ ),
69
+ 'doc' => '[channel/]<package> ...
70
+ Installs one or more PEAR packages. You can specify a package to
71
+ install in four ways:
72
+
73
+ "Package-1.0.tgz" : installs from a local file
74
+
75
+ "http://example.com/Package-1.0.tgz" : installs from
76
+ anywhere on the net.
77
+
78
+ "package.xml" : installs the package described in
79
+ package.xml. Useful for testing, or for wrapping a PEAR package in
80
+ another package manager such as RPM.
81
+
82
+ "Package[-version/state][.tar]" : queries your default channel\'s server
83
+ ({config master_server}) and downloads the newest package with
84
+ the preferred quality/state ({config preferred_state}).
85
+
86
+ To retrieve Package version 1.1, use "Package-1.1," to retrieve
87
+ Package state beta, use "Package-beta." To retrieve an uncompressed
88
+ file, append .tar (make sure there is no file by the same name first)
89
+
90
+ To download a package from another channel, prefix with the channel name like
91
+ "channel/Package"
92
+
93
+ More than one package may be specified at once. It is ok to mix these
94
+ four ways of specifying packages.
95
+ '),
96
+ 'upgrade' => array(
97
+ 'summary' => 'Upgrade Package',
98
+ 'function' => 'doUpgrade',
99
+ 'shortcut' => 'up',
100
+ 'options' => array(
101
+ 'channel' => array(
102
+ 'shortopt' => 'c',
103
+ 'doc' => 'upgrade packages from a specific channel',
104
+ 'arg' => 'CHAN',
105
+ ),
106
+ 'force' => array(
107
+ 'shortopt' => 'f',
108
+ 'doc' => 'overwrite newer installed packages',
109
+ ),
110
+ 'loose' => array(
111
+ 'shortopt' => 'l',
112
+ 'doc' => 'do not check for recommended dependency version',
113
+ ),
114
+ 'nodeps' => array(
115
+ 'shortopt' => 'n',
116
+ 'doc' => 'ignore dependencies, upgrade anyway',
117
+ ),
118
+ 'register-only' => array(
119
+ 'shortopt' => 'r',
120
+ 'doc' => 'do not install files, only register the package as upgraded',
121
+ ),
122
+ 'nobuild' => array(
123
+ 'shortopt' => 'B',
124
+ 'doc' => 'don\'t build C extensions',
125
+ ),
126
+ 'nocompress' => array(
127
+ 'shortopt' => 'Z',
128
+ 'doc' => 'request uncompressed files when downloading',
129
+ ),
130
+ 'installroot' => array(
131
+ 'shortopt' => 'R',
132
+ 'arg' => 'DIR',
133
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
134
+ ),
135
+ 'ignore-errors' => array(
136
+ 'doc' => 'force install even if there were errors',
137
+ ),
138
+ 'alldeps' => array(
139
+ 'shortopt' => 'a',
140
+ 'doc' => 'install all required and optional dependencies',
141
+ ),
142
+ 'onlyreqdeps' => array(
143
+ 'shortopt' => 'o',
144
+ 'doc' => 'install all required dependencies',
145
+ ),
146
+ 'offline' => array(
147
+ 'shortopt' => 'O',
148
+ 'doc' => 'do not attempt to download any urls or contact channels',
149
+ ),
150
+ 'pretend' => array(
151
+ 'shortopt' => 'p',
152
+ 'doc' => 'Only list the packages that would be downloaded',
153
+ ),
154
+ ),
155
+ 'doc' => '<package> ...
156
+ Upgrades one or more PEAR packages. See documentation for the
157
+ "install" command for ways to specify a package.
158
+
159
+ When upgrading, your package will be updated if the provided new
160
+ package has a higher version number (use the -f option if you need to
161
+ upgrade anyway).
162
+
163
+ More than one package may be specified at once.
164
+ '),
165
+ 'upgrade-all' => array(
166
+ 'summary' => 'Upgrade All Packages',
167
+ 'function' => 'doUpgradeAll',
168
+ 'shortcut' => 'ua',
169
+ 'options' => array(
170
+ 'channel' => array(
171
+ 'shortopt' => 'c',
172
+ 'doc' => 'upgrade packages from a specific channel',
173
+ 'arg' => 'CHAN',
174
+ ),
175
+ 'nodeps' => array(
176
+ 'shortopt' => 'n',
177
+ 'doc' => 'ignore dependencies, upgrade anyway',
178
+ ),
179
+ 'register-only' => array(
180
+ 'shortopt' => 'r',
181
+ 'doc' => 'do not install files, only register the package as upgraded',
182
+ ),
183
+ 'nobuild' => array(
184
+ 'shortopt' => 'B',
185
+ 'doc' => 'don\'t build C extensions',
186
+ ),
187
+ 'nocompress' => array(
188
+ 'shortopt' => 'Z',
189
+ 'doc' => 'request uncompressed files when downloading',
190
+ ),
191
+ 'installroot' => array(
192
+ 'shortopt' => 'R',
193
+ 'arg' => 'DIR',
194
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
195
+ ),
196
+ 'ignore-errors' => array(
197
+ 'doc' => 'force install even if there were errors',
198
+ ),
199
+ 'loose' => array(
200
+ 'doc' => 'do not check for recommended dependency version',
201
+ ),
202
+ ),
203
+ 'doc' => '
204
+ WARNING: This function is deprecated in favor of using the upgrade command with no params
205
+
206
+ Upgrades all packages that have a newer release available. Upgrades are
207
+ done only if there is a release available of the state specified in
208
+ "preferred_state" (currently {config preferred_state}), or a state considered
209
+ more stable.
210
+ '),
211
+ 'uninstall' => array(
212
+ 'summary' => 'Un-install Package',
213
+ 'function' => 'doUninstall',
214
+ 'shortcut' => 'un',
215
+ 'options' => array(
216
+ 'nodeps' => array(
217
+ 'shortopt' => 'n',
218
+ 'doc' => 'ignore dependencies, uninstall anyway',
219
+ ),
220
+ 'register-only' => array(
221
+ 'shortopt' => 'r',
222
+ 'doc' => 'do not remove files, only register the packages as not installed',
223
+ ),
224
+ 'ignore-errors' => array(
225
+ 'doc' => 'force install even if there were errors',
226
+ ),
227
+ 'offline' => array(
228
+ 'shortopt' => 'O',
229
+ 'doc' => 'do not attempt to uninstall remotely',
230
+ ),
231
+ ),
232
+ 'doc' => '[channel/]<package> ...
233
+ Uninstalls one or more PEAR packages. More than one package may be
234
+ specified at once. Prefix with channel name to uninstall from a
235
+ channel not in your default channel ({config default_channel})
236
+ '),
237
+ );
lib/Mage/Connect/Command/Package.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide- web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ final class Mage_Connect_Command_Package
28
+ extends Mage_Connect_Command
29
+ {
30
+ /**
31
+ * Dependencies list
32
+ * @var array
33
+ */
34
+ private $_depsList = array();
35
+
36
+ /**
37
+ * Releases list
38
+ * @var array
39
+ */
40
+ private $_releasesList = array();
41
+
42
+ /**
43
+ * Package command callback
44
+ * @param string $command
45
+ * @param array $options
46
+ * @param array $params
47
+ * @return void
48
+ */
49
+ public function doPackage($command, $options, $params)
50
+ {
51
+ $this->cleanupParams($params);
52
+
53
+ if(count($params) < 1) {
54
+ return $this->doError($command, "Parameters count should be >= 1");
55
+ }
56
+
57
+ $file = strtolower($params[0]);
58
+ $file = realpath($file);
59
+
60
+ if(!file_exists($file)) {
61
+ return $this->doError($command, "File {$params[0]} doesn't exist");
62
+ }
63
+
64
+ try {
65
+ $packager = new Mage_Connect_Package($file);
66
+ $res = $packager->validate();
67
+ if(!$res) {
68
+ $this->doError($command, implode("\n", $packager->getErrors()));
69
+ return;
70
+ }
71
+ $packager->save(dirname($file));
72
+ $this->ui()->output('Done building package');
73
+ } catch (Exception $e) {
74
+ $this->doError( $command, $e->getMessage() );
75
+ }
76
+ }
77
+
78
+
79
+ /**
80
+ * Display/get dependencies
81
+ * @param string $command
82
+ * @param array $options
83
+ * @param array $params
84
+ * @return void/array
85
+ */
86
+ public function doPackageDependencies($command, $options, $params)
87
+ {
88
+ $this->cleanupParams($params);
89
+ try {
90
+ if(count($params) < 2) {
91
+ return $this->doError($command, "Argument count should be >= 2");
92
+ }
93
+
94
+ $channel = $params[0];
95
+ $package = $params[1];
96
+
97
+ $argVersionMin = isset($params[3]) ? $params[3] : false;
98
+ $argVersionMax = isset($params[2]) ? $params[2] : false;
99
+
100
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
101
+ $packager = $this->getPackager();
102
+ if($ftp) {
103
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
104
+ } else {
105
+ $cache = $this->getSconfig();
106
+ $config = $this->config();
107
+ }
108
+ $data = $packager->getDependenciesList($channel, $package, $cache, $config, $argVersionMax, $argVersionMin);
109
+ $this->ui()->output(array($command=> array('data'=>$data['deps'], 'title'=>"Package deps for {$params[1]}: ")));
110
+
111
+ } catch (Exception $e) {
112
+ $this->doError($command, $e->getMessage());
113
+ }
114
+ }
115
+
116
+ public function doConvert($command, $options, $params)
117
+ {
118
+ $this->cleanupParams($params);
119
+ try {
120
+ if(count($params) < 1) {
121
+ throw new Exception("Arguments should be: source.tgz [target.tgz]");
122
+ }
123
+ $sourceFile = $params[0];
124
+ $converter = new Mage_Connect_Converter();
125
+ $targetFile = isset($params[1]) ? $params[1] : false;
126
+ $result = $converter->convertPearToMage($sourceFile, $targetFile);
127
+ $this->ui()->output("Saved to: ".$result);
128
+ } catch (Exception $e) {
129
+ $this->doError($command, $e->getMessage());
130
+ }
131
+
132
+ }
133
+
134
+ }
lib/Mage/Connect/Command/Package_Header.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ $commands = array(
28
+ 'package' => array(
29
+ 'summary' => 'Build Package',
30
+ 'function' => 'doPackage',
31
+ 'shortcut' => 'p',
32
+ 'options' => array(
33
+ 'nocompress' => array(
34
+ 'shortopt' => 'Z',
35
+ 'doc' => 'Do not gzip the package file'
36
+ ),
37
+ 'showname' => array(
38
+ 'shortopt' => 'n',
39
+ 'doc' => 'Print the name of the packaged file.',
40
+ ),
41
+ ),
42
+ 'doc' => '[descfile] [descfile2]
43
+ Creates a PEAR package from its description file (usually called
44
+ package.xml). If a second packagefile is passed in, then
45
+ the packager will check to make sure that one is a package.xml
46
+ version 1.0, and the other is a package.xml version 2.0. The
47
+ package.xml version 1.0 will be saved as "package.xml" in the archive,
48
+ and the other as "package2.xml" in the archive"
49
+ '
50
+ ),
51
+ 'package-dependencies' => array(
52
+ 'summary' => 'Show package dependencies',
53
+ 'function' => 'doPackageDependencies',
54
+ 'shortcut' => 'pd',
55
+ 'options' => array(),
56
+ 'doc' => '<package-file> or <package.xml> or <install-package-name>
57
+ List all dependencies the package has.
58
+ Can take a tgz / tar file, package.xml or a package name of an installed package.'
59
+ ),
60
+ 'convert' => array(
61
+ 'summary' => 'Convert old magento PEAR package to new format',
62
+ 'function' => 'doConvert',
63
+ 'shortcut' => 'conv',
64
+ 'options' => array(),
65
+ 'doc' => ''
66
+ ),
67
+ );
lib/Mage/Connect/Command/Registry.php ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ final class Mage_Connect_Command_Registry
28
+ extends Mage_Connect_Command
29
+ {
30
+
31
+ /**
32
+ * List-installed callback
33
+ * @param string $command
34
+ * @param array $options
35
+ * @param array $params
36
+ * @return void
37
+ */
38
+ public function doList($command, $options, $params)
39
+ {
40
+ $this->cleanupParams($params);
41
+ try {
42
+ $packager = $this->getPackager();
43
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
44
+ if($ftp) {
45
+ list($cache, $ftpObj) = $packager->getRemoteCache($ftp);
46
+ } else {
47
+ $cache = $this->getSconfig();
48
+ }
49
+ if(!empty($params[0])) {
50
+ $chanName = $conf->chanName($params[0]);
51
+ $data = $cache->getInstalledPackages($chanName);
52
+ } else {
53
+ $data = $cache->getInstalledPackages();
54
+ }
55
+ if($ftp) {
56
+ @unlink($cache->getFilename());
57
+ }
58
+ $this->ui()->output(array($command=>array('data'=>$data, 'channel-title'=>"Installed package for channel '%s' :")));
59
+ } catch (Exception $e) {
60
+ if($ftp) {
61
+ @unlink($cache->getFilename());
62
+ }
63
+ $this->doError($command, $e->getMessage());
64
+ }
65
+
66
+ }
67
+
68
+ /**
69
+ * list-files callback
70
+ * @param string $command
71
+ * @param array $options
72
+ * @param array $params
73
+ * @return void
74
+ */
75
+ public function doFileList($command, $options, $params)
76
+ {
77
+ $this->cleanupParams($params);
78
+ //$this->splitPackageArgs($params);
79
+ try {
80
+ $channel = false;
81
+ if(count($params) < 2) {
82
+ throw new Exception("Argument count should be = 2");
83
+ }
84
+ $channel = $params[0];
85
+ $package = $params[1];
86
+
87
+ $packager = $this->getPackager();
88
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
89
+ if($ftp) {
90
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
91
+ } else {
92
+ $cache = $this->getSconfig();
93
+ $confif = $this->config();
94
+ }
95
+ if(!$cache->hasPackage($channel, $package)) {
96
+ return $this->ui()->output("No package found: {$channel}/{$package}");
97
+ }
98
+
99
+ $p = $cache->getPackageObject($channel, $package);
100
+ $contents = $p->getContents();
101
+ if($ftp) {
102
+ $ftpObj->close();
103
+ }
104
+ if(!count($contents)) {
105
+ return $this->ui()->output("No contents for package {$package}");
106
+ }
107
+ $title = ("Contents of '{$package}': ");
108
+ if($ftp) {
109
+ @unlink($config->getFilename());
110
+ @unlink($cache->getFilename());
111
+ }
112
+
113
+ $this->ui()->output(array($command=>array('data'=>$contents, 'title'=>$title)));
114
+
115
+ } catch (Exception $e) {
116
+ if($ftp) {
117
+ @unlink($config->getFilename());
118
+ @unlink($cache->getFilename());
119
+ }
120
+ $this->doError($command, $e->getMessage());
121
+ }
122
+
123
+ }
124
+
125
+ /**
126
+ * Installed package info
127
+ * info command callback
128
+ * @param string $command
129
+ * @param array $options
130
+ * @param array $params
131
+ * @return
132
+ */
133
+ public function doInfo($command, $options, $params)
134
+ {
135
+ $this->cleanupParams($params);
136
+ //$this->splitPackageArgs($params);
137
+
138
+ try {
139
+ $channel = false;
140
+ if(count($params) < 2) {
141
+ throw new Exception("Argument count should be = 2");
142
+ }
143
+ $channel = $params[0];
144
+ $package = $params[1];
145
+ $packager = $this->getPackager();
146
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
147
+ if($ftp) {
148
+ list($cache, $ftpObj) = $packager->getRemoteCache($ftp);
149
+ } else {
150
+ $cache = $this->getSconfig();
151
+ }
152
+
153
+ if(!$cache->isChannel($channel)) {
154
+ throw new Exception("'{$channel}' is not a valid installed channel name/uri");
155
+ }
156
+ $channelUri = $cache->chanUrl($channel);
157
+ $rest = $this->rest();
158
+ $rest->setChannel($channelUri);
159
+ $releases = $rest->getReleases($package);
160
+ if(false === $releases) {
161
+ throw new Exception("No information found about {$channel}/{$package}");
162
+ }
163
+ $data = array($command => array('releases'=>$releases));
164
+ if($ftp) {
165
+ @unlink($cache->getFilename());
166
+ }
167
+ $this->ui()->output($data);
168
+ } catch (Exception $e) {
169
+ if($ftp) {
170
+ @unlink($cache->getFilename());
171
+ }
172
+ $this->doError($command, $e->getMessage());
173
+ }
174
+ }
175
+ }
lib/Mage/Connect/Command/Registry_Header.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ $commands = array(
28
+ 'list-installed' => array(
29
+ 'summary' => 'List Installed Packages In The Default Channel',
30
+ 'function' => 'doList',
31
+ 'shortcut' => 'l',
32
+ 'options' => array(
33
+ 'channel' => array(
34
+ 'shortopt' => 'c',
35
+ 'doc' => 'list installed packages from this channel',
36
+ 'arg' => 'CHAN',
37
+ ),
38
+ 'allchannels' => array(
39
+ 'shortopt' => 'a',
40
+ 'doc' => 'list installed packages from all channels',
41
+ ),
42
+ ),
43
+ 'doc' => '<package>
44
+ If invoked without parameters, this command lists the PEAR packages
45
+ installed in your php_dir ({config php_dir}). With a parameter, it
46
+ lists the files in a package.
47
+ ',
48
+ ),
49
+ 'list-files' => array(
50
+ 'summary' => 'List Files In Installed Package',
51
+ 'function' => 'doFileList',
52
+ 'shortcut' => 'fl',
53
+ 'options' => array(),
54
+ 'doc' => '<package>
55
+ List the files in an installed package.
56
+ '
57
+ ),
58
+ 'info' => array(
59
+ 'summary' => 'Display information about a package',
60
+ 'function' => 'doInfo',
61
+ 'shortcut' => 'in',
62
+ 'options' => array(),
63
+ 'doc' => '<package>
64
+ Displays information about a package. The package argument may be a
65
+ local package file, an URL to a package file, or the name of an
66
+ installed package.'
67
+ )
68
+ );
lib/Mage/Connect/Command/Remote.php ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ final class Mage_Connect_Command_Remote
28
+ extends Mage_Connect_Command
29
+ {
30
+
31
+ /**
32
+ * List-upgrades callback
33
+ * @param srting $command
34
+ * @param array $options
35
+ * @param array $params
36
+ * @return void
37
+ */
38
+ public function doListUpgrades($command, $options, $params)
39
+ {
40
+
41
+ $this->cleanupParams($params);
42
+ try {
43
+ $packager = new Mage_Connect_Packager();
44
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
45
+ if($ftp) {
46
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
47
+ } else {
48
+ $cache = $this->getSconfig();
49
+ $config = $this->config();
50
+ }
51
+
52
+ if(!empty($params[0])) {
53
+ $channels = $params[0];
54
+ $cache->getChannel($channels);
55
+ } else {
56
+ $channels = $cache->getChannelNames();
57
+ }
58
+ $ups = $packager->getUpgradesList($channels, $cache, $config);
59
+
60
+ if(count($ups)) {
61
+ $data = array($command => array('data'=>$ups));
62
+ } else {
63
+ $data = "No upgrades available";
64
+ }
65
+ $this->ui()->output($data);
66
+ } catch(Exception $e) {
67
+ $this->doError($command, $e->getMessage());
68
+ }
69
+ }
70
+
71
+
72
+ /**
73
+ * List available
74
+ * @param $command
75
+ * @param $options
76
+ * @param $params
77
+ * @return unknown_type
78
+ */
79
+
80
+ public function doListAvailable($command, $options, $params)
81
+ {
82
+ $this->cleanupParams($params);
83
+
84
+ try {
85
+ $packager = new Mage_Connect_Packager();
86
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
87
+ if($ftp) {
88
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
89
+ } else {
90
+ $cache = $this->getSconfig();
91
+ $config = $this->config();
92
+ }
93
+
94
+ if(!empty($params[0])) {
95
+ $channels = array($params[0]);
96
+ $cache->getChannel($channels[0]);
97
+ } else {
98
+ $channels = $cache->getChannelNames();
99
+ }
100
+
101
+
102
+
103
+ $packs = array();
104
+ foreach ($channels as $channel) {
105
+ try {
106
+ $chan = $cache->getChannel($channel);
107
+ $uri = $cache->chanUrl($channel);
108
+
109
+ $rest = $this->rest();
110
+ $rest->setChannel($uri);
111
+
112
+ $packages = $rest->getPackages();
113
+ if(!count($packages)) {
114
+ $this->ui()->output("Channel '{$channel}' has no packages");
115
+ continue;
116
+ }
117
+ $packs[$channel]['title'] = "Packages for channel '".$channel."':";
118
+ foreach($packages as $p) {
119
+ $packageName = $p['n'];
120
+ $releases = array();
121
+ foreach($p['r'] as $k=>$r) {
122
+ $releases[$r] = $rest->shortStateToLong($k);
123
+ }
124
+ $packs[$channel]['packages'][$packageName]['releases'] = $releases;
125
+ }
126
+ } catch (Exception $e) {
127
+ $this->doError($command, $e->getMessage());
128
+ }
129
+ }
130
+ $dataOut = array();
131
+ $dataOut[$command]= array('data'=>$packs);
132
+ $this->ui()->output($dataOut);
133
+
134
+ } catch(Exception $e) {
135
+ $this->doError($command, $e->getMessage());
136
+ }
137
+
138
+ }
139
+
140
+ /**
141
+ * Download command callback
142
+ *
143
+ * @param string $command
144
+ * @param array $options
145
+ * @param array $params
146
+ * @return void
147
+ */
148
+ public function doDownload($command, $options, $params)
149
+ {
150
+ $this->cleanupParams($params);
151
+ //$this->splitPackageArgs($params);
152
+ try {
153
+ if(count($params) < 2) {
154
+ throw new Exception("Arguments should be: channel Package");
155
+ }
156
+
157
+ $channel = $params[0];
158
+ $package = $params[1];
159
+
160
+ $packager = $this->getPackager();
161
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
162
+ if($ftp) {
163
+ list($cache, $config, $ftpObj) = $packager->getRemoteConf($ftp);
164
+ } else {
165
+ $cache = $this->getSconfig();
166
+ $config = $this->config();
167
+ }
168
+
169
+ $chan = $cache->getChannel($channel);
170
+ $uri = $cache->chanUrl($channel);
171
+
172
+ $rest = $this->rest();
173
+ $rest->setChannel($uri);
174
+ $c = $rest->getReleases($package);
175
+ if(!count($c)) {
176
+ throw new Exception("No releases found for package");
177
+ }
178
+ $version = $cache->detectVersionFromRestArray($c);
179
+ $dir = $config->getChannelCacheDir($channel);
180
+ $file = $dir.DIRECTORY_SEPARATOR.$package."-".$version.".tgz";
181
+ $rest->downloadPackageFileOfRelease($package, $version, $file);
182
+ if($ftp) {
183
+ @unlink($config->getFilename());
184
+ @unlink($cache->getFilename());
185
+ }
186
+ $this->ui()->output("Saved to: ". $file);
187
+ } catch (Exception $e) {
188
+ if($ftp) {
189
+ @unlink($config->getFilename());
190
+ @unlink($cache->getFilename());
191
+ }
192
+ $this->doError($command, $e->getMessage());
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Clear cache command callback
198
+ * @param string $command
199
+ * @param array $options
200
+ * @param array $params
201
+ * @return void
202
+ */
203
+ public function doClearCache($command, $options, $params)
204
+ {
205
+ $this->cleanupParams($params);
206
+ try {
207
+ $packager = new Mage_Connect_Packager();
208
+ $ftp = empty($options['ftp']) ? false : $options['ftp'];
209
+ if($ftp) {
210
+ list($cache, $ftpObj) = $packager->getRemoteCache($ftp);
211
+ $cache->clear();
212
+ $packager->writeToRemoteCache($cache, $ftpObj);
213
+ } else {
214
+ $cache = $this->getSconfig();
215
+ $cache->clear();
216
+ }
217
+ } catch (Exception $e) {
218
+ $this->doError($command, $e->getMessage());
219
+ }
220
+ }
221
+
222
+
223
+
224
+
225
+
226
+ }
lib/Mage/Connect/Command/Remote_Header.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ $commands = array(
28
+ 'list-upgrades' => array(
29
+ 'summary' => 'List Available Upgrades',
30
+ 'function' => 'doListUpgrades',
31
+ 'shortcut' => 'lu',
32
+ 'options' => array(
33
+ 'channelinfo' => array(
34
+ 'shortopt' => 'i',
35
+ 'doc' => 'output fully channel-aware data, even on failure',
36
+ ),
37
+ ),
38
+ 'doc' => '[preferred_state]
39
+ List releases on the server of packages you have installed where
40
+ a newer version is available with the same release state (stable etc.)
41
+ or the state passed as the second parameter.'
42
+ ),
43
+ 'list-available' => array(
44
+ 'summary' => 'List Available Packages',
45
+ 'function' => 'doListAvailable',
46
+ 'shortcut' => 'la',
47
+ 'options' => array(
48
+ 'channel' =>
49
+ array(
50
+ 'shortopt' => 'c',
51
+ 'doc' => 'specify a channel other than the default channel',
52
+ 'arg' => 'CHAN',
53
+ ),
54
+ 'channelinfo' => array(
55
+ 'shortopt' => 'i',
56
+ 'doc' => 'output fully channel-aware data, even on failure',
57
+ ),
58
+ ),
59
+ 'doc' => '
60
+ Lists the packages available on the configured server along with the
61
+ latest stable release of each package.',
62
+ ),
63
+ 'download' => array(
64
+ 'summary' => 'Download Package',
65
+ 'function' => 'doDownload',
66
+ 'shortcut' => 'd',
67
+ 'options' => array(
68
+ 'nocompress' => array(
69
+ 'shortopt' => 'Z',
70
+ 'doc' => 'download an uncompressed (.tar) file',
71
+ ),
72
+ ),
73
+ 'doc' => '<package>...
74
+ Download package tarballs. The files will be named as suggested by the
75
+ server, for example if you download the DB package and the latest stable
76
+ version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.',
77
+ ),
78
+ 'clear-cache' => array(
79
+ 'summary' => 'Clear Web Services Cache',
80
+ 'function' => 'doClearCache',
81
+ 'shortcut' => 'cc',
82
+ 'options' => array(),
83
+ 'doc' => '
84
+ Clear the XML-RPC/REST cache. See also the cache_ttl configuration
85
+ parameter.
86
+ ',
87
+ ),
88
+ );
lib/Mage/Connect/Config.php ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Mage_Connect_Config
4
+ implements Iterator
5
+ {
6
+ protected $_configFile;
7
+ const HEADER = "::ConnectConfig::v::1.0::";
8
+ const DEFAULT_DOWNLOADER_PATH = "downloader";
9
+ const DEFAULT_CACHE_PATH = ".cache";
10
+
11
+ protected $properties = array();
12
+
13
+ protected function initProperties()
14
+ {
15
+ $this->properties = array (
16
+ 'php_ini' => array(
17
+ 'type' => 'file',
18
+ 'value' => '',
19
+ 'prompt' => 'location of php.ini',
20
+ 'doc' => "It's a location of PHP.ini to use blah",
21
+ 'possible' => '/path/php.ini',
22
+ ),
23
+ 'protocol' => array(
24
+ 'type' => 'set',
25
+ 'value' => 'http',
26
+ 'prompt' => 'preffered protocol',
27
+ 'doc' => 'preffered protocol',
28
+ 'rules' => array('http', 'ftp')
29
+ ),
30
+ 'preferred_state' => array(
31
+ 'type' => 'set',
32
+ 'value' => 'stable',
33
+ 'prompt' => 'preferred package state',
34
+ 'doc' => 'preferred package state',
35
+ 'rules' => array('beta','alpha','stable','devel')
36
+ ),
37
+ 'global_dir_mode' => array (
38
+ 'type' => 'octal',
39
+ 'value' => 0777,
40
+ 'prompt' => 'directory creation mode',
41
+ 'doc' => 'directory creation mode',
42
+ 'possible' => '0777, 0666 etc.',
43
+ ),
44
+ 'global_file_mode' => array (
45
+ 'type' => 'octal',
46
+ 'value' => 0666,
47
+ 'prompt' => 'file creation mode',
48
+ 'doc' => 'file creation mode',
49
+ 'possible' => '0777, 0666 etc.',
50
+ ),
51
+ 'downloader_path' => array(
52
+ 'type' => 'dir',
53
+ 'value' => 'downloader',
54
+ 'prompt' => 'relative path, location of magento downloader',
55
+ 'doc' => "relative path, location of magento downloader",
56
+ 'possible' => 'path',
57
+ ),
58
+ 'magento_root' => array(
59
+ 'type' => 'dir',
60
+ 'value' => '',
61
+ 'prompt' => 'location of magento root dir',
62
+ 'doc' => "Location of magento",
63
+ 'possible' => '/path',
64
+ ),
65
+ 'root_channel' => array(
66
+ 'type' => 'string',
67
+ 'value' => 'core',
68
+ 'prompt' => '',
69
+ 'doc' => "",
70
+ 'possible' => '',
71
+ ),
72
+
73
+ );
74
+
75
+ }
76
+
77
+ public function getDownloaderPath()
78
+ {
79
+ return $this->magento_root . DIRECTORY_SEPARATOR . $this->downloader_path;
80
+ }
81
+
82
+ public function getPackagesCacheDir()
83
+ {
84
+ return $this->getDownloaderPath() . DIRECTORY_SEPARATOR . self::DEFAULT_CACHE_PATH;
85
+ }
86
+
87
+ public function getChannelCacheDir($channel)
88
+ {
89
+ $channel = trim( $channel, "\\/");
90
+ return $this->getPackagesCacheDir(). DIRECTORY_SEPARATOR . $channel;
91
+ }
92
+
93
+
94
+ public function __construct($configFile = "connect.cfg")
95
+ {
96
+ $this->initProperties();
97
+ $this->_configFile = $configFile;
98
+ $this->load();
99
+ }
100
+
101
+ public function getFilename()
102
+ {
103
+ return $this->_configFile;
104
+ }
105
+
106
+ public function load()
107
+ {
108
+ /**
109
+ * Trick: open in append mode to read,
110
+ * place pointer to begin
111
+ * create if not exists
112
+ */
113
+ $f = fopen($this->_configFile, "a+");
114
+ fseek($f, 0, SEEK_SET);
115
+ $size = filesize($this->_configFile);
116
+ if(!$size) {
117
+ $this->store();
118
+ return;
119
+ }
120
+
121
+ $headerLen = strlen(self::HEADER);
122
+ $contents = fread($f, $headerLen);
123
+
124
+ if(self::HEADER != $contents) {
125
+ $this->store();
126
+ return;
127
+ }
128
+
129
+ $size -= $headerLen;
130
+ $contents = fread($f, $size);
131
+
132
+ $data = @unserialize($contents);
133
+ if($data === unserialize(false)) {
134
+ $this->store();
135
+ return;
136
+ }
137
+ foreach($data as $k=>$v) {
138
+ $this->$k = $v;
139
+ }
140
+ fclose($f);
141
+ }
142
+
143
+ public function store()
144
+ {
145
+ $data = serialize($this->toArray());
146
+ $f = @fopen($this->_configFile, "w+");
147
+ @fwrite($f, self::HEADER);
148
+ @fwrite($f, $data);
149
+ @fclose($f);
150
+ }
151
+
152
+
153
+ public function validate($key, $val)
154
+ {
155
+ $rules = $this->extractField($key, 'rules');
156
+ if(null === $rules) {
157
+ return true;
158
+ } elseif( is_array($rules) ) {
159
+ return in_array($val, $rules);
160
+ }
161
+ return false;
162
+ }
163
+
164
+ public function possible($key)
165
+ {
166
+ $data = $this->getKey($key);
167
+ if(! $data) {
168
+ return null;
169
+ }
170
+ if('set' == $data['type']) {
171
+ return implode("|", $data['rules']);
172
+ }
173
+ if(!empty($data['possible'])) {
174
+ return $data['possible'];
175
+ }
176
+ return "<".$data['type'].">";
177
+ }
178
+
179
+ public function type($key)
180
+ {
181
+ return $this->extractField($key, 'type');
182
+ }
183
+
184
+ public function doc($key)
185
+ {
186
+ return $this->extractField($key, 'doc');
187
+ }
188
+
189
+
190
+ public function extractField($key, $field)
191
+ {
192
+ if(!isset($this->properties[$key][$field])) {
193
+ return null;
194
+ }
195
+ return $this->properties[$key][$field];
196
+ }
197
+
198
+
199
+ public function hasKey($fld)
200
+ {
201
+ return isset($this->properties[$fld]);
202
+ }
203
+
204
+ public function getKey($fld)
205
+ {
206
+ if($this->hasKey($fld)) {
207
+ return $this->properties[$fld];
208
+ }
209
+ return null;
210
+ }
211
+
212
+ public function rewind() {
213
+ reset($this->properties);
214
+ }
215
+
216
+ public function valid() {
217
+ return current($this->properties) !== false;
218
+ }
219
+
220
+ public function key() {
221
+ return key($this->properties);
222
+ }
223
+
224
+ public function current() {
225
+ return current($this->properties);
226
+ }
227
+
228
+ public function next() {
229
+ next($this->properties);
230
+ }
231
+
232
+ public function __get($var)
233
+ {
234
+ if (isset($this->properties[$var]['value'])) {
235
+ return $this->properties[$var]['value'];
236
+ }
237
+ return null;
238
+ }
239
+
240
+ public function __set($var, $value)
241
+ {
242
+ if (is_string($value)) {
243
+ $value = trim($value);
244
+ }
245
+ if (isset($this->properties[$var])) {
246
+ if ($value === null) {
247
+ $value = '';
248
+ }
249
+ if($this->properties[$var]['value'] !== $value) {
250
+ $this->properties[$var]['value'] = $value;
251
+ $this->store();
252
+ }
253
+ }
254
+ }
255
+
256
+ public function toArray($withRules = false)
257
+ {
258
+ $out = array();
259
+ foreach($this as $k=>$v) {
260
+ $out[$k] = $withRules ? $v : $v['value'];
261
+ }
262
+ return $out;
263
+ }
264
+
265
+ }
lib/Mage/Connect/Converter.php ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Magento
5
+ *
6
+ * NOTICE OF LICENSE
7
+ *
8
+ * This source file is subject to the Open Software License (OSL 3.0)
9
+ * that is bundled with this package in the file LICENSE.txt.
10
+ * It is also available through the world-wide-web at this URL:
11
+ * http://opensource.org/licenses/osl-3.0.php
12
+ * If you did not receive a copy of the license and are unable to
13
+ * obtain it through the world-wide-web, please send an email
14
+ * to license@magentocommerce.com so we can send you a copy immediately.
15
+ *
16
+ * DISCLAIMER
17
+ *
18
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
19
+ * versions in the future. If you wish to customize Magento for your
20
+ * needs please refer to http://www.magentocommerce.com for more information.
21
+ *
22
+ * @category Mage
23
+ * @package Mage_Connect
24
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
25
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
26
+ */
27
+
28
+ /**
29
+ * Class for convertiong old magento PEAR packages to new one
30
+ *
31
+ * @category Mage
32
+ * @package Mage_Connect
33
+ * @author Magento Core Team <core@magentocommerce.com>
34
+ */
35
+
36
+ final class Mage_Connect_Converter
37
+ {
38
+ protected $_archiver;
39
+
40
+ /**
41
+ *
42
+ * @return Mage_Archive
43
+ */
44
+ public function arc()
45
+ {
46
+ if(!$this->_archiver) {
47
+ $this->_archiver = new Mage_Archive();
48
+ }
49
+ return $this->_archiver;
50
+ }
51
+
52
+ public function newPackage()
53
+ {
54
+ return new Mage_Connect_Package();
55
+ }
56
+
57
+ /**
58
+ *
59
+ * @return Pear_Package_Parser_v2
60
+ */
61
+ public function oldPackageReader()
62
+ {
63
+ return new Pear_Package_Parser_v2();
64
+ }
65
+
66
+
67
+ public function __construct()
68
+ {
69
+
70
+ }
71
+
72
+
73
+ public function convertChannelName($channel)
74
+ {
75
+ return str_replace("connect.magentocommerce.com/", "", $channel);
76
+ }
77
+
78
+ /**
79
+ * Convert package dependencies - urls - by ref
80
+ * @param array $deps ref to array
81
+ * @return void
82
+ */
83
+ public function convertPackageDependencies($oldDeps)
84
+ {
85
+ $out = array();
86
+ if(empty($oldDeps['required']['package'])) {
87
+ return $out;
88
+ }
89
+ $deps = $oldDeps['required']['package'];
90
+ if(!isset($deps[0])) {
91
+ $deps = array($deps);
92
+ }
93
+ for($i=0, $c=count($deps); $i<$c; $i++) {
94
+ $deps[$i]['min_version'] = isset($deps[$i]['min']) ? $deps[$i]['min'] : false;
95
+ $deps[$i]['max_version'] = isset($deps[$i]['max']) ? $deps[$i]['max'] : false;
96
+ $deps[$i]['channel'] = $this->convertChannelName($deps[$i]['channel']);
97
+ $out[] = $deps[$i];
98
+ }
99
+
100
+ return $out;
101
+ }
102
+
103
+ public function convertLicense($oldLicense)
104
+ {
105
+ if(is_scalar($oldLicense)) {
106
+ return $oldLicense;
107
+ }
108
+ return array($oldLicense['_content'], $oldLicense['attribs']['uri']);
109
+ }
110
+
111
+ public function convertMaintainers($maintainers)
112
+ {
113
+ if(!is_array($maintainers) || !count($maintainers)) {
114
+ return array();
115
+ }
116
+ $out = array();
117
+ foreach($maintainers as $row) {
118
+ $out[] = array('name'=>$row['name'], 'email'=>$row['email'], 'user'=>'auto-converted');
119
+ }
120
+ return $out;
121
+ }
122
+
123
+ protected $fileMap = array();
124
+
125
+
126
+ /**
127
+ * Conver pear package object to magento object
128
+ * @param Pear_Package_V2 $pearObject
129
+ * @return Mage_Connect_Package
130
+ */
131
+
132
+ public function convertPackageObject($pearObject)
133
+ {
134
+ $data = array();
135
+ $mageObject = $this->newPackage();
136
+
137
+
138
+
139
+ $map = array (
140
+ 'name' => null,
141
+ 'version' => array('getterArgs' => array('release')
142
+ ),
143
+ 'package_deps' => array( 'getter'=>'getDependencies',
144
+ 'converter'=>'convertPackageDependencies',
145
+ 'setter'=>'setDependencyPackages',
146
+ ),
147
+ 'stability' => array( 'getter'=>'getState',
148
+ 'getterArgs' => array('release'),
149
+ ),
150
+ 'license' => array( 'getterArgs' => array(true),
151
+ 'converter' => 'convertLicense',
152
+ 'noArrayWrap' => true,
153
+ ),
154
+ 'summary' => null,
155
+ 'description' => null,
156
+ 'notes' => null,
157
+ 'date' => null,
158
+ 'time' => null,
159
+ 'authors' => array( 'converter' => 'convertMaintainers',
160
+ 'getter' => 'getMaintainers',
161
+ ),
162
+ 'channel' => array( 'converter' => 'convertChannelName',
163
+
164
+ ),
165
+
166
+ );
167
+ foreach($map as $field=>$rules) {
168
+
169
+ if(empty($rules)) {
170
+ $rules = array('setter'=> '', 'getter'=> '');
171
+ }
172
+
173
+ if(empty($rules['getter'])) {
174
+ $rules['getter'] = 'get'. ucfirst($field);
175
+ }
176
+
177
+ $useSetter = empty($rules['noSetter']);
178
+ $useGetter = empty($rules['noGetter']);
179
+
180
+
181
+ if(empty($rules['setter'])) {
182
+ $rules['setter'] = 'set'. ucfirst($field);
183
+ }
184
+ if(empty($rules['getterArgs'])) {
185
+ $rules['getterArgs'] = array();
186
+ } elseif(!is_array($rules['getterArgs'])) {
187
+ throw new Exception("Invalid 'getterArgs' for '{$field}', should be array");
188
+ }
189
+
190
+ if($useGetter && !method_exists($pearObject, $rules['getter'])) {
191
+ $mName = get_class($pearObject)."::".$rules['getter'];
192
+ throw new Exception('No getter method exists: '.$mName);
193
+ }
194
+
195
+ if($useSetter && !method_exists($mageObject, $rules['setter'])) {
196
+ $mName = get_class($mageObject)."::".$rules['setter'];
197
+ throw new Exception('No setter method exists: '.$mName);
198
+ }
199
+
200
+ $useConverter = !empty($rules['converter']);
201
+
202
+ if($useConverter && false === method_exists($this, $rules['converter'])) {
203
+ $mName = get_class($this)."::".$rules['converter'];
204
+ throw new Exception('No converter method exists: '.$mName);
205
+ }
206
+
207
+ if($useGetter) {
208
+ $getData = call_user_func_array(array($pearObject, $rules['getter']), $rules['getterArgs']);
209
+ } else {
210
+ $getData = array();
211
+ }
212
+
213
+ if($useConverter) {
214
+ $args = array();
215
+ if(!$useGetter && !$useSetter) {
216
+ $args = array($pearObject, $mageObject);
217
+ } elseif(!$useSetter) {
218
+ $args = array($mageObject, $getData);
219
+ } else {
220
+ $args = array($getData);
221
+ }
222
+ $getData = call_user_func_array(array($this, $rules['converter']), $args);
223
+ }
224
+
225
+ $noWrap = !empty($rules['noArrayWrap']);
226
+ if($useSetter) {
227
+ $setData = call_user_func_array(array($mageObject, $rules['setter']), $noWrap ? $getData : array($getData));
228
+ }
229
+ }
230
+ return $mageObject;
231
+ }
232
+
233
+ /**
234
+ * Convert PEAR package to Magento package
235
+ * @param string $sourceFile path to PEAR .tgz
236
+ * @param string|false $destFile path to newly-created Magento .tgz, false to specify auto
237
+ * @return bool
238
+ */
239
+ public function convertPearToMage($sourceFile, $destFile = false)
240
+ {
241
+ try {
242
+ if(!file_exists($sourceFile)) {
243
+ throw new Exception("File doesn't exist: {$sourceFile}");
244
+ }
245
+ $arc = $this->arc();
246
+ $tempDir = "tmp-".basename($sourceFile).uniqid();
247
+ $outDir = "out-".basename($sourceFile).uniqid();
248
+ $outDir = rtrim($outDir, "\\/");
249
+ Mage_System_Dirs::mkdirStrict($outDir);
250
+ Mage_System_Dirs::mkdirStrict($tempDir);
251
+
252
+ $result = $arc->unpack($sourceFile, $tempDir);
253
+ if(!$result) {
254
+ throw new Exception("'{$sourceFile}' was not unpacked");
255
+ }
256
+
257
+ $result = rtrim($result, "\\/");
258
+ $packageXml = $result . DS . "package.xml";
259
+ if(!file_exists($packageXml)) {
260
+ throw new Exception("No package.xml found inside '{$sourceFile}'");
261
+ }
262
+
263
+ $reader = $this->oldPackageReader();
264
+ $data = file_get_contents($packageXml);
265
+
266
+ $pearObject = $reader->parsePackage($data, $packageXml);
267
+ $mageObject = $this->convertPackageObject($pearObject);
268
+ if(!$mageObject->validate()) {
269
+ throw new Exception("Package validation failed.\n". implode("\n", $mageObject->getErrors()));
270
+ }
271
+
272
+ /**
273
+ * Calculate destination file if false
274
+ */
275
+ if(false === $destFile) {
276
+ $pathinfo = pathinfo($sourceFile);
277
+ $destFile = $pathinfo['dirname'] . DS .$pathinfo['filename'].'-converted';
278
+ if(isset($pathinfo['extension'])) {
279
+ $destFile .= ".".$pathinfo['extension'];
280
+ }
281
+ }
282
+
283
+ $target = new Mage_Connect_Package_Target("target.xml");
284
+ $targets = $target->getTargets();
285
+ $mageObject->setTarget($target);
286
+ $validRoles = array_keys($targets);
287
+ $data = $pearObject->getFilelist();
288
+ $pathSource = dirname($pearObject->getPackageFile()).DS.$pearObject->getName()."-".$pearObject->getVersion();
289
+
290
+ $filesToDo = array();
291
+ foreach($data as $file =>$row) {
292
+ $name = $row['name'];
293
+ $role = $row['role'];
294
+ if(!in_array($role, $validRoles)) {
295
+ $role = 'mage';
296
+ }
297
+ $baseName = ltrim($targets[$role], "\\/.");
298
+ $baseName = rtrim($baseName, "\\/");
299
+ $sourceFile = $pathSource.DS.$name;
300
+ $targetFile = $outDir . DS . $baseName . DS. $name;
301
+ if(file_exists($sourceFile)) {
302
+ Mage_System_Dirs::mkdirStrict(dirname($targetFile));
303
+ $copy = @copy($sourceFile, $targetFile);
304
+ if(false === $copy) {
305
+ throw new Exception("Cannot copy '{$sourceFile}' to '{$targetFile}'");
306
+ }
307
+ }
308
+ $filesToDo[] = array ('name'=> $name, 'role'=>$role);
309
+ }
310
+ $cwd = getcwd();
311
+ @chdir($outDir);
312
+ foreach($filesToDo as $fileToDo) {
313
+ $mageObject->addContent($fileToDo['name'], $fileToDo['role']);
314
+ }
315
+ $mageObject->save(getcwd());
316
+ @chdir($cwd);
317
+ $filename = $outDir. DS . $mageObject->getReleaseFilename().".tgz";
318
+ if(@file_exists($targetArchive)) {
319
+ @unlink($targetArchive);
320
+ }
321
+ Mage_System_Dirs::mkdirStrict(dirname($destFile));
322
+ $copy = @copy($filename, $destFile);
323
+ if(false === $copy) {
324
+ throw new Exception("Cannot copy '{$filename}' to '{$targetArchive}'");
325
+ }
326
+ Mage_System_Dirs::rm($tempDir);
327
+ Mage_System_Dirs::rm($outDir);
328
+
329
+ } catch (Exception $e) {
330
+ throw $e;
331
+ }
332
+ return $destFile;
333
+ }
334
+
335
+
336
+
337
+ }
lib/Mage/Connect/Frontend.php ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ class Mage_Connect_Frontend
28
+ {
29
+
30
+ /**
31
+ * Silent flag. If set no output is produced to view.
32
+ * Should be used in derived classes.
33
+ * @var bool
34
+ */
35
+ protected $_silent = false;
36
+
37
+ /**
38
+ * Capture mode. If set command output should be collected
39
+ * by derived class impplementation
40
+ * @var bool
41
+ */
42
+ protected $_capture = false;
43
+
44
+
45
+ /**
46
+ * push/pop variable for capture
47
+ * @var array
48
+ */
49
+ protected $_captureSaved = array();
50
+
51
+ /**
52
+ * push/pop variable for silent
53
+ * @var array
54
+ */
55
+ protected $_silentSaved = array();
56
+
57
+ /**
58
+ * Errors list
59
+ * @var array
60
+ */
61
+ protected $_errors = array();
62
+
63
+ /**
64
+ * Add error to errors list
65
+ * @param mixed $data
66
+ * @return void
67
+ */
68
+ public function addError($data)
69
+ {
70
+ $this->_errors[] = $data;
71
+ }
72
+
73
+ /**
74
+ * Get errors, clear errors list with first param
75
+ * @param bool $clear
76
+ * @return array
77
+ */
78
+ public function getErrors($clear = true)
79
+ {
80
+ if(!$clear) {
81
+ return $this->_errors;
82
+ }
83
+ $out = $this->_errors;
84
+ $this->clearErrors();
85
+ return $out;
86
+ }
87
+
88
+ /**
89
+ * Clear errors array
90
+ * @return void
91
+ */
92
+ public function clearErrors()
93
+ {
94
+ $this->_errors = array();
95
+ }
96
+
97
+ /**
98
+ * Are there any errros?
99
+ * @return bool
100
+ */
101
+ public function hasErrors()
102
+ {
103
+ return count($this->_errors) != 0;
104
+ }
105
+
106
+ /**
107
+ * Error processing
108
+ * @param string $command
109
+ * @param stting $message
110
+ * @return void
111
+ */
112
+ public function doError($command, $message)
113
+ {
114
+ $this->addError(array($command, $message));
115
+ }
116
+
117
+ /**
118
+ * Save capture state
119
+ * @return Mage_Connect_Frontend
120
+ */
121
+ public function pushCapture()
122
+ {
123
+ array_push($this->_captureSaved, $this->_capture);
124
+ return $this;
125
+ }
126
+
127
+ /**
128
+ * Restore capture state
129
+ * @return Mage_Connect_Frontend
130
+ */
131
+ public function popCapture()
132
+ {
133
+ $this->_capture = array_pop($this->_captureSaved);
134
+ return $this;
135
+ }
136
+
137
+ /**
138
+ * Set capture mode
139
+ * @param bool $arg true by default
140
+ * @return Mage_Connect_Frontend
141
+ */
142
+ public function setCapture($arg = true)
143
+ {
144
+ $this->_capture = $arg;
145
+ return $this;
146
+ }
147
+
148
+ /**
149
+ * Getter for capture mode
150
+ * @return bool
151
+ */
152
+ public function isCapture()
153
+ {
154
+ return $this->_capture;
155
+ }
156
+
157
+ /**
158
+ * Log stub
159
+ * @param $msg
160
+ * @return
161
+ */
162
+ public function log($msg)
163
+ {
164
+
165
+ }
166
+
167
+ /**
168
+ * Ouptut method
169
+ * @param array $data
170
+ * @return void
171
+ */
172
+ public function output($data)
173
+ {
174
+
175
+ }
176
+
177
+ /**
178
+ * Get instance of derived class
179
+ *
180
+ * @param $class CLI for example will produce Mage_Connect_Frontend_CLI
181
+ * @return object
182
+ */
183
+ public static function getInstance($class)
184
+ {
185
+ $class = __CLASS__."_".$class;
186
+ return new $class();
187
+ }
188
+
189
+ /**
190
+ * Get output if capture mode set
191
+ * Clear prevoius if needed
192
+ * @param bool $clearPrevious
193
+ * @return mixed
194
+ */
195
+ public function getOutput($clearPrevious = true)
196
+ {
197
+
198
+ }
199
+
200
+
201
+ /**
202
+ * Save silent mode
203
+ * @return Mage_Connect_Frontend
204
+ */
205
+ public function pushSilent()
206
+ {
207
+ array_push($this->_silentSaved, $this->_silent);
208
+ return $this;
209
+ }
210
+
211
+ /**
212
+ * Restore silent mode
213
+ * @return Mage_Connect_Frontend
214
+ */
215
+ public function popSilent()
216
+ {
217
+ $this->_silent = array_pop($this->_silentSaved);
218
+ return $this;
219
+ }
220
+
221
+ /**
222
+ * Set silent mode
223
+ * @param bool $value
224
+ * @return Mage_Connect_Frontend
225
+ */
226
+ public function setSilent($value = true)
227
+ {
228
+ $this->_silent = (bool) $value;
229
+ return $this;
230
+ }
231
+
232
+ /**
233
+ * Is silent mode?
234
+ * @return bool
235
+ */
236
+ public function isSilent()
237
+ {
238
+ return (bool) $this->_silent;
239
+ }
240
+
241
+ /**
242
+ * Method for ask client about rewrite all files.
243
+ *
244
+ * @param $string
245
+ */
246
+ public function confirm($string)
247
+ {
248
+
249
+ }
250
+ }
251
+
lib/Mage/Connect/Frontend/CLI.php ADDED
@@ -0,0 +1,441 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * CLI Frontend implementation
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+
35
+ class Mage_Connect_Frontend_CLI
36
+ extends Mage_Connect_Frontend
37
+ {
38
+
39
+ /**
40
+ * Collected output
41
+ * @var array
42
+ */
43
+ protected $_output = array();
44
+
45
+ /**
46
+ * Output error
47
+ * @param string $command
48
+ * @param string $message
49
+ * @return void
50
+ */
51
+ public function doError($command, $message)
52
+ {
53
+ parent::doError($command, $message);
54
+ $this->writeln("Error: ");
55
+ $this->writeln("$command: $message");
56
+ }
57
+
58
+
59
+ /**
60
+ * Output config help
61
+ * @param array $data
62
+ * @return void
63
+ */
64
+ public function outputConfigHelp($data)
65
+ {
66
+ foreach($data['data'] as $k=>$v) {
67
+ if(is_scalar($v)) {
68
+ $this->writeln($v);
69
+ } elseif(is_array($v)) {
70
+ $this->writeln(implode(": ", $v));
71
+ }
72
+ }
73
+ }
74
+
75
+
76
+ /**
77
+ * Output info
78
+ * @param array $data
79
+ * @return void
80
+ */
81
+ public function outputRemoteInfo($data)
82
+ {
83
+ if(!is_array($data['releases'])) {
84
+ return;
85
+ }
86
+ foreach ($data['releases'] as $r) {
87
+ $this->writeln(implode(" ", $r));
88
+ }
89
+ }
90
+
91
+
92
+ public function detectMethodByType($type)
93
+ {
94
+ $defaultMethod = "output";
95
+ $methodMap = array(
96
+ 'list-upgrades'=> 'outputUpgrades',
97
+ 'list-available' => 'outputChannelsPackages',
98
+ 'list-installed' => 'writeInstalledList',
99
+ 'package-dependencies' => 'outputPackageDeps',
100
+ 'list-files' => 'outputPackageContents',
101
+ 'config-help' => 'outputConfigHelp',
102
+ 'info' => 'outputRemoteInfo',
103
+ 'config-show' => 'outputConfig',
104
+ 'install' => 'outputInstallResult',
105
+ 'install-file' => 'outputInstallResult',
106
+ 'upgrade' => 'outputInstallResult',
107
+ 'upgrade-all' => 'outputInstallResult',
108
+ 'uninstall' => 'outputDeleted',
109
+ 'list-channels' => 'outputListChannels',
110
+ );
111
+ if(isset($methodMap[$type])) {
112
+ return $methodMap[$type];
113
+ }
114
+ return $defaultMethod;
115
+ }
116
+
117
+
118
+ public function outputDeleted($data)
119
+ {
120
+ if(!count($data['data'])) {
121
+ return;
122
+ }
123
+ $this->writeln($data['title']);
124
+ foreach($data['data'] as $row) {
125
+ $this->writeln("$row[0]/$row[1]");
126
+ }
127
+ }
128
+
129
+ public function outputListChannels($data)
130
+ {
131
+ $this->writeln($data['title']);
132
+
133
+ $channels =& $data['data'][Mage_Connect_Singleconfig::K_CHAN];
134
+ foreach($channels as $name => $v) {
135
+ $this->writeln("$name: {$v[Mage_Connect_Singleconfig::K_URI]}");
136
+ }
137
+ $aliases =& $data['data'][Mage_Connect_Singleconfig::K_CHAN_ALIAS];
138
+ if(count($aliases)) {
139
+ $this->writeln();
140
+ $this->writeln($data['title_aliases']);
141
+ foreach($aliases as $k=>$v) {
142
+ $this->writeln("$k => $v");
143
+ }
144
+ }
145
+
146
+ }
147
+
148
+ /**
149
+ * Output install result
150
+ * @param array $data
151
+ * @return void
152
+ */
153
+ public function outputInstallResult($data)
154
+ {
155
+ if(isset($data['title'])) {
156
+ $title = trim($data['title'])." ";
157
+ } else {
158
+ $title = '';
159
+ }
160
+ foreach($data['assoc'] as $row) {
161
+ $this->printf("%s%s/%s %s\n", $title, $row['channel'], $row['name'], $row['version']);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Ouptut package contents
167
+ * @param array $data
168
+ * @return void
169
+ */
170
+ public function outputPackageContents($data)
171
+ {
172
+ $this->writeln($data['title']);
173
+ foreach($data['data'] as $file) {
174
+ $this->writeln($file);
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Output package dependencies
180
+ * @param $data
181
+ * @return void
182
+ */
183
+ public function outputPackageDeps($data)
184
+ {
185
+ $title = $data['title'];
186
+ $this->writeln($title);
187
+ foreach($data['data'] as $package) {
188
+ $this->printf("%-20s %-20s %-20s %-20s\n", $package['channel'], $package['name'], $package['min'], $package['max']);
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Ouptut channel packages
194
+ * @param $data
195
+ * @return unknown_type
196
+ */
197
+ public function outputChannelsPackages($data)
198
+ {
199
+ foreach($data['data'] as $channelInfo) {
200
+ $title =& $channelInfo['title'];
201
+ $packages =& $channelInfo['packages'];
202
+ $this->writeln($title);
203
+ foreach($packages as $name=>$package) {
204
+ $releases =& $package['releases'];
205
+ $tmp = array();
206
+ foreach($releases as $ver=>$state) {
207
+ $tmp[] = "$ver $state";
208
+ }
209
+ $tmp = implode(',',$tmp);
210
+ $this->writeln($name.": ".$tmp);
211
+ }
212
+ }
213
+ }
214
+
215
+
216
+ /**
217
+ * Make output
218
+ *
219
+ * @param array $data
220
+ * @return void
221
+ */
222
+
223
+ public function output($data)
224
+ {
225
+ $capture = $this->isCapture();
226
+ if($capture) {
227
+ $this->_output[] = $data;
228
+ return;
229
+ }
230
+
231
+ if(is_array($data)) {
232
+ foreach($data as $type=>$params) {
233
+ $method = $this->detectMethodByType($type);
234
+ if($method) {
235
+ $this->$method($params);
236
+ } else {
237
+ $this->writeln(__METHOD__." handler not found for {$type}");
238
+ }
239
+ }
240
+ } else {
241
+ $this->writeln($data);
242
+ }
243
+ }
244
+
245
+
246
+ /**
247
+ * Detailed package info
248
+ * @param Mage_Connect_Package $package
249
+ * @return void
250
+ */
251
+ public function outputPackage($package)
252
+ {
253
+ $fields = array(
254
+ 'Name'=>'name',
255
+ 'Version'=>'version',
256
+ 'Stability'=>'stability',
257
+ 'Description' => 'description',
258
+ 'Date' => 'date',
259
+ 'Authors' => 'authors',
260
+ );
261
+
262
+ foreach($fields as $title => $fld) {
263
+ $method = "get".ucfirst($fld);
264
+ $data = $package->$method();
265
+ if(empty($data)) {
266
+ continue;
267
+ }
268
+ $this->write($title.": ");
269
+ if(is_array($data)) {
270
+ $this->write(print_r($data,true));
271
+ } else {
272
+ $this->write($data);
273
+ }
274
+ $this->writeln('');
275
+ }
276
+ }
277
+
278
+
279
+ /**
280
+ * Write channels list
281
+ * @param array $data
282
+ * @return void
283
+ */
284
+ public function writeChannelsList($data)
285
+ {
286
+ $this->writeln("Channels available: ");
287
+ $this->writeln("===================");
288
+ $out = $data['byName'];
289
+ ksort($out);
290
+ foreach($out as $k=>$v) {
291
+ $this->printf ("%-20s %-20s\n", $k, $v);
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Write installed list
297
+ * @param array $data
298
+ * @return void
299
+ */
300
+ public function writeInstalledList($data)
301
+ {
302
+ $totalCount = 0;
303
+ foreach($data['data'] as $channel=>$packages) {
304
+ $title = sprintf($data['channel-title'], $channel);
305
+ $c = count($packages);
306
+ $totalCount += $c;
307
+ if(!$c) {
308
+ continue;
309
+ }
310
+ $this->writeln($title);
311
+ foreach($packages as $name=>$row) {
312
+ $this->printf("%-20s %-20s\n", $name, $row['version']." ".$row['stability']);
313
+ }
314
+ }
315
+ if($totalCount === 0) {
316
+ $this->writeln("No installed packages");
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Output commands list
322
+ * @param array $data
323
+ * @return void
324
+ */
325
+ public function outputCommandList($data)
326
+ {
327
+ $this->writeln("Connect commands available:");
328
+ $this->writeln("===========================");
329
+ foreach ($data as $k=>$v) {
330
+ $this->printf ("%-20s %-20s\n", $k, $v['summary']);
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Output config
336
+ * @param array $data
337
+ * @return void
338
+ */
339
+ public function outputConfig($data)
340
+ {
341
+ foreach($data['data'] as $name=>$row) {
342
+ $value = $row['value'] === '' ? "<not set>" : strval($row['value']);
343
+ $this->printf("%-30s %-20s %-20s\n", $row['prompt'], $name, $value);
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Output config variable
349
+ * @param string $key
350
+ * @param string $value
351
+ * @return void
352
+ */
353
+ public function outputConfigVariable($key, $value)
354
+ {
355
+ if($value === '') {
356
+ $value = '<not set>';
357
+ }
358
+ $this->writeln("Config variable '{$key}': {$value}");
359
+ }
360
+
361
+ /**
362
+ * Write data and "\n" afterwards
363
+ * @param string $data
364
+ * @return void
365
+ */
366
+ public function writeln($data = '')
367
+ {
368
+ $this->write($data."\n");
369
+ }
370
+
371
+
372
+ /**
373
+ * get output, clear if needed
374
+ *
375
+ * @param bool $clearPrevoius optional, true by default
376
+ * @return array
377
+ */
378
+ public function getOutput($clearPrevious = true)
379
+ {
380
+ $out = $this->_output;
381
+ if($clearPrevious) {
382
+ $this->_output = array();
383
+ }
384
+ return $out;
385
+ }
386
+
387
+ /**
388
+ * Write data to console
389
+ * @param string $data
390
+ * @return void
391
+ */
392
+ public function write($data)
393
+ {
394
+ if($this->isSilent()) {
395
+ return;
396
+ }
397
+ print $data;
398
+ }
399
+
400
+ /**
401
+ * Output printf-stlye formatted string and args
402
+ * @return void
403
+ */
404
+ public function printf()
405
+ {
406
+ $args = func_get_args();
407
+ $this->write(call_user_func_array('sprintf', $args));
408
+ }
409
+
410
+ /**
411
+ * Readline from console
412
+ * @return string
413
+ */
414
+ public function readln()
415
+ {
416
+ $out = "";
417
+ $key = fgetc(STDIN);
418
+ while ($key!="\n") {
419
+ $out.= $key;
420
+ $key = fread(STDIN, 1);
421
+ }
422
+ return $out;
423
+ }
424
+
425
+ /**
426
+ * Output upgrades
427
+ * @param array $data
428
+ * @return void
429
+ */
430
+ public function outputUpgrades($data)
431
+ {
432
+ foreach($data['data'] as $chan => $packages) {
433
+ $this->writeln("Updates for ".$chan.": ");
434
+ foreach($packages as $name => $data) {
435
+ $this->writeln(" $name: {$data['from']} => {$data['to']}");
436
+ }
437
+ }
438
+ }
439
+
440
+ }
441
+
lib/Mage/Connect/Ftp.php ADDED
@@ -0,0 +1,494 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with remote FTP server
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Connect_Ftp
35
+ {
36
+
37
+ /**
38
+ * Connection object
39
+ * @var resource
40
+ */
41
+ protected $_conn = false;
42
+
43
+ public function __construct()
44
+ {
45
+
46
+ }
47
+
48
+ /**
49
+ * Check connected, throw exception if not
50
+ * @throws Exception
51
+ * @return void
52
+ */
53
+ protected function checkConnected()
54
+ {
55
+ if(!$this->_conn) {
56
+ throw new Exception(__CLASS__." - no connection established with server");
57
+ }
58
+ }
59
+
60
+ /**
61
+ * ftp_mkdir wrapper
62
+ * @param stin$name
63
+ * @return unknown_type
64
+ */
65
+ public function mdkir($name)
66
+ {
67
+ $this->checkConnected();
68
+ return @ftp_mkdir($this->_conn, $name);
69
+ }
70
+
71
+
72
+
73
+ /**
74
+ * Make dir recursive
75
+ * @param string $path
76
+ * @param int $mode
77
+ */
78
+ public function mkdirRecursive($path, $mode = 0777)
79
+ {
80
+ $this->checkConnected();
81
+ $dir = split("/", $path);
82
+ $path= "";
83
+ $ret = true;
84
+ for ($i=0; $i < count($dir); $i++) {
85
+ $path .= "/" .$dir[$i];
86
+ if(!@ftp_chdir($this->_conn, $path)) {
87
+ @ftp_chdir($this->_conn,"/");
88
+ if(!@ftp_mkdir($this->_conn,$path)) {
89
+ $ret=false;
90
+ break;
91
+ } else {
92
+ @ftp_chmod($this->_conn, $mode, $path);
93
+ }
94
+ }
95
+ }
96
+ return $ret;
97
+ }
98
+
99
+
100
+ /**
101
+ * Try to login to server
102
+ * @param string $login
103
+ * @param string $password
104
+ * @throws Exception on invalid login credentials
105
+ * @return bool
106
+ */
107
+ public function login($login = "anonymous", $password = "test@gmail.com")
108
+ {
109
+ $this->checkConnected();
110
+ $res = @ftp_login($this->_conn, $login, $password);
111
+ if(!$res) {
112
+ throw new Exception("Invalid login credentials");
113
+ }
114
+ return $res;
115
+ }
116
+
117
+ /**
118
+ * Validate connection string
119
+ * @param string $string
120
+ * @throws Exception
121
+ * @return string
122
+ */
123
+ public function validateConnectionString($string)
124
+ {
125
+ $data = @parse_url($string);
126
+ if(false === $data) {
127
+ throw new Exception("Connection string invalid: '{$string}'");
128
+ }
129
+ if($data['scheme'] != 'ftp') {
130
+ throw new Exception("Support for scheme unsupported: '{$data['scheme']}'");
131
+ }
132
+ return $data;
133
+ }
134
+
135
+ /**
136
+ * Connect to server using connect string
137
+ * Connection string: ftp://user:pass@server:port/path
138
+ * user,pass,port,path are optional parts
139
+ * @param string $string
140
+ *
141
+ * @param $timeout
142
+ * @return unknown_type
143
+ */
144
+ public function connect($string, $timeout = 900)
145
+ {
146
+ $params = $this->validateConnectionString($string);
147
+ $port = isset($params['port']) ? intval($params['port']) : 21;
148
+
149
+ $this->_conn = ftp_connect($params['host'], $port, $timeout);
150
+
151
+ if(!$this->_conn) {
152
+ throw new Exception("Cannot connect to host: {$params['host']}");
153
+ }
154
+ if(isset($params['user']) && isset($params['pass'])) {
155
+ $this->login($params['user'], $params['pass']);
156
+ } else {
157
+ $this->login();
158
+ }
159
+ if(isset($params['path'])) {
160
+ if(!$this->chdir($params['path'])) {
161
+ throw new Exception ("Cannot chdir after login to: {$params['path']}");
162
+ }
163
+ }
164
+ }
165
+
166
+ /**
167
+ * ftp_fput wrapper
168
+ * @param string $remoteFile
169
+ * @param resource $handle
170
+ * @param int $mode FTP_BINARY | FTP_ASCII
171
+ * @param int $startPos
172
+ * @return bool
173
+ */
174
+
175
+ public function fput($remoteFile, $handle, $mode = FTP_BINARY, $startPos = 0)
176
+ {
177
+ $this->checkConnected();
178
+ return @ftp_fput($this->_conn, $remoteFile, $handle, $mode, $startPos);
179
+ }
180
+
181
+ /**
182
+ * ftp_put wrapper
183
+ * @param string $remoteFile
184
+ * @param string $localFile
185
+ * @param int $mode FTP_BINARY | FTP_ASCII
186
+ * @param int $startPos
187
+ * @return bool
188
+ */
189
+ public function put($remoteFile, $localFile, $mode = FTP_BINARY, $startPos = 0)
190
+ {
191
+ $this->checkConnected();
192
+ return ftp_put($this->_conn, $remoteFile, $localFile, $mode, $startPos);
193
+ }
194
+
195
+
196
+ /**
197
+ * Get current working directory
198
+ * @return mixed
199
+ */
200
+ public function getcwd()
201
+ {
202
+ $d = $this->raw("pwd");
203
+ $data = explode(" ", $d[0], 3);
204
+ if(empty($data[1])) {
205
+ return false;
206
+ }
207
+ if(intval($data[0]) != 257) {
208
+ return false;
209
+ }
210
+ $out = trim($data[1], '"');
211
+ if($out !== "/") {
212
+ $out = rtrim($out, "/");
213
+ }
214
+ return $out;
215
+ }
216
+
217
+ /**
218
+ * ftp_raw wrapper
219
+ * @param string $cmd
220
+ * @return mixed
221
+ */
222
+ public function raw($cmd)
223
+ {
224
+ $this->checkConnected();
225
+ return @ftp_raw($this->_conn, $cmd);
226
+ }
227
+
228
+
229
+ /**
230
+ * Upload local file to remote
231
+ *
232
+ * Can be used for relative and absoulte remote paths
233
+ * Relative: use chdir before calling this
234
+ *
235
+ * @param srting $remote
236
+ * @param string $local
237
+ * @param int $dirMode
238
+ * @param int $ftpMode
239
+ * @return unknown_type
240
+ */
241
+ public function upload($remote, $local, $dirMode = 0777, $ftpMode = FTP_BINARY)
242
+ {
243
+ $this->checkConnected();
244
+
245
+ if(!file_exists($local)) {
246
+ throw new Exception("Local file doesn't exist: {$localFile}");
247
+ }
248
+ if(!is_readable($local)) {
249
+ throw new Exception("Local file is not readable: {$localFile}");
250
+ }
251
+ if(is_dir($local)) {
252
+ throw new Exception("Directory given instead of file: {$localFile}");
253
+ }
254
+
255
+ $globalPathMode = substr($remote, 0, 1) == "/";
256
+ $dirname = dirname($remote);
257
+ $cwd = $this->getcwd();
258
+ if(false === $cwd) {
259
+ throw new Exception("Server returns something awful on PWD command");
260
+ }
261
+
262
+ if(!$globalPathMode) {
263
+ $dirname = $cwd."/".$dirname;
264
+ $remote = $cwd."/".$remote;
265
+ }
266
+ $res = $this->mkdirRecursive($dirname, $dirMode);
267
+ $this->chdir($cwd);
268
+
269
+ if(!$res) {
270
+ return false;
271
+ }
272
+ return $this->put($remote, $local, $ftpMode);
273
+ }
274
+
275
+ /**
276
+ * Download remote file to local machine
277
+ * @param string $remote
278
+ * @param string $local
279
+ * @param int $ftpMode FTP_BINARY|FTP_ASCII
280
+ * @return bool
281
+ */
282
+ public function download($remote, $local, $ftpMode = FTP_BINARY)
283
+ {
284
+ $this->checkConnected();
285
+ return $this->get($local, $remote, $ftpMode);
286
+ }
287
+
288
+
289
+ /**
290
+ * ftp_pasv wrapper
291
+ * @param bool $pasv
292
+ * @return bool
293
+ */
294
+ public function pasv($pasv)
295
+ {
296
+ $this->checkConnected();
297
+ return @ftp_pasv($this->_conn, (bool) $pasv);
298
+ }
299
+
300
+ /**
301
+ * Close FTP connection
302
+ * @return void
303
+ */
304
+ public function close()
305
+ {
306
+ if($this->_conn) {
307
+ @ftp_close($this->_conn);
308
+ }
309
+ }
310
+
311
+ /**
312
+ * ftp_chmod wrapper
313
+ * @param $mode
314
+ * @param $remoteFile
315
+ * @return bool
316
+ */
317
+ public function chmod($mode, $remoteFile)
318
+ {
319
+ $this->checkConnected();
320
+ return @ftp_chmod($this->_conn, $mode, $remoteFile);
321
+ }
322
+
323
+ /**
324
+ * ftp_chdir wrapper
325
+ * @param string $dir
326
+ * @return bool
327
+ */
328
+ public function chdir($dir)
329
+ {
330
+ $this->checkConnected();
331
+ return @ftp_chdir($this->_conn, $dir);
332
+ }
333
+
334
+ /**
335
+ * ftp_cdup wrapper
336
+ * @return bool
337
+ */
338
+ public function cdup()
339
+ {
340
+ $this->checkConnected();
341
+ return @ftp_cdup($this->_conn);
342
+ }
343
+
344
+ /**
345
+ * ftp_get wrapper
346
+ * @param string $localFile
347
+ * @param string $remoteFile
348
+ * @param int $fileMode FTP_BINARY | FTP_ASCII
349
+ * @param int $resumeOffset
350
+ * @return bool
351
+ */
352
+ public function get($localFile, $remoteFile, $fileMode = FTP_BINARY, $resumeOffset = 0)
353
+ {
354
+ $remoteFile = $this->correctFilePath($remoteFile);
355
+ $this->checkConnected();
356
+ return @ftp_get($this->_conn, $localFile, $remoteFile, $fileMode, $resumeOffset);
357
+ }
358
+
359
+ /**
360
+ * ftp_nlist wrapper
361
+ * @param string $dir
362
+ * @return bool
363
+ */
364
+ public function nlist($dir = "/")
365
+ {
366
+ $this->checkConnected();
367
+ $dir = $this->correctFilePath($dir);
368
+ return @ftp_nlist($this->_conn, $dir);
369
+ }
370
+
371
+ /**
372
+ * ftp_rawlist wrapper
373
+ * @param string $dir
374
+ * @param bool $recursive
375
+ * @return mixed
376
+ */
377
+ public function rawlist( $dir = "/", $recursive = false )
378
+ {
379
+ $this->checkConnected();
380
+ $dir = $this->correctFilePath($dir);
381
+ return @ftp_rawlist($this->_conn, $dir, $recursive);
382
+ }
383
+
384
+
385
+ /**
386
+ * Convert byte count to float KB/MB format
387
+ * @param int $bytes
388
+ * @return string
389
+ */
390
+ public static function byteconvert($bytes)
391
+ {
392
+ $symbol = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
393
+ $exp = floor( log($bytes) / log(1024) );
394
+ return sprintf( '%.2f ' . $symbol[ $exp ], ($bytes / pow(1024, floor($exp))) );
395
+ }
396
+
397
+ /**
398
+ * Chmod string "-rwxrwxrwx" to "777" converter
399
+ * @param string $chmod
400
+ * @return string
401
+ */
402
+ public static function chmodnum($chmod)
403
+ {
404
+ $trans = array('-' => '0', 'r' => '4', 'w' => '2', 'x' => '1');
405
+ $chmod = substr(strtr($chmod, $trans), 1);
406
+ $array = str_split($chmod, 3);
407
+ return array_sum(str_split($array[0])) . array_sum(str_split($array[1])) . array_sum(str_split($array[2]));
408
+ }
409
+
410
+
411
+ public function fileExists($path, $excludeIfIsDir = true)
412
+ {
413
+ $path = $this->correctFilePath($path);
414
+ $globalPathMode = substr($path, 0, 1) == "/";
415
+
416
+ $file = basename($path);
417
+ $dir = $globalPathMode ? dirname($path) : $this->getcwd()."/".$path;
418
+ $data = $this->ls($dir);
419
+ foreach($data as $row) {
420
+ if($file == $row['name']) {
421
+ if($excludeIfIsDir && $row['dir']) {
422
+ continue;
423
+ }
424
+ return true;
425
+ }
426
+ }
427
+ return false;
428
+ }
429
+
430
+
431
+ /**
432
+ * Get directory contents in PHP array
433
+ * @param string $dir
434
+ * @param bool $recursive
435
+ * @return array
436
+ */
437
+ public function ls($dir = "/", $recursive = false)
438
+ {
439
+ $dir= $this->correctFilePath($dir);
440
+ $rawfiles = (array) $this->rawlist($dir, $recursive);
441
+ $structure = array();
442
+ $arraypointer = &$structure;
443
+ foreach ($rawfiles as $rawfile) {
444
+ if ($rawfile[0] == '/') {
445
+ $paths = array_slice(explode('/', str_replace(':', '', $rawfile)), 1);
446
+ $arraypointer = &$structure;
447
+ foreach ($paths as $path) {
448
+ foreach ($arraypointer as $i => $file) {
449
+ if ($file['name'] == $path) {
450
+ $arraypointer = &$arraypointer[ $i ]['children'];
451
+ break;
452
+ }
453
+ }
454
+ }
455
+ } elseif(!empty($rawfile)) {
456
+ $info = preg_split("/[\s]+/", $rawfile, 9);
457
+ $arraypointer[] = array(
458
+ 'name' => $info[8],
459
+ 'dir' => $info[0]{0} == 'd',
460
+ 'size' => (int) $info[4],
461
+ 'chmod' => self::chmodnum($info[0]),
462
+ 'rawdata' => $info,
463
+ 'raw' => $rawfile
464
+ );
465
+ }
466
+ }
467
+ return $structure;
468
+ }
469
+
470
+
471
+ /**
472
+ * Correct file path
473
+ * @param $str
474
+ * @return string
475
+ */
476
+ public function correctFilePath($str)
477
+ {
478
+ $str = str_replace("\\", "/", $str);
479
+ $str = preg_replace("/^.\//", "", $str);
480
+ return $str;
481
+ }
482
+
483
+ public function delete($file)
484
+ {
485
+ $this->checkConnected();
486
+ $file = $this->correctFilePath($file);
487
+ return @ftp_delete($this->_conn, $file);
488
+ }
489
+
490
+
491
+
492
+
493
+
494
+ }
lib/Mage/Connect/Loader.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class for loader which using in the Rest
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Connect_Loader
35
+ {
36
+
37
+ /**
38
+ * Factory for HTTP client
39
+ * @param string/false $protocol 'curl'/'socket' or false for auto-detect
40
+ * @return Mage_HTTP_Client/Mage_Connect_Loader_Ftp
41
+ */
42
+ public static function getInstance($protocol='')
43
+ {
44
+ if ($protocol == 'ftp') {
45
+ return new Mage_Connect_Loader_Ftp();
46
+ } else {
47
+ return Mage_HTTP_Client::getInstance();
48
+ }
49
+ }
50
+
51
+ }
lib/Mage/Connect/Loader/Ftp.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class for ftp loader which using in the Rest
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Connect_Loader_Ftp
35
+ {
36
+
37
+ const TEMPORARY_DIR = 'var/package/tmp';
38
+
39
+ const FTP_USER = 'magconnect';
40
+
41
+ const FTP_PASS = '4SyTUxPts0o2';
42
+
43
+ /**
44
+ * Object of Ftp
45
+ *
46
+ * @var Mage_Connect_Ftp
47
+ */
48
+ protected $_ftp = null;
49
+
50
+ /**
51
+ * Response body
52
+ * @var string
53
+ */
54
+ protected $_responseBody = '';
55
+
56
+ /**
57
+ * Response status
58
+ * @var int
59
+ */
60
+ protected $_responseStatus = 0;
61
+
62
+ /**
63
+ * Constructor
64
+ */
65
+ public function __construct()
66
+ {
67
+ $this->_ftp = new Mage_Connect_Ftp();
68
+ }
69
+
70
+ public function getFtp()
71
+ {
72
+ return $this->_ftp;
73
+ }
74
+
75
+ /**
76
+ * Retrieve file from URI
77
+ *
78
+ * @param mixed $uri
79
+ * @return bool
80
+ */
81
+ public function get($uri)
82
+ {
83
+ $remoteFile = basename($uri);
84
+ $uri = dirname($uri);
85
+ $uri = str_replace('http://', '', $uri);
86
+ $uri = str_replace('ftp://', '', $uri);
87
+ $uri = self::FTP_USER.":".self::FTP_PASS."@".$uri;
88
+ $this->getFtp()->connect("ftp://".$uri);
89
+ $this->getFtp()->pasv(true);
90
+ $localFile = self::TEMPORARY_DIR.DS.time().".xml";
91
+
92
+ if ($this->getFtp()->get($localFile, $remoteFile)) {
93
+ $this->_responseBody = file_get_contents($localFile);
94
+ $this->_responseStatus = 200;
95
+ }
96
+ @unlink($localFile);
97
+ $this->getFtp()->close();
98
+ return $out;
99
+ }
100
+
101
+ /**
102
+ * Get response status code
103
+ *
104
+ * @return string
105
+ */
106
+ public function getStatus()
107
+ {
108
+ return $this->_responseStatus;
109
+ }
110
+
111
+ /**
112
+ * put your comment there...
113
+ *
114
+ * @return string
115
+ */
116
+ public function getBody()
117
+ {
118
+ return $this->_responseBody;
119
+ }
120
+
121
+ }
lib/Mage/Connect/Package.php ADDED
@@ -0,0 +1,1486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with Magento Connect packages
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Connect_Package
35
+ {
36
+ /*
37
+ * Current version of magento connect package format
38
+ */
39
+ const PACKAGE_VERSION_2X = '2';
40
+
41
+ /*
42
+ * Previous version of magento connect package format
43
+ */
44
+ const PACKAGE_VERSION_1X = '1';
45
+
46
+ /**
47
+ * Contain SimpleXMLElement for composing document.
48
+ *
49
+ * @var SimpleXMLElement
50
+ */
51
+ protected $_packageXml;
52
+
53
+ /**
54
+ * Internal cache
55
+ *
56
+ * @var array
57
+ */
58
+ protected $_authors;
59
+
60
+ /**
61
+ * Internal cache
62
+ *
63
+ * @var array
64
+ */
65
+ protected $_contents;
66
+
67
+ /**
68
+ * Internal cache
69
+ *
70
+ * @var array
71
+ */
72
+ protected $_hashContents;
73
+
74
+ /**
75
+ * Internal cache
76
+ *
77
+ * @var array
78
+ */
79
+ protected $_compatible;
80
+
81
+ /**
82
+ * Internal cache
83
+ *
84
+ * @var array
85
+ */
86
+ protected $_dependencyPhpExtensions;
87
+
88
+ /**
89
+ * Internal cache
90
+ *
91
+ * @var array
92
+ */
93
+ protected $_dependencyPackages;
94
+
95
+ /**
96
+ * A helper object that can read from a package archive
97
+ *
98
+ * @var Mage_Connect_Package_Reader
99
+ */
100
+ protected $_reader;
101
+
102
+ /**
103
+ * A helper object that can create and write to a package archive
104
+ *
105
+ * @var Mage_Connect_Package_Writer
106
+ */
107
+ protected $_writer;
108
+
109
+ /**
110
+ * Validator object
111
+ *
112
+ * @var Mage_Connect_Validator
113
+ */
114
+ protected $_validator = null;
115
+
116
+ /**
117
+ * Validation errors
118
+ *
119
+ * @var array
120
+ */
121
+ protected $_validationErrors = array();
122
+
123
+ /**
124
+ * Object with target
125
+ *
126
+ * @var Mage_Connect_Package_Target
127
+ */
128
+ protected $_target = null;
129
+
130
+ /**
131
+ * Creates a package object (empty, or from existing archive, or from package definition xml)
132
+ *
133
+ * @param null|string|resource $source
134
+ */
135
+ public function __construct($source=null)
136
+ {
137
+ libxml_use_internal_errors(true);
138
+
139
+ if (is_string($source)) {
140
+ // check what's in the string (a package definition or a package filename)
141
+ if (0 === strpos($source, "<?xml")) {
142
+ // package definition xml
143
+ $this->_init($source);
144
+ } elseif (is_file($source) && is_readable($source)) {
145
+ // package archive filename
146
+ $this->_loadFile($source);
147
+ } else {
148
+ throw new Mage_Exception('Invalid package source');
149
+ }
150
+ } elseif (is_resource($source)) {
151
+ $this->_loadResource($source);
152
+ } elseif (is_null($source)) {
153
+ $this->_init();
154
+ } else {
155
+ throw new Mage_Exception('Invalid package source');
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Initializes an empty package object
161
+ *
162
+ * @param null|string $definition optional package definition xml
163
+ * @return Mage_Connect_Package
164
+ */
165
+ protected function _init($definition=null)
166
+ {
167
+
168
+ if (!is_null($definition)) {
169
+ $this->_packageXml = simplexml_load_string($definition);
170
+ } else {
171
+ $packageXmlStub = <<<END
172
+ <?xml version="1.0"?>
173
+ <package>
174
+ <name />
175
+ <version />
176
+ <stability />
177
+ <license />
178
+ <channel />
179
+ <extends />
180
+ <summary />
181
+ <description />
182
+ <notes />
183
+ <authors />
184
+ <date />
185
+ <time />
186
+ <contents />
187
+ <compatible />
188
+ <dependencies />
189
+ </package>
190
+ END;
191
+ $this->_packageXml = simplexml_load_string($packageXmlStub);
192
+ }
193
+ return $this;
194
+ }
195
+
196
+ /**
197
+ * Loads a package from specified file
198
+ *
199
+ * @param string $filename
200
+ * @return Mage_Connect_Package
201
+ */
202
+ protected function _loadFile($filename='')
203
+ {
204
+ if (is_null($this->_reader)) {
205
+ $this->_reader = new Mage_Connect_Package_Reader($filename);
206
+ }
207
+ $content = $this->_reader->load();
208
+ $this->_packageXml = simplexml_load_string($content);
209
+ return $this;
210
+ }
211
+
212
+ /**
213
+ * Creates a package and saves it
214
+ *
215
+ * @param string $path
216
+ * @return Mage_Connect_Package
217
+ */
218
+ public function save($path)
219
+ {
220
+ $this->validate();
221
+ $path = rtrim($path, "\\/") . DS;
222
+ $this->_savePackage($path);
223
+ return $this;
224
+ }
225
+
226
+ /**
227
+ * Creates a package that is compatible with the previous version of Magento Connect Manager and saves it
228
+ *
229
+ * @param string $path
230
+ * @return Mage_Connect_Package
231
+ */
232
+ public function saveV1x($path)
233
+ {
234
+ $this->validate();
235
+ $path = rtrim($path, "\\/") . DS;
236
+ $this->_savePackageV1x($path);
237
+ return $this;
238
+ }
239
+
240
+ /**
241
+ * Creates a package archive and saves it to specified path
242
+ *
243
+ * @param string $path
244
+ * @return Mage_Connect_Package
245
+ */
246
+ protected function _savePackage($path)
247
+ {
248
+ $fileName = $this->getReleaseFilename();
249
+ if (is_null($this->_writer)) {
250
+ $this->_writer = new Mage_Connect_Package_Writer($this->getContents(), $path.$fileName);
251
+ }
252
+ $this->_writer
253
+ ->composePackage()
254
+ ->addPackageXml($this->getPackageXml())
255
+ ->archivePackage();
256
+ return $this;
257
+ }
258
+
259
+ /**
260
+ * Creates a package archive and saves it to specified path
261
+ * Package is compatible with the previous version of magento Connect Manager
262
+ *
263
+ * @param string $path
264
+ * @return Mage_Connect_Package
265
+ */
266
+ protected function _savePackageV1x($path)
267
+ {
268
+ $fileName = $this->getReleaseFilename();
269
+ $writer = new Mage_Connect_Package_Writer($this->getContents(), $path.$fileName);
270
+ $writer->composePackageV1x($this->getContentsV1x())
271
+ ->addPackageXml($this->_getPackageXmlV1x())
272
+ ->archivePackage();
273
+ return $this;
274
+ }
275
+
276
+ /**
277
+ * Generate package xml that is compatible with first version of Magento Connect Manager
278
+ * Function uses already generated package xml to import data
279
+ *
280
+ * @return string
281
+ */
282
+ protected function _getPackageXmlV1x()
283
+ {
284
+ $newPackageXml = $this->_packageXml;
285
+ $packageXmlV1xStub = <<<END
286
+ <?xml version="1.0" encoding="UTF-8"?>
287
+ <package packagerversion="1.9.1"
288
+ version="2.0"
289
+ xmlns="http://pear.php.net/dtd/package-2.0"
290
+ xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
291
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
292
+ xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd" />
293
+ END;
294
+ $packageXmlV1x = simplexml_load_string($packageXmlV1xStub);
295
+ // Note: The previous version of MCM requires precise node order in package.xml file
296
+ $packageXmlV1x->addChild('name', (string)$newPackageXml->name);
297
+ $packageXmlV1x->addChild('channel', Mage::helper('connect')->convertChannelToV1x((string)$newPackageXml->channel));
298
+ $packageXmlV1x->addChild('summary', (string)$newPackageXml->summary);
299
+ $packageXmlV1x->addChild('description', (string)$newPackageXml->description);
300
+ // Import authors
301
+ foreach ($newPackageXml->authors->author as $author) {
302
+ $leadNode = $packageXmlV1x->addChild('lead');
303
+ $leadNode->addChild('name', (string)$author->name);
304
+ $leadNode->addChild('user', (string)$author->user);
305
+ $leadNode->addChild('email', (string)$author->email);
306
+ $leadNode->addChild('active', 'yes');
307
+ }
308
+ // Import date and time
309
+ $packageXmlV1x->addChild('date', (string)$newPackageXml->date);
310
+ $packageXmlV1x->addChild('time', (string)$newPackageXml->time);
311
+ // Import version
312
+ $versionNode = $packageXmlV1x->addChild('version');
313
+ $versionNode->addChild('release', (string)$newPackageXml->version);
314
+ $versionNode->addChild('api', (string)$newPackageXml->version);
315
+ // Import stability
316
+ $stabilityNode = $packageXmlV1x->addChild('stability');
317
+ $stabilityNode->addChild('release', (string)$newPackageXml->stability);
318
+ $stabilityNode->addChild('api', (string)$newPackageXml->stability);
319
+ // Import license
320
+ $licenseNode = $packageXmlV1x->addChild('license', (string)$newPackageXml->license);
321
+ if ($newPackageXml->license['uri']) {
322
+ $licenseNode->addAttribute('uri', (string)$newPackageXml->license['uri']);
323
+ }
324
+ $packageXmlV1x->addChild('notes', (string)$newPackageXml->notes);
325
+ // Import content
326
+ $conentsRootDirNode = $packageXmlV1x->addChild('contents')->addChild('dir');
327
+ $conentsRootDirNode->addAttribute('name', '/');
328
+ foreach ($newPackageXml->contents->target as $target) {
329
+ $role = (string)$target['name'];
330
+ $this->_mergeContentsToV1x($conentsRootDirNode, $target, $role);
331
+ }
332
+ // Import dependencies
333
+ $requiredDependenciesNode = $packageXmlV1x->addChild('dependencies')->addChild('required');
334
+ $requiredDependenciesPhpNode = $requiredDependenciesNode->addChild('php');
335
+ $requiredDependenciesPhpNode->addChild('min', (string)$newPackageXml->dependencies->required->php->min);
336
+ $requiredDependenciesPhpNode->addChild('max', (string)$newPackageXml->dependencies->required->php->max);
337
+ $requiredDependenciesNode->addChild('pearinstaller')->addChild('min','1.6.2');
338
+ // Handle packages
339
+ foreach ($newPackageXml->dependencies->required->package as $package) {
340
+ $packageNode = $requiredDependenciesNode->addChild('package');
341
+ $packageNode->addChild('name', (string)$package->name);
342
+ // Convert channel to previous version format
343
+ $channel = (string)$package->channel;
344
+ $channel = Mage::helper('connect')->convertChannelToV1x($channel);
345
+ $packageNode->addChild('channel', $channel);
346
+ $minVersion = (string)$package->min;
347
+ if ($minVersion) {
348
+ $packageNode->addChild('min', $minVersion);
349
+ }
350
+ $maxVersion = (string)$package->max;
351
+ if ($maxVersion) {
352
+ $packageNode->addChild('max', $maxVersion);
353
+ }
354
+ }
355
+ // Handle extensions
356
+ foreach ($newPackageXml->dependencies->required->extension as $extension) {
357
+ $extensionNode = $requiredDependenciesNode->addChild('extension');
358
+ $extensionNode->addChild('name', (string)$extension->name);
359
+ $minVersion = (string)$extension->min;
360
+ if ($minVersion) {
361
+ $extensionNode->addChild('min', $minVersion);
362
+ }
363
+ $maxVersion = (string)$extension->max;
364
+ if ($maxVersion) {
365
+ $extensionNode->addChild('max', $maxVersion);
366
+ }
367
+ }
368
+ $packageXmlV1x->addChild('phprelease');
369
+
370
+ return $packageXmlV1x->asXML();
371
+ }
372
+
373
+ /**
374
+ * Merge contents of source element into destination element
375
+ * Function converts <file/> and <dir/> nodes into format that is compatible
376
+ * with previous version of Magento Connect Manager
377
+ *
378
+ * @param SimpleXMLElement $destination
379
+ * @param SimpleXMLElement $source
380
+ * @param string $role
381
+ * @return Mage_Connect_Package
382
+ */
383
+ protected function _mergeContentsToV1x($destination, $source, $role)
384
+ {
385
+ foreach ($source->children() as $child) {
386
+ if ($child->getName() == 'dir') {
387
+ $newDestination = $destination;
388
+ if ($child['name'] != '.') {
389
+ $directoryElement = $destination->addChild('dir');
390
+ $directoryElement->addAttribute('name', $child['name']);
391
+ $newDestination = $directoryElement;
392
+ }
393
+ $this->_mergeContentsToV1x($newDestination, $child, $role);
394
+ } elseif ($child->getName() == 'file') {
395
+ $fileElement = $destination->addChild('file');
396
+ $fileElement->addAttribute('name', $child['name']);
397
+ $fileElement->addAttribute('md5sum', $child['hash']);
398
+ $fileElement->addAttribute('role', $role);
399
+ }
400
+ }
401
+ return $this;
402
+ }
403
+
404
+ /**
405
+ * Retrieve Target object
406
+ *
407
+ * @return Mage_Connect_Package_Target
408
+ */
409
+ protected function getTarget()
410
+ {
411
+ if (!$this->_target instanceof Mage_Connect_Package_Target) {
412
+ $this->_target = new Mage_Connect_Package_Target();
413
+ }
414
+ return $this->_target;
415
+ }
416
+
417
+ public function setTarget($arg)
418
+ {
419
+ if ($arg instanceof Mage_Connect_Package_Target) {
420
+ $this->_target = $arg;
421
+ }
422
+ }
423
+
424
+ /* Mutators */
425
+
426
+ /**
427
+ * Puts value to name
428
+ *
429
+ * @param string $name
430
+ * @return Mage_Connect_Package
431
+ */
432
+ public function setName($name)
433
+ {
434
+ $this->_packageXml->name = $name;
435
+ return $this;
436
+ }
437
+
438
+ /**
439
+ * Puts value to <channel />
440
+ *
441
+ * @param string $channel
442
+ * @return Mage_Connect_Package
443
+ */
444
+ public function setChannel($channel)
445
+ {
446
+ $this->_packageXml->channel = $channel;
447
+ return $this;
448
+ }
449
+
450
+ /**
451
+ * Puts value to <summary />
452
+ *
453
+ * @param string $summary
454
+ * @return Mage_Connect_Package
455
+ */
456
+ public function setSummary($summary)
457
+ {
458
+ $this->_packageXml->summary = $summary;
459
+ return $this;
460
+ }
461
+
462
+ /**
463
+ * Puts value to <description />
464
+ *
465
+ * @param string $description
466
+ * @return Mage_Connect_Package
467
+ */
468
+ public function setDescription($description)
469
+ {
470
+ $this->_packageXml->description = $description;
471
+ return $this;
472
+ }
473
+
474
+ /**
475
+ * Puts value to <authors />
476
+ *
477
+ * array(
478
+ * array('name'=>'Name1', 'user'=>'User1', 'email'=>'email1@email.com'),
479
+ * array('name'=>'Name2', 'user'=>'User2', 'email'=>'email2@email.com'),
480
+ * );
481
+ *
482
+ * @param array $authors
483
+ * @return Mage_Connect_Package
484
+ */
485
+ public function setAuthors($authors)
486
+ {
487
+ $this->_authors = $authors;
488
+ foreach ($authors as $_author) {
489
+ $this->addAuthor($_author['name'], $_author['user'], $_author['email']);
490
+ }
491
+ return $this;
492
+ }
493
+
494
+ /**
495
+ * Add author to <authors/>
496
+ *
497
+ * @param string $name
498
+ * @param string $user
499
+ * @param string $email
500
+ * @return Mage_Connect_Package
501
+ */
502
+ public function addAuthor($name=null, $user=null, $email=null)
503
+ {
504
+ $this->_authors[] = array(
505
+ 'name' =>$name,
506
+ 'user' =>$user,
507
+ 'email'=>$email
508
+ );
509
+ $author = $this->_packageXml->authors->addChild('author');
510
+ $author->addChild('name', $name);
511
+ $author->addChild('user', $user);
512
+ $author->addChild('email', $email);
513
+ return $this;
514
+ }
515
+
516
+ /**
517
+ * Puts value to <date/>. Format should be Y-M-D.
518
+ *
519
+ * @param string $date
520
+ * @return Mage_Connect_Package
521
+ */
522
+ public function setDate($date)
523
+ {
524
+ $this->_packageXml->date = $date;
525
+ return $this;
526
+ }
527
+
528
+ /**
529
+ * Puts value to <time />. Format should be H:i:s.
530
+ *
531
+ * @param string $time
532
+ * @return Mage_Connect_Package
533
+ */
534
+ public function setTime($time)
535
+ {
536
+ $this->_packageXml->time = $time;
537
+ return $this;
538
+ }
539
+
540
+ /**
541
+ * Puts value to <version/>. Format should be X.Y.Z.
542
+ *
543
+ * @param string $version
544
+ * @return Mage_Connect_Package
545
+ */
546
+ public function setVersion($version)
547
+ {
548
+ $this->_packageXml->version = $version;
549
+ return $this;
550
+ }
551
+
552
+ /**
553
+ * Puts value to <stability/>. It can be alpha, beta, devel and stable.
554
+ *
555
+ * @param string $stability
556
+ * @return Mage_Connect_Package
557
+ */
558
+ public function setStability($stability)
559
+ {
560
+ $this->_packageXml->stability = $stability;
561
+ return $this;
562
+ }
563
+
564
+ /**
565
+ * Puts value to <license/>, also method can used for set attribute URI.
566
+ *
567
+ * @param string $license
568
+ * @param string $uri
569
+ * @return Mage_Connect_Package
570
+ */
571
+ public function setLicense($license, $uri=null)
572
+ {
573
+ $this->_packageXml->license = $license;
574
+ if ($uri) {
575
+ $this->_packageXml->license['uri'] = $uri;
576
+ }
577
+ return $this;
578
+ }
579
+
580
+ /**
581
+ * Puts value to <notes/>.
582
+ *
583
+ * @param string $notes
584
+ * @return Mage_Connect_Package
585
+ */
586
+ public function setNotes($notes)
587
+ {
588
+ $this->_packageXml->notes = $notes;
589
+ return $this;
590
+ }
591
+
592
+ /**
593
+ * Retrieve SimpleXMLElement node by xpath. If it absent, create new.
594
+ * For comparing nodes method uses attribute "name" in each nodes.
595
+ * If attribute "name" is same for both nodes, nodes are same.
596
+ *
597
+ * @param string $tag
598
+ * @param SimpleXMLElement $parent
599
+ * @param string $name
600
+ * @return SimpleXMLElement
601
+ */
602
+ protected function _getNode($tag, $parent, $name='')
603
+ {
604
+ $found = false;
605
+ foreach ($parent->xpath($tag) as $_node) {
606
+ if ($_node['name'] == $name) {
607
+ $node = $_node;
608
+ $found = true;
609
+ break;
610
+ }
611
+ }
612
+ if (!$found) {
613
+ $node = $parent->addChild($tag);
614
+ if ($name) {
615
+ $node->addAttribute('name', $name);
616
+ }
617
+ }
618
+ return $node;
619
+ }
620
+
621
+ /**
622
+ * Add directory or file to <contents />.
623
+ *
624
+ * @param string $path Path to directory or file
625
+ * @param string $targetName Target name.
626
+ * @param string $hash MD5 hash of the file
627
+ * @return Mage_Connect_Package
628
+ */
629
+ public function addContent($path, $targetName)
630
+ {
631
+ $found = false;
632
+ $parent = $this->_getNode('target', $this->_packageXml->contents, $targetName);
633
+ $source = str_replace('\\', '/', $path);
634
+ $directories = explode('/', dirname($source));
635
+ foreach ($directories as $directory) {
636
+ $parent = $this->_getNode('dir', $parent, $directory);
637
+ }
638
+ $fileName = basename($source);
639
+ if ($fileName!='') {
640
+ $fileNode = $parent->addChild('file');
641
+ $fileNode->addAttribute('name', $fileName);
642
+ $targetDir = $this->getTarget()->getTargetUri($targetName);
643
+ $hash = md5_file($targetDir.DS.$path);
644
+ $fileNode->addAttribute('hash', $hash);
645
+ }
646
+ return $this;
647
+ }
648
+
649
+ /**
650
+ * Add directory recursively (with subdirectory and file).
651
+ * Exclude and Include can be add using Regular Expression.
652
+ *
653
+ * @param string $targetName Target name
654
+ * @param string $targetDir Path for target name
655
+ * @param string $path Path to directory
656
+ * @param string $exclude Exclude
657
+ * @param string $include Include
658
+ * @return Mage_Connect_Package
659
+ */
660
+ public function addContentDir($targetName, $path, $exclude=null, $include=null)
661
+ {
662
+ $targetDir = $this->getTarget()->getTargetUri($targetName);
663
+ $targetDirLen = strlen($targetDir . DS);
664
+ //get all subdirectories and files.
665
+ $entries = @glob($targetDir. DS . $path . DS . "{,.}*", GLOB_BRACE);
666
+ if (!empty($entries)) {
667
+ foreach ($entries as $entry) {
668
+ $filePath = substr($entry, $targetDirLen);
669
+ if (!empty($include) && !preg_match($include, $filePath)) {
670
+ continue;
671
+ }
672
+ if (!empty($exclude) && preg_match($exclude, $filePath)) {
673
+ continue;
674
+ }
675
+ if (is_dir($entry)) {
676
+ $baseName = basename($entry);
677
+ if (in_array($baseName, array('.', '..', '.svn'))) {
678
+ continue;
679
+ }
680
+ //for subdirectory call method recursively
681
+ $this->addContentDir($targetName, $filePath, $exclude, $include);
682
+ } elseif (is_file($entry)) {
683
+ $this->addContent($filePath, $targetName);
684
+ }
685
+ }
686
+ }
687
+ return $this;
688
+ }
689
+
690
+ /**
691
+ * Add value to <compatible />.
692
+ *
693
+ * @param string $packageName
694
+ * @param string $channel
695
+ * @param string $minVersion
696
+ * @param string $maxVersion
697
+ * @return Mage_Connect_Package
698
+ */
699
+ public function addCompatible($packageName, $channel, $minVersion, $maxVersion)
700
+ {
701
+ $package = $this->_packageXml->compatible->addChild('package');
702
+ $package->addChild('name', $packageName);
703
+ $package->addChild('channel', $channel);
704
+ $package->addChild('min', $minVersion);
705
+ $package->addChild('max', $maxVersion);
706
+ return $this;
707
+ }
708
+
709
+ /**
710
+ * Set dependency from php version.
711
+ *
712
+ * @param string $minVersion
713
+ * @param string $maxVersion
714
+ * @return Mage_Connect_Package
715
+ */
716
+ public function setDependencyPhpVersion($minVersion, $maxVersion)
717
+ {
718
+ $parent = $this->_packageXml->dependencies;
719
+ $parent = $this->_getNode('required', $parent);
720
+ $parent = $this->_getNode('php', $parent);
721
+ $parent->addChild('min', $minVersion);
722
+ $parent->addChild('max', $maxVersion);
723
+ return $this;
724
+ }
725
+
726
+
727
+ /**
728
+ * Check PHP version restriction
729
+ * @param $phpVersion PHP_VERSION by default
730
+ * @return true | string
731
+ */
732
+ public function checkPhpVersion()
733
+ {
734
+ $min = $this->getDependencyPhpVersionMin();
735
+ $max = $this->getDependencyPhpVersionMax();
736
+
737
+ $minOk = $min? version_compare(PHP_VERSION, $min, ">=") : true;
738
+ $maxOk = $max? version_compare(PHP_VERSION, $max, "<=") : true;
739
+
740
+ if(!$minOk || !$maxOk) {
741
+ $err = "requires PHP version ";
742
+ if($min && $max) {
743
+ $err .= " >= $min and <= $max ";
744
+ } elseif($min) {
745
+ $err .= " >= $min ";
746
+ } elseif($max) {
747
+ $err .= " <= $max ";
748
+ }
749
+ $err .= " current is: ".PHP_VERSION;
750
+ return $err;
751
+ }
752
+ return true;
753
+ }
754
+
755
+
756
+ /**
757
+ * Check PHP extensions availability
758
+ * @throws Exceptiom on failure
759
+ * @return true | array
760
+ */
761
+ public function checkPhpDependencies()
762
+ {
763
+ $errors = array();
764
+ foreach($this->getDependencyPhpExtensions() as $dep)
765
+ {
766
+ if(!extension_loaded($dep['name'])) {
767
+ $errors[] = $dep;
768
+ }
769
+ }
770
+ if(count($errors)) {
771
+ return $errors;
772
+ }
773
+ return true;
774
+ }
775
+
776
+
777
+ /**
778
+ * Set dependency from php extensions.
779
+ *
780
+ * $extension has next view:
781
+ * array('curl', 'mysql')
782
+ *
783
+ * @param array|string $extensions
784
+ * @return Mage_Connect_Package
785
+ */
786
+ public function setDependencyPhpExtensions($extensions)
787
+ {
788
+ foreach($extensions as $_extension) {
789
+ $this->addDependencyExtension(
790
+ $_extension['name'],
791
+ $_extension['min_version'],
792
+ $_extension['max_version']
793
+ );
794
+ }
795
+ return $this;
796
+ }
797
+
798
+ /**
799
+ * Set dependency from another packages.
800
+ *
801
+ * $packages should contain:
802
+ * array(
803
+ * array('name'=>'test1', 'channel'=>'test1', 'min_version'=>'0.0.1', 'max_version'=>'0.1.0'),
804
+ * array('name'=>'test2', 'channel'=>'test2', 'min_version'=>'0.0.1', 'max_version'=>'0.1.0'),
805
+ * )
806
+ *
807
+ * @param array $packages
808
+ * @param bool $clear
809
+ * @return Mage_Connect_Package
810
+ */
811
+ public function setDependencyPackages($packages, $clear = false)
812
+ {
813
+ if($clear) {
814
+ unset($this->_packageXml->dependencies->required->package);
815
+ }
816
+
817
+ foreach($packages as $_package) {
818
+
819
+ $filesArrayCondition = isset($_package['files']) && is_array($_package['files']);
820
+ $filesArray = $filesArrayCondition ? $_package['files'] : array();
821
+
822
+ $this->addDependencyPackage(
823
+ $_package['name'],
824
+ $_package['channel'],
825
+ $_package['min_version'],
826
+ $_package['max_version'],
827
+ $filesArray
828
+ );
829
+ }
830
+ return $this;
831
+ }
832
+
833
+
834
+
835
+ /**
836
+ * Add package to dependency packages.
837
+ *
838
+ * @param string $package
839
+ * @param string $channel
840
+ * @param string $minVersion
841
+ * @param string $maxVersion
842
+ * @return Mage_Connect_Package
843
+ */
844
+ public function addDependencyPackage($name, $channel, $minVersion, $maxVersion, $files = array())
845
+ {
846
+ $parent = $this->_packageXml->dependencies;
847
+ $parent = $this->_getNode('required', $parent);
848
+ $parent = $parent->addChild('package');
849
+ $parent->addChild('name', $name);
850
+ $parent->addChild('channel', $channel);
851
+ $parent->addChild('min', $minVersion);
852
+ $parent->addChild('max', $maxVersion);
853
+ if(count($files)) {
854
+ $parent = $parent->addChild('files');
855
+ foreach($files as $row) {
856
+ if(!empty($row['target']) && !empty($row['path'])) {
857
+ $node = $parent->addChild("file");
858
+ $node["target"] = $row['target'];
859
+ $node["path"] = $row['path'];
860
+
861
+ }
862
+ }
863
+ }
864
+ return $this;
865
+ }
866
+
867
+
868
+
869
+ /**
870
+ * Add package to dependency extension.
871
+ *
872
+ * @param string $package
873
+ * @param string $minVersion
874
+ * @param string $maxVersion
875
+ * @return Mage_Connect_Package
876
+ */
877
+ public function addDependencyExtension($name, $minVersion, $maxVersion)
878
+ {
879
+ $parent = $this->_packageXml->dependencies;
880
+ $parent = $this->_getNode('required', $parent);
881
+ $parent = $parent->addChild('extension');
882
+ $parent->addChild('name', $name);
883
+ $parent->addChild('min', $minVersion);
884
+ $parent->addChild('max', $maxVersion);
885
+ return $this;
886
+ }
887
+
888
+ /* Accessors */
889
+
890
+ /**
891
+ * Getter
892
+ *
893
+ * @return string
894
+ */
895
+ public function getName()
896
+ {
897
+ return (string)$this->_packageXml->name;
898
+ }
899
+
900
+ /**
901
+ * Getter
902
+ *
903
+ * @return string
904
+ */
905
+ public function getChannel()
906
+ {
907
+ return (string)$this->_packageXml->channel;
908
+ }
909
+
910
+ /**
911
+ * Getter
912
+ *
913
+ * @return string
914
+ */
915
+ public function getSummary()
916
+ {
917
+ return (string)$this->_packageXml->summary;
918
+ }
919
+
920
+ /**
921
+ * Getter
922
+ *
923
+ * @return string
924
+ */
925
+ public function getDescription()
926
+ {
927
+ return (string)$this->_packageXml->description;
928
+ }
929
+
930
+ /**
931
+ * Get list of authors in associative array.
932
+ *
933
+ * @return array
934
+ */
935
+ public function getAuthors()
936
+ {
937
+ if (is_array($this->_authors)) return $this->_authors;
938
+ $this->_authors = array();
939
+ if(!isset($this->_packageXml->authors->author)) {
940
+ return array();
941
+ }
942
+ foreach ($this->_packageXml->authors->author as $_author) {
943
+ $this->_authors[] = array(
944
+ 'name' => (string)$_author->name,
945
+ 'user' => (string)$_author->user,
946
+ 'email'=> (string)$_author->email
947
+ );
948
+ }
949
+ return $this->_authors;
950
+ }
951
+
952
+ /**
953
+ * Getter
954
+ *
955
+ * @return string
956
+ */
957
+ public function getDate()
958
+ {
959
+ return (string)$this->_packageXml->date;
960
+ }
961
+
962
+ /**
963
+ * Getter
964
+ *
965
+ * @return string
966
+ */
967
+ public function getTime()
968
+ {
969
+ return (string)$this->_packageXml->time;
970
+ }
971
+
972
+ /**
973
+ * Getter
974
+ *
975
+ * @return string
976
+ */
977
+ public function getVersion()
978
+ {
979
+ return (string)$this->_packageXml->version;
980
+ }
981
+
982
+ /**
983
+ * Getter
984
+ *
985
+ * @return string
986
+ */
987
+ public function getStability()
988
+ {
989
+ return (string)$this->_packageXml->stability;
990
+ }
991
+
992
+ /**
993
+ * Getter
994
+ *
995
+ * @return string
996
+ */
997
+ public function getLicense()
998
+ {
999
+ return (string)$this->_packageXml->license;
1000
+ }
1001
+
1002
+ /**
1003
+ * Getter
1004
+ *
1005
+ * @return string
1006
+ */
1007
+ public function getLicenseUri()
1008
+ {
1009
+ return (string)$this->_packageXml->license['uri'];
1010
+ }
1011
+
1012
+ /**
1013
+ * Getter
1014
+ *
1015
+ * @return string
1016
+ */
1017
+ public function getNotes()
1018
+ {
1019
+ return (string)$this->_packageXml->notes;
1020
+ }
1021
+
1022
+ /**
1023
+ * Create list of all files from package.xml
1024
+ *
1025
+ * @return array
1026
+ */
1027
+ public function getContents()
1028
+ {
1029
+ if (is_array($this->_contents)) return $this->_contents;
1030
+ $this->_contents = array();
1031
+ if(!isset($this->_packageXml->contents->target)) {
1032
+ return $this->_contents;
1033
+ }
1034
+ foreach($this->_packageXml->contents->target as $target) {
1035
+ $targetUri = $this->getTarget()->getTargetUri($target['name']);
1036
+ $this->_getList($target, $targetUri);
1037
+ }
1038
+ return $this->_contents;
1039
+ }
1040
+
1041
+ /**
1042
+ * Create list of all files from package.xml compatible with previous version of Magento Connect Manager
1043
+ *
1044
+ * @return array
1045
+ */
1046
+ public function getContentsV1x()
1047
+ {
1048
+ $currentContents = $this->_contents;
1049
+ $this->_contents = array();
1050
+
1051
+ if(!isset($this->_packageXml->contents->target)) {
1052
+ return $this->_contents;
1053
+ }
1054
+ foreach($this->_packageXml->contents->target as $target) {
1055
+ $this->_getList($target, '');
1056
+ }
1057
+ $contents = $this->_contents;
1058
+
1059
+ $this->_contents = $currentContents;
1060
+ return $contents;
1061
+ }
1062
+
1063
+ /**
1064
+ * Helper for getContents(). Create recursively list.
1065
+ *
1066
+ * @param SimpleXMLElement $parent
1067
+ * @param string $path
1068
+ */
1069
+ protected function _getList($parent, $path)
1070
+ {
1071
+ if (count($parent) == 0) {
1072
+ $this->_contents[] = $path;
1073
+ } else {
1074
+ foreach($parent as $_content) {
1075
+ $this->_getList($_content, ($path ? $path . DS : '') . $_content['name']);
1076
+ }
1077
+ }
1078
+ }
1079
+
1080
+ /**
1081
+ * Create list of all files from package.xml with hash
1082
+ *
1083
+ * @return array
1084
+ */
1085
+ public function getHashContents()
1086
+ {
1087
+ if (is_array($this->_hashContents)) return $this->_hashContents;
1088
+ $this->_hashContents = array();
1089
+ if(!isset($this->_packageXml->contents->target)) {
1090
+ return $this->_hashContents;
1091
+ }
1092
+ foreach($this->_packageXml->contents->target as $target) {
1093
+ $targetUri = $this->getTarget()->getTargetUri($target['name']);
1094
+ $this->_getHashList($target, $targetUri);
1095
+ }
1096
+ return $this->_hashContents;
1097
+ }
1098
+
1099
+ /**
1100
+ * Helper for getHashContents(). Create recursively list.
1101
+ *
1102
+ * @param SimpleXMLElement $parent
1103
+ * @param string $path
1104
+ */
1105
+ protected function _getHashList($parent, $path, $hash='')
1106
+ {
1107
+ if (count($parent) == 0) {
1108
+ $this->_hashContents[$path] = $hash;
1109
+ } else {
1110
+ foreach($parent as $_content) {
1111
+ $contentHash = '';
1112
+ if (isset($_content['hash'])) {
1113
+ $contentHash = (string)$_content['hash'];
1114
+ }
1115
+ $this->_getHashList($_content, ($path ? $path . DS : '') . $_content['name'], $contentHash);
1116
+ }
1117
+ }
1118
+ }
1119
+
1120
+ /**
1121
+ * Get compatible packages.
1122
+ *
1123
+ * @return array
1124
+ */
1125
+ public function getCompatible()
1126
+ {
1127
+ if (is_array($this->_compatible)) return $this->_compatible;
1128
+ $this->_compatible = array();
1129
+ if(!isset($this->_packageXml->compatible->package)) {
1130
+ return array();
1131
+ }
1132
+ foreach ($this->_packageXml->compatible->package as $_package) {
1133
+ $this->_compatible[] = array(
1134
+ 'name' => (string)$_package->name,
1135
+ 'channel' => (string)$_package->channel,
1136
+ 'min' => (string)$_package->min,
1137
+ 'max' => (string)$_package->max
1138
+ );
1139
+ }
1140
+ return $this->_compatible;
1141
+ }
1142
+
1143
+ /**
1144
+ * Getter
1145
+ *
1146
+ * @return string
1147
+ */
1148
+ public function getDependencyPhpVersionMin()
1149
+ {
1150
+ if(!isset($this->_packageXml->dependencies->required->php->min)) {
1151
+ return false;
1152
+ }
1153
+ return (string)$this->_packageXml->dependencies->required->php->min;
1154
+ }
1155
+
1156
+ /**
1157
+ * Getter
1158
+ *
1159
+ * @return string
1160
+ */
1161
+ public function getDependencyPhpVersionMax()
1162
+ {
1163
+ if(!isset($this->_packageXml->dependencies->required->php->max)) {
1164
+ return false;
1165
+ }
1166
+ return (string)$this->_packageXml->dependencies->required->php->max;
1167
+ }
1168
+
1169
+ /**
1170
+ * Get list of php extensions.
1171
+ *
1172
+ * @return array
1173
+ */
1174
+ public function getDependencyPhpExtensions()
1175
+ {
1176
+ if (is_array($this->_dependencyPhpExtensions)) return $this->_dependencyPhpExtensions;
1177
+ $this->_dependencyPhpExtensions = array();
1178
+ foreach($this->_packageXml->dependencies->required->extension as $_package) {
1179
+ $this->_dependencyPhpExtensions[] = array(
1180
+ 'name' => (string)$_package->name,
1181
+ 'min' => (string)$_package->min,
1182
+ 'max' => (string)$_package->max,
1183
+ );
1184
+ }
1185
+ return $this->_dependencyPhpExtensions;
1186
+ }
1187
+
1188
+ /**
1189
+ * Get list of dependency packages.
1190
+ *
1191
+ * @return array
1192
+ */
1193
+ public function getDependencyPackages()
1194
+ {
1195
+ $this->_dependencyPackages = array();
1196
+ foreach($this->_packageXml->dependencies->required->package as $_package) {
1197
+ $add = array(
1198
+ 'name' => (string)$_package->name,
1199
+ 'channel' => (string)$_package->channel,
1200
+ 'min' => (string)$_package->min,
1201
+ 'max' => (string)$_package->max,
1202
+ );
1203
+ if(isset($_package->files)) {
1204
+ $add['files'] = array();
1205
+ foreach($_package->files as $node) {
1206
+ if(isset($node->file)) {
1207
+
1208
+ $add['files'][] = array('target' => (string) $node->file['target'], 'path'=> (string) $node->file['path']);
1209
+ }
1210
+ }
1211
+ }
1212
+ $this->_dependencyPackages[] = $add;
1213
+ }
1214
+ return $this->_dependencyPackages;
1215
+ }
1216
+
1217
+
1218
+
1219
+
1220
+ /**
1221
+ * Get string with XML content.
1222
+ *
1223
+ * @return string
1224
+ */
1225
+ public function getPackageXml()
1226
+ {
1227
+ return $this->_packageXml->asXml();
1228
+ }
1229
+
1230
+
1231
+ /**
1232
+ * Validator instance (single)
1233
+ *
1234
+ * @return Mage_Connect_Validator
1235
+ */
1236
+ protected function validator()
1237
+ {
1238
+ if(is_null($this->_validator)) {
1239
+ $this->_validator = new Mage_Connect_Validator();
1240
+ }
1241
+ return $this->_validator;
1242
+ }
1243
+
1244
+ /**
1245
+ * Get validation error strings
1246
+ *
1247
+ * @return array
1248
+ */
1249
+ public function getErrors()
1250
+ {
1251
+ return $this->_validationErrors;
1252
+ }
1253
+
1254
+ /**
1255
+ * Setter for validation errors
1256
+ *
1257
+ * @param array $errors
1258
+ * @return
1259
+ */
1260
+ protected function setErrors(array $errors)
1261
+ {
1262
+ $this->_validationErrors = $errors;
1263
+ }
1264
+
1265
+ /**
1266
+ * Check validation result.
1267
+ * Returns true if package data is invalid.
1268
+ *
1269
+ * @return bool
1270
+ */
1271
+ public function hasErrors()
1272
+ {
1273
+ return count($this->_validationErrors) != 0;
1274
+ }
1275
+
1276
+ /**
1277
+ * Validate package. Errors can be
1278
+ * retreived by calling getErrors();
1279
+ *
1280
+ * @return bool
1281
+ */
1282
+ public function validate()
1283
+ {
1284
+ $v = $this->validator();
1285
+
1286
+ /**
1287
+ * Validation map
1288
+ *
1289
+ * Format:
1290
+ *
1291
+ * 'key' => array(
1292
+ * 'method' => this class method name to call, string, required
1293
+ * 'method_args' => optional args for 'method' call, array, optional
1294
+ * 'v_method' => validator method to call, string, required
1295
+ * 'error' => custom error string when validation fails, optional
1296
+ * if not set, error string fprmatted as "Invalid '$key' specified"
1297
+ * 'v_error_method' => validator method - when called returned error string
1298
+ * prepared by validator, optional,
1299
+ * if not set => see 'error'
1300
+ * 'optional' => optional value, if it's empty validation result ignored
1301
+ *
1302
+ */
1303
+ $validateMap = array(
1304
+ 'name' => array('method' => 'getName',
1305
+ 'v_method' => 'validatePackageName',
1306
+ 'error'=>"Invalid package name, allowed: [a-zA-Z0-9_-] chars"),
1307
+ 'version' => array('method' => 'getVersion',
1308
+ 'v_method' => 'validateVersion',
1309
+ 'error'=>"Invalid version, should be like: x.x.x"),
1310
+ 'stability' => array('method' => 'getStability',
1311
+ 'v_method' => 'validateStability',
1312
+ 'error'=>"Invalid stability"),
1313
+ 'date' => array('method' => 'getDate',
1314
+ 'v_method' => 'validateDate',
1315
+ 'error'=>"Invalid date, should be YYYY-DD-MM"),
1316
+ 'license_uri' => array('method' => 'getLicenseUri',
1317
+ 'v_method' => 'validateLicenseUrl',
1318
+ 'error'=>"Invalid license URL"),
1319
+ 'channel' => array('method' => 'getChannel',
1320
+ 'v_method' => 'validateChannelNameOrUri',
1321
+ 'error'=>"Invalid channel URL"),
1322
+ 'authors' => array('method' => 'getAuthors',
1323
+ 'v_method' => 'validateAuthors',
1324
+ 'v_error_method' => 'getErrors'),
1325
+ 'php_min' => array('method' => 'getDependencyPhpVersionMin',
1326
+ 'v_method' => 'validateVersion',
1327
+ 'error' => 'PHP minimum version invalid',
1328
+ 'optional' => true ),
1329
+ 'php_max' => array('method' => 'getDependencyPhpVersionMax',
1330
+ 'v_method' => 'validateVersion',
1331
+ 'error' => 'PHP maximum version invalid',
1332
+ 'optional' => true ),
1333
+ 'compatible' => array('method' => 'getCompatible',
1334
+ 'v_method' => 'validateCompatible',
1335
+ 'v_error_method' => 'getErrors'),
1336
+
1337
+
1338
+ );
1339
+
1340
+ $errors = array();
1341
+ /**
1342
+ * Iterate validation map
1343
+ */
1344
+ foreach($validateMap as $name=>$data) {
1345
+
1346
+ /**
1347
+ * Check mandatory rules fields
1348
+ */
1349
+ if(!isset($data['method'], $data['v_method'])) {
1350
+ throw new Mage_Exception("Invalid rules specified!");
1351
+ }
1352
+
1353
+ $method = $data['method'];
1354
+ $validatorMethod = $data['v_method'];
1355
+
1356
+ /**
1357
+ * If $optional === false, value is mandatory
1358
+ */
1359
+ $optional = isset($data['optional']) ? (bool) $data['optional'] : false;
1360
+
1361
+ /**
1362
+ * Check for method availability, package
1363
+ */
1364
+ if(!method_exists($this, $method)) {
1365
+ throw new Mage_Exception("Invalid method specified for Package : $method");
1366
+ }
1367
+
1368
+ /**
1369
+ * Check for method availability, validator
1370
+ */
1371
+ if(!method_exists($v, $validatorMethod)) {
1372
+ throw new Mage_Exception("Invalid method specified for Validator : $validatorMethod");
1373
+ }
1374
+
1375
+ /**
1376
+ * If $data['error'] => get error string from $data['error']
1377
+ * Else concatenate "Invalid '{$name}' specified"
1378
+ */
1379
+ $errorString = isset($data['error']) ? $data['error'] : "Invalid '{$name}' specified";
1380
+
1381
+ /**
1382
+ * Additional method args check
1383
+ * array() by default
1384
+ */
1385
+ $methodArgs = isset($data['method_args']) ? $data['method_args'] : array();
1386
+
1387
+ /**
1388
+ * Call package method
1389
+ */
1390
+ $out = @call_user_func_array(array($this, $method), $methodArgs);
1391
+
1392
+ /**
1393
+ * Skip if result is empty and value is optional
1394
+ */
1395
+ if(empty($out) && $optional) {
1396
+ continue;
1397
+ }
1398
+
1399
+ /**
1400
+ * Additional validator arguments, merged with array($out)
1401
+ */
1402
+ $validatorArgs = isset($data['v_args']) ? array_merge(array($out), $data['v_args']) : array($out);
1403
+
1404
+ /**
1405
+ * Get validation result
1406
+ */
1407
+ $result = call_user_func_array(array($v, $validatorMethod), $validatorArgs);
1408
+
1409
+ /**
1410
+ * Skip if validation success
1411
+ */
1412
+ if($result) {
1413
+ continue;
1414
+ }
1415
+
1416
+ /**
1417
+ * From where to get error string?
1418
+ * If validator callback method specified, call it to get errors array
1419
+ * Else get it from $errorString - local error string
1420
+ */
1421
+ $validatorFetchErrorsMethod = isset($data['v_error_method']) ? $data['v_error_method'] : false;
1422
+ if (false !== $validatorFetchErrorsMethod) {
1423
+ $errorString = call_user_func_array(array($v, $validatorFetchErrorsMethod), array());
1424
+ }
1425
+
1426
+ /**
1427
+ * If errors is array => merge
1428
+ * Else append
1429
+ */
1430
+ if(is_array($errorString)) {
1431
+ $errors = array_merge($errors, $errorString);
1432
+ } else {
1433
+ $errors[] = $errorString;
1434
+ }
1435
+ }
1436
+ /**
1437
+ * Set local errors
1438
+ */
1439
+ $this->setErrors($errors);
1440
+ /**
1441
+ * Return true if there's no errors :)
1442
+ */
1443
+ return ! $this->hasErrors();
1444
+ }
1445
+
1446
+ /**
1447
+ * Return package release filename w/o extension
1448
+ * @return string
1449
+ */
1450
+ public function getReleaseFilename()
1451
+ {
1452
+ return $this->getName()."-".$this->getVersion();
1453
+ }
1454
+
1455
+ /**
1456
+ * Return release filepath w/o extension
1457
+ * @return string
1458
+ */
1459
+ public function getRelaseDirFilename()
1460
+ {
1461
+ return $this->getName() . DS . $this->getVersion() . DS . $this->getReleaseFilename();
1462
+ }
1463
+
1464
+ /**
1465
+ * Clear dependencies
1466
+ *
1467
+ * @return Mage_Connect_Package
1468
+ */
1469
+ public function clearDependencies()
1470
+ {
1471
+ $this->_packageXml->dependencies = null;
1472
+ return $this;
1473
+ }
1474
+
1475
+ /**
1476
+ * Clear contents
1477
+ *
1478
+ * @return Mage_Connect_Package
1479
+ */
1480
+ public function clearContents()
1481
+ {
1482
+ $this->_packageXml->contents = null;
1483
+ return $this;
1484
+ }
1485
+
1486
+ }
lib/Mage/Connect/Package/Extension.php ADDED
@@ -0,0 +1 @@
 
1
+
lib/Mage/Connect/Package/Hotfix.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with Magento Connect Hotfix
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+
35
+ class Mage_Connect_Package_Hotfix extends Mage_Connect_Package
36
+ {
37
+
38
+ /**
39
+ * Initializes an empty package object
40
+ *
41
+ * @param null|string $definition optional package definition xml
42
+ * @return Mage_Connect_Package
43
+ */
44
+ protected function _init($definition=null)
45
+ {
46
+
47
+ if (!is_null($definition)) {
48
+ $this->_packageXml = simplexml_load_string($definition);
49
+ } else {
50
+ $packageXmlStub = <<<END
51
+ <?xml version="1.0"?>
52
+ <package>
53
+ <name />
54
+ <version />
55
+ <stability />
56
+ <license />
57
+ <channel />
58
+ <extends />
59
+ <summary />
60
+ <description />
61
+ <notes />
62
+ <authors />
63
+ <date />
64
+ <time />
65
+ <replace />
66
+ <compatible />
67
+ <dependencies />
68
+ </package>
69
+ END;
70
+ $this->_packageXml = simplexml_load_string($packageXmlStub);
71
+ }
72
+ return $this;
73
+ }
74
+
75
+ /**
76
+ * Add content to node <replace/>
77
+ *
78
+ * @return Mage_Connect_Package_Hotfix
79
+ */
80
+ public function addReplace($path, $targetName)
81
+ {
82
+ $found = false;
83
+ $parent = $this->_getNode('target', $this->_packageXml->replace, $targetName);
84
+ $path = str_replace('\\', '/', $path);
85
+ $directories = explode('/', dirname($path));
86
+ foreach ($directories as $directory) {
87
+ $parent = $this->_getNode('dir', $parent, $directory);
88
+ }
89
+ $fileName = basename($path);
90
+ if ($fileName!='') {
91
+ $fileNode = $parent->addChild('file');
92
+ $fileNode->addAttribute('name', $fileName);
93
+ }
94
+ return $this;
95
+ }
96
+
97
+ /**
98
+ * Add directory recursively (with subdirectory and file).
99
+ * Exclude and Include can be add using Regular Expression.
100
+ *
101
+ * @param string $targetName Target name
102
+ * @param string $targetDir Path for target name
103
+ * @param string $path Path to directory
104
+ * @param string $exclude Exclude
105
+ * @param string $include Include
106
+ * @return Mage_Connect_Package
107
+ */
108
+ public function addReplaceDir($targetName, $targetDir, $path, $exclude=null, $include=null)
109
+ {
110
+ $targetDirLen = strlen($targetDir);
111
+ //get all subdirectories and files.
112
+ $entries = @glob($targetDir.$path.DS."*");
113
+ if (!empty($entries)) {
114
+ foreach ($entries as $entry) {
115
+ $filePath = substr($entry, $targetDirLen);
116
+ if (!empty($include) && !preg_match($include, $filePath)) {
117
+ continue;
118
+ }
119
+ if (!empty($ignore) && preg_match($exclude, $filePath)) {
120
+ continue;
121
+ }
122
+ if (is_dir($entry)) {
123
+ $baseName = basename($entry);
124
+ if ('.'===$baseName || '..'===$baseName) {
125
+ continue;
126
+ }
127
+ //for subdirectory call method recursively
128
+ $this->addReplaceDir($targetName, $targetDir, $filePath, $exclude, $include);
129
+ } elseif (is_file($entry)) {
130
+ $this->addReplace($filePath, $targetName);
131
+ }
132
+ }
133
+ }
134
+ return $this;
135
+ }
136
+
137
+ }
lib/Mage/Connect/Package/Maintainer.php ADDED
File without changes
lib/Mage/Connect/Package/Reader.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to get package.xml from different places.
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Connect_Package_Reader
35
+ {
36
+
37
+ /**
38
+ * Name of package file
39
+ */
40
+ const DEFAULT_NAME_PACKAGE = 'package.xml';
41
+
42
+ /**
43
+ * Temporary dir for extract DEFAULT_NAME_PACKAGE.
44
+ */
45
+ const PATH_TO_TEMPORARY_DIRECTORY = 'var/package/tmp/';
46
+
47
+ /**
48
+ * Current path to file.
49
+ *
50
+ * @var string
51
+ */
52
+ protected $_file = '';
53
+
54
+ /**
55
+ * Archivator is used for extract DEFAULT_NAME_PACKAGE.
56
+ *
57
+ * @var Mage_Archive
58
+ */
59
+ protected $_archivator = null;
60
+
61
+ /**
62
+ * Constructor initializes $_file.
63
+ *
64
+ * @param string $file
65
+ * @return Mage_Connect_Package_Reader
66
+ */
67
+ public function __construct($file='')
68
+ {
69
+ if ($file) {
70
+ $this->_file = $file;
71
+ } else {
72
+ $this->_file = self::DEFAULT_NAME_PACKAGE;
73
+ }
74
+ return $this;
75
+ }
76
+
77
+ /**
78
+ * Retrieve archivator.
79
+ *
80
+ * @return Mage_Archive
81
+ */
82
+ protected function _getArchivator()
83
+ {
84
+ if (is_null($this->_archivator)) {
85
+ $this->_archivator = new Mage_Archive();
86
+ }
87
+ return $this->_archivator;
88
+ }
89
+
90
+ /**
91
+ * Open file directly or from archive and return his content.
92
+ *
93
+ * @return string Content of file $file
94
+ */
95
+ public function load()
96
+ {
97
+ if (!is_file($this->_file) || !is_readable($this->_file)) {
98
+ throw new Exception('Invalid package file specified.');
99
+ }
100
+ if ($this->_getArchivator()->isArchive($this->_file)) {
101
+ @mkdir(self::PATH_TO_TEMPORARY_DIRECTORY, 0777, true);
102
+ $this->_file = $this->_getArchivator()->extract(
103
+ self::DEFAULT_NAME_PACKAGE,
104
+ $this->_file,
105
+ self::PATH_TO_TEMPORARY_DIRECTORY
106
+ );
107
+ }
108
+ $xmlContent = $this->_readFile();
109
+ return $xmlContent;
110
+ }
111
+
112
+ /**
113
+ * Read content file.
114
+ *
115
+ * @return string Content of file $file
116
+ */
117
+ protected function _readFile()
118
+ {
119
+ $handle = fopen($this->_file, 'r');
120
+ try {
121
+ $data = $this->_loadResource($handle);
122
+ } catch (Mage_Exception $e) {
123
+ fclose($handle);
124
+ throw $e;
125
+ }
126
+ fclose($handle);
127
+ return $data;
128
+ }
129
+
130
+ /**
131
+ * Loads a package from specified resource
132
+ *
133
+ * @param resource $resource only file resources are supported at the moment
134
+ * @return Mage_Connect_Package
135
+ */
136
+ protected function _loadResource(&$resource)
137
+ {
138
+ $data = '';
139
+ //var_dump("====", $res, get_resource_type($resource));
140
+ if ('stream' === get_resource_type($resource)) {
141
+ while (!feof($resource)) {
142
+ $data .= fread($resource, 10240);
143
+ }
144
+ } else {
145
+ throw new Mage_Exception('Unsupported resource type');
146
+ }
147
+ return $data;
148
+ }
149
+
150
+ }
lib/Mage/Connect/Package/Target.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to get targets and their basepath from target.xml.
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+
35
+ class Mage_Connect_Package_Target
36
+ {
37
+
38
+ /**
39
+ * Object contains all contents from target.xml.
40
+ *
41
+ * @var array
42
+ */
43
+ protected $_targetMap=null;
44
+
45
+ /**
46
+ * Cache for targets.
47
+ *
48
+ * @var array
49
+ */
50
+ protected $_targets;
51
+
52
+ /**
53
+ * Retrieve content from target.xml.
54
+ *
55
+ * @return SimpleXMLElement
56
+ */
57
+ protected function _getTargetMap()
58
+ {
59
+ if (is_null($this->_targetMap)) {
60
+ $this->_targetMap = array();
61
+ $this->_targetMap[] = array('name'=>"magelocal" ,'label'=>"Magento Local module file" , 'uri'=>"./app/code/local");
62
+ $this->_targetMap[] = array('name'=>"magecommunity" ,'label'=>"Magento Community module file" , 'uri'=>"./app/code/community");
63
+ $this->_targetMap[] = array('name'=>"magecore" ,'label'=>"Magento Core team module file" , 'uri'=>"./app/code/core");
64
+ $this->_targetMap[] = array('name'=>"magedesign" ,'label'=>"Magento User Interface (layouts, templates)" , 'uri'=>"./app/design");
65
+ $this->_targetMap[] = array('name'=>"mageetc" ,'label'=>"Magento Global Configuration" , 'uri'=>"./app/etc");
66
+ $this->_targetMap[] = array('name'=>"magelib" ,'label'=>"Magento PHP Library file" , 'uri'=>"./lib");
67
+ $this->_targetMap[] = array('name'=>"magelocale" ,'label'=>"Magento Locale language file" , 'uri'=>"./app/locale");
68
+ $this->_targetMap[] = array('name'=>"magemedia" ,'label'=>"Magento Media library" , 'uri'=>"./media");
69
+ $this->_targetMap[] = array('name'=>"mageskin" ,'label'=>"Magento Theme Skin (Images, CSS, JS)" , 'uri'=>"./skin");
70
+ $this->_targetMap[] = array('name'=>"mageweb" ,'label'=>"Magento Other web accessible file" , 'uri'=>".");
71
+ $this->_targetMap[] = array('name'=>"magetest" ,'label'=>"Magento PHPUnit test" , 'uri'=>"./tests");
72
+ $this->_targetMap[] = array('name'=>"mage" ,'label'=>"Magento other" , 'uri'=>".");
73
+ }
74
+ return $this->_targetMap;
75
+ }
76
+
77
+ /**
78
+ * Retrieve targets as associative array from target.xml.
79
+ *
80
+ * @return array
81
+ */
82
+ public function getTargets()
83
+ {
84
+ if (!is_array($this->_targets)) {
85
+ $this->_targets = array();
86
+ if($this->_getTargetMap()) {
87
+ foreach ($this->_getTargetMap() as $_target) {
88
+ $this->_targets[$_target['name']] = (string)$_target['uri'];
89
+ }
90
+ }
91
+ }
92
+ return $this->_targets;
93
+ }
94
+
95
+ /**
96
+ * Retrieve tragets with label for select options.
97
+ *
98
+ * @return array
99
+ */
100
+ public function getLabelTargets()
101
+ {
102
+ $targets = array();
103
+ foreach ($this->_getTargetMap() as $_target) {
104
+ $targets[$_target['name']] = $_target['label'];
105
+ }
106
+ return $targets;
107
+ }
108
+
109
+ /**
110
+ * Get uri by target's name.
111
+ *
112
+ * @param string $name
113
+ * @return string
114
+ */
115
+ public function getTargetUri($name)
116
+ {
117
+ foreach ($this->getTargets() as $_name=>$_uri) {
118
+ if ($name == $_name) {
119
+ return $_uri;
120
+ }
121
+ }
122
+ return '';
123
+ }
124
+
125
+
126
+ }
lib/Mage/Connect/Package/VO.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ class Mage_Connect_Package_VO implements Iterator
28
+ {
29
+ protected $properties = array(
30
+ 'name' => '',
31
+ 'package_type_vesrion' => '',
32
+ 'cahnnel' => '',
33
+ 'extends' => '',
34
+ 'summary' => '',
35
+ 'description' => '',
36
+ 'authors' => '',
37
+ 'date' => '',
38
+ 'time' => '',
39
+ 'version' => '',
40
+ 'stability' => 'dev',
41
+ 'license' => '',
42
+ 'license_uri' => '',
43
+ 'contents' => '',
44
+ 'compatible' => '',
45
+ 'hotfix' => ''
46
+ );
47
+
48
+ public function rewind() {
49
+ reset($this->properties);
50
+ }
51
+
52
+ public function valid() {
53
+ return current($this->properties) !== false;
54
+ }
55
+
56
+ public function key() {
57
+ return key($this->properties);
58
+ }
59
+
60
+ public function current() {
61
+ return current($this->properties);
62
+ }
63
+
64
+ public function next() {
65
+ next($this->properties);
66
+ }
67
+
68
+ public function __get($var)
69
+ {
70
+ if (isset($this->properties[$var])) {
71
+ return $this->properties[$var];
72
+ }
73
+ return null;
74
+ }
75
+
76
+ public function __set($var, $value)
77
+ {
78
+ if (is_string($value)) {
79
+ $value = trim($value);
80
+ }
81
+ if (isset($this->properties[$var])) {
82
+ if ($value === null) {
83
+ $value = '';
84
+ }
85
+ $this->properties[$var] = $value;
86
+ }
87
+ }
88
+
89
+ public function toArray()
90
+ {
91
+ return $this->properties;
92
+ }
93
+
94
+ }
95
+
96
+
lib/Mage/Connect/Package/Writer.php ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to create archive.
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Connect_Package_Writer
35
+ {
36
+
37
+ /**
38
+ * Name of package configuration file
39
+ */
40
+ const DEFAULT_NAME_PACKAGE_CONFIG = 'package.xml';
41
+
42
+ /**
43
+ * Temporary dir for extract DEFAULT_NAME_PACKAGE.
44
+ */
45
+ const PATH_TO_TEMPORARY_DIRECTORY = 'var/package/tmp/';
46
+
47
+ /**
48
+ * Files are used in package.
49
+ *
50
+ * @var array
51
+ */
52
+ protected $_files = array();
53
+
54
+ /**
55
+ * Archivator is used for extract DEFAULT_NAME_PACKAGE.
56
+ *
57
+ * @var Mage_Archive
58
+ */
59
+ protected $_archivator = null;
60
+
61
+ /**
62
+ * Name of package with extension. Extension should be only one.
63
+ * "package.tar.gz" is not ability, only "package.tgz".
64
+ *
65
+ * @var string
66
+ */
67
+ protected $_namePackage = 'package';
68
+
69
+ /**
70
+ * Temporary directory where package is situated.
71
+ *
72
+ * @var string
73
+ */
74
+ protected $_temporaryPackageDir = '';
75
+
76
+ /**
77
+ * Path to archive with package.
78
+ *
79
+ * @var mixed
80
+ */
81
+ protected $_pathToArchive = '';
82
+
83
+ /**
84
+ * Constructor initializes $_file.
85
+ *
86
+ * @param array $files
87
+ * @param string $namePackage
88
+ * @return Mage_Connect_Package_Reader
89
+ */
90
+ public function __construct($files, $namePackage='')
91
+ {
92
+ $this->_files = $files;
93
+ $this->_namePackage = $namePackage;
94
+ return $this;
95
+ }
96
+
97
+ /**
98
+ * Retrieve archivator.
99
+ *
100
+ * @return Mage_Archive
101
+ */
102
+ protected function _getArchivator()
103
+ {
104
+ if (is_null($this->_archivator)) {
105
+ $this->_archivator = new Mage_Archive();
106
+ }
107
+ return $this->_archivator;
108
+ }
109
+
110
+ /**
111
+ * Create dir in PATH_TO_TEMPORARY_DIRECTORY and move all files
112
+ * to this dir.
113
+ *
114
+ * @return Mage_Connect_Package_Writer
115
+ */
116
+ public function composePackage()
117
+ {
118
+ @mkdir(self::PATH_TO_TEMPORARY_DIRECTORY, 0777, true);
119
+ $root = self::PATH_TO_TEMPORARY_DIRECTORY . basename($this->_namePackage);
120
+ @mkdir($root, 0777, true);
121
+ foreach ($this->_files as $file) {
122
+
123
+ if (is_dir($file) || is_file($file)) {
124
+ $fileName = basename($file);
125
+ $filePath = dirname($file);
126
+ @mkdir($root . DS . $filePath, 0777, true);
127
+ if (is_file($file)) {
128
+ copy($file, $root . DS . $filePath . DS . $fileName);
129
+ } else {
130
+ @mkdir($root . DS . $filePath . $fileName, 0777);
131
+ }
132
+ }
133
+ }
134
+ $this->_temporaryPackageDir = $root;
135
+ return $this;
136
+ }
137
+
138
+ /**
139
+ * Create dir in PATH_TO_TEMPORARY_DIRECTORY and move all files
140
+ * to this dir.
141
+ * This dir has a structure compatible with previous version of Magento Connact Manager
142
+ *
143
+ * @param arra $destinationFiles
144
+ * @return Mage_Connect_Package_Writer
145
+ */
146
+ public function composePackageV1x(array $destinationFiles)
147
+ {
148
+ @mkdir(self::PATH_TO_TEMPORARY_DIRECTORY, 0777, true);
149
+ $root = self::PATH_TO_TEMPORARY_DIRECTORY . basename($this->_namePackage);
150
+ @mkdir($root, 0777, true);
151
+ $packageFilesDir = $root . DS . basename($this->_namePackage);
152
+ @mkdir($packageFilesDir, 0777, true);
153
+ foreach ($this->_files as $index => $file) {
154
+ $destinationFile = $destinationFiles[$index];
155
+ if (is_dir($file) || is_file($file)) {
156
+ $fileName = basename($destinationFile);
157
+ $filePath = dirname($destinationFile);
158
+ @mkdir($packageFilesDir . DS . $filePath, 0777, true);
159
+ if (is_file($file)) {
160
+ copy($file, $packageFilesDir . DS . $filePath . DS . $fileName);
161
+ } else {
162
+ @mkdir($packageFilesDir . DS . $filePath . $fileName, 0777);
163
+ }
164
+ }
165
+ }
166
+ $this->_temporaryPackageDir = $root;
167
+ return $this;
168
+ }
169
+
170
+ /**
171
+ * Add package.xml to temporary package directory.
172
+ *
173
+ * @param $content
174
+ * @return Mage_Connect_Package_Writer
175
+ */
176
+ public function addPackageXml($content)
177
+ {
178
+ file_put_contents($this->_temporaryPackageDir . DS . self::DEFAULT_NAME_PACKAGE_CONFIG, $content);
179
+ return $this;
180
+ }
181
+
182
+ /**
183
+ * Archives package.
184
+ *
185
+ * @return Mage_Connect_Package_Writer
186
+ */
187
+ public function archivePackage()
188
+ {
189
+ $this->_pathToArchive = $this->_getArchivator()->pack(
190
+ $this->_temporaryPackageDir,
191
+ $this->_namePackage.'.tgz',
192
+ true);
193
+
194
+ //delete temporary dir
195
+ Mage_System_Dirs::rm(array("-r", $this->_temporaryPackageDir));
196
+ return $this;
197
+ }
198
+
199
+ /**
200
+ * Getter for pathToArchive
201
+ *
202
+ * @return string
203
+ */
204
+ public function getPathToArchive()
205
+ {
206
+ return $this->_pathToArchive;
207
+ }
208
+
209
+ }
lib/Mage/Connect/Packager.php ADDED
@@ -0,0 +1,662 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to manipulate with packages
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+
35
+ class Mage_Connect_Packager
36
+ {
37
+
38
+ /**
39
+ * Constructor
40
+ * @param Mage_connect_Config $config
41
+ */
42
+ public function __construct()
43
+ {
44
+
45
+ }
46
+
47
+ /**
48
+ *
49
+ * @var Mage_Archive
50
+ */
51
+ protected $_archiver = null;
52
+ protected $_http = null;
53
+
54
+
55
+
56
+ /**
57
+ *
58
+ * @return Mage_Archive
59
+ */
60
+ public function getArchiver()
61
+ {
62
+ if(is_null($this->_archiver)) {
63
+ $this->_archiver = new Mage_Archive();
64
+ }
65
+ return $this->_archiver;
66
+ }
67
+
68
+ public function getDownloader()
69
+ {
70
+ if(is_null($this->_http)) {
71
+ $this->_http = Mage_HTTP_Client::getInstance();
72
+ }
73
+ return $this->_http;
74
+ }
75
+
76
+
77
+ public function getRemoteConf($ftpString)
78
+ {
79
+ $ftpObj = new Mage_Connect_Ftp();
80
+ $ftpObj->connect($ftpString);
81
+ $cfgFile = "connect.cfg";
82
+ $cacheFile = "cache.cfg";
83
+
84
+
85
+ $wd = $ftpObj->getcwd();
86
+
87
+ $remoteConfigExists = $ftpObj->fileExists($cfgFile);
88
+ $tempConfigFile = uniqid($cfgFile."_temp");
89
+ if(!$remoteConfigExists) {
90
+ $remoteCfg = new Mage_Connect_Config($tempConfigFile);
91
+ $remoteCfg->store();
92
+ $ftpObj->upload($cfgFile, $tempConfigFile);
93
+ } else {
94
+ $ftpObj->get($tempConfigFile, $cfgFile);
95
+ $remoteCfg = new Mage_Connect_Config($tempConfigFile);
96
+ }
97
+
98
+ $ftpObj->chdir($wd);
99
+
100
+ $remoteCacheExists = $ftpObj->fileExists($cacheFile);
101
+ $tempCacheFile = uniqid($cacheFile."_temp");
102
+
103
+ if(!$remoteCacheExists) {
104
+ $remoteCache = new Mage_Connect_Singleconfig($tempCacheFile);
105
+ $remoteCache->clear();
106
+ $ftpObj->upload($cacheFile, $tempCacheFile);
107
+ } else {
108
+ $ftpObj->get($tempCacheFile, $cacheFile);
109
+ $remoteCache = new Mage_Connect_Singleconfig($tempCacheFile);
110
+ }
111
+ $ftpObj->chdir($wd);
112
+ return array($remoteCache, $remoteCfg, $ftpObj);
113
+ }
114
+
115
+
116
+ public function getRemoteCache($ftpString)
117
+ {
118
+
119
+ $ftpObj = new Mage_Connect_Ftp();
120
+ $ftpObj->connect($ftpString);
121
+ $remoteConfigExists = $ftpObj->fileExists("cache.cfg");
122
+ if(!$remoteConfigExists) {
123
+ $configFile= uniqid("temp_cachecfg_");
124
+ $remoteCfg = new Mage_Connect_Singleconfig($configFile);
125
+ $remoteCfg->clear();
126
+ $ftpObj->upload("cache.cfg", $configFile);
127
+ } else {
128
+ $configFile = uniqid("temp_cachecfg_");
129
+ $ftpObj->get($configFile, "cache.cfg");
130
+ $remoteCfg = new Mage_Connect_Singleconfig($configFile);
131
+ }
132
+ return array($remoteCfg, $ftpObj);
133
+ }
134
+
135
+
136
+ public function getRemoteConfig($ftpString)
137
+ {
138
+ $ftpObj = new Mage_Connect_Ftp();
139
+ $ftpObj->connect($ftpString);
140
+ $cfgFile = "connect.cfg";
141
+
142
+ $wd = $ftpObj->getcwd();
143
+ $remoteConfigExists = $ftpObj->fileExists($cfgFile);
144
+ $tempConfigFile = uniqid($cfgFile."_temp");
145
+ if(!$remoteConfigExists) {
146
+ $remoteCfg = new Mage_Connect_Config($tempConfigFile);
147
+ $remoteCfg->store();
148
+ $ftpObj->upload($cfgFile, $tempConfigFile);
149
+ } else {
150
+ $ftpObj->get($tempConfigFile, $cfgFile);
151
+ $remoteCfg = new Mage_Connect_Config($tempConfigFile);
152
+ }
153
+ $ftpObj->chdir($wd);
154
+ return array($remoteCfg, $ftpObj);
155
+ }
156
+
157
+ public function writeToRemoteCache($cache, $ftpObj)
158
+ {
159
+ $wd = $ftpObj->getcwd();
160
+ $ftpObj->upload("cache.cfg", $cache->getFilename());
161
+ @unlink($cache->getFilename());
162
+ $ftpObj->chdir($wd);
163
+ }
164
+
165
+ public function writeToRemoteConfig($cache, $ftpObj)
166
+ {
167
+ $wd = $ftpObj->getcwd();
168
+ $ftpObj->upload("connect.cfg", $cache->getFilename());
169
+ @unlink($cache->getFilename());
170
+ $ftpObj->chdir($wd);
171
+ }
172
+
173
+ /**
174
+ *
175
+ * @param $chanName
176
+ * @param $package
177
+ * @param Mage_Connect_Singleconfig $cacheObj
178
+ * @param $ftp
179
+ * @return unknown_type
180
+ */
181
+ public function processUninstallPackage($chanName, $package, $cacheObj, $configObj)
182
+ {
183
+ $package = $cacheObj->getPackageObject($chanName, $package);
184
+ $contents = $package->getContents();
185
+
186
+ $targetPath = rtrim($configObj->magento_root, "\\/");
187
+ foreach($contents as $file) {
188
+ $fileName = basename($file);
189
+ $filePath = dirname($file);
190
+ $dest = $targetPath . DIRECTORY_SEPARATOR . $filePath . DIRECTORY_SEPARATOR . $fileName;
191
+ if(@file_exists($dest)) {
192
+ //var_dump($dest);
193
+ @unlink($dest);
194
+ }
195
+ }
196
+ }
197
+
198
+ /**
199
+ *
200
+ * @param $chanName
201
+ * @param $package
202
+ * @param Mage_Connect_Singleconfig $cacheObj
203
+ * @param Mage_Connect_Ftp $ftp
204
+ * @return unknown_type
205
+ */
206
+ public function processUninstallPackageFtp($chanName, $package, $cacheObj, $ftp)
207
+ {
208
+ $ftpDir = $ftp->getcwd();
209
+ $package = $cacheObj->getPackageObject($chanName, $package);
210
+ $contents = $package->getContents();
211
+ foreach($contents as $file) {
212
+ $res = $ftp->delete($file);
213
+ }
214
+ $ftp->chdir($ftpDir);
215
+ }
216
+
217
+ protected function convertFtpPath($str)
218
+ {
219
+ return str_replace("\\", "/", $str);
220
+ }
221
+
222
+ public function processInstallPackageFtp($package, $file, $configObj, $ftp)
223
+ {
224
+ $ftpDir = $ftp->getcwd();
225
+ $contents = $package->getContents();
226
+ $arc = $this->getArchiver();
227
+ $target = dirname($file).DS.$package->getReleaseFilename();
228
+ @mkdir($target, 0777, true);
229
+ $mode = $configObj->global_dir_mode;
230
+ $tar = $arc->unpack($file, $target);
231
+ $modeFile = $configObj->global_file_mode;
232
+ $modeDir = $configObj->global_dir_mode;
233
+ foreach($contents as $file) {
234
+ $fileName = basename($file);
235
+ $filePath = $this->convertFtpPath(dirname($file));
236
+ $source = $tar.DS.$file;
237
+ if (file_exists($source) && is_file($source)) {
238
+ $args = array(ltrim($file,"/"), $source);
239
+ if($modeDir) {
240
+ $args[] = $modeDir;
241
+ }
242
+ call_user_func_array(array($ftp,'upload'), $args);
243
+ }
244
+ }
245
+ $ftp->chdir($ftpDir);
246
+ Mage_System_Dirs::rm(array("-r",$target));
247
+ }
248
+
249
+ /**
250
+ * Package installation to FS
251
+ * @param Mage_Connect_Package $package
252
+ * @param string $file
253
+ * @return void
254
+ * @throws Exception
255
+ */
256
+ public function processInstallPackage($package, $file, $configObj)
257
+ {
258
+ $contents = $package->getContents();
259
+ $arc = $this->getArchiver();
260
+ $target = dirname($file).DS.$package->getReleaseFilename();
261
+ @mkdir($target, 0777, true);
262
+ $mode = $configObj->global_dir_mode;
263
+ $tar = $arc->unpack($file, $target);
264
+ $modeFile = $configObj->global_file_mode;
265
+ $modeDir = $configObj->global_dir_mode;
266
+ foreach($contents as $file) {
267
+ $fileName = basename($file);
268
+ $filePath = dirname($file);
269
+ $source = $tar.DS.$file;
270
+ $targetPath = rtrim($configObj->magento_root, "\\/");
271
+ @mkdir($targetPath. DS . $filePath, $modeDir, true);
272
+ $dest = $targetPath . DS . $filePath . DS . $fileName;
273
+ if (is_file($source)) {
274
+ @copy($source, $dest);
275
+ if($modeFile) {
276
+ @chmod($dest, $modeFile);
277
+ }
278
+ } else {
279
+ @mkdir($dest, $modeDir);
280
+ }
281
+ }
282
+ Mage_System_Dirs::rm(array("-r",$target));
283
+ }
284
+
285
+
286
+ /**
287
+ * Get local modified files
288
+ * @param $chanName
289
+ * @param $package
290
+ * @param $cacheObj
291
+ * @param $configObj
292
+ * @return array
293
+ */
294
+ public function getLocalModifiedFiles($chanName, $package, $cacheObj, $configObj)
295
+ {
296
+ $p = $cachObj->getPackageObject($chanName, $package);
297
+ $hashContents = $p->getHashContents();
298
+ $listModified = array();
299
+ foreach ($hashContents as $file=>$hash) {
300
+ if (md5_file($configObj->magento_root . DS . $file)!==$hash) {
301
+ $listModified[] = $file;
302
+ }
303
+ }
304
+ return $listModified;
305
+ }
306
+
307
+ /**
308
+ * Get remote modified files
309
+ *
310
+ * @param $chanName
311
+ * @param $package
312
+ * @param $cacheObj
313
+ * @param Mage_Connect_Ftp $ftp
314
+ * @return array
315
+ */
316
+ public function getRemoteModifiedFiles($chanName, $package, $cacheObj, $ftp)
317
+ {
318
+ $p = $cacheObj->getPackageObject($chanName, $package);
319
+ $hashContents = $p->getHashContents();
320
+ $listModified = array();
321
+ foreach ($hashContents as $file=>$hash) {
322
+ $localFile = uniqid("temp_remote_");
323
+ if(!$ftp->fileExists($file)) {
324
+ continue;
325
+ }
326
+ $ftp->get($localFile, $file);
327
+ if (file_exists($localFile) && md5_file($localFile)!==$hash) {
328
+ $listModified[] = $file;
329
+ }
330
+ @unlink($localFile);
331
+ }
332
+ return $listModified;
333
+ }
334
+
335
+
336
+ /**
337
+ *
338
+ * Get upgrades list
339
+ *
340
+ * @param string/array $channels
341
+ * @param Mage_Connect_Singleconfig $cacheObject
342
+ * @param Mage_Connect_Rest $restObj optional
343
+ * @param bool $checkConflicts
344
+ * @return array
345
+ */
346
+ public function getUpgradesList($channels, $cacheObject, $configObj, $restObj = null, $checkConflicts = false)
347
+ {
348
+ if(is_scalar($channels)) {
349
+ $channels = array($channels);
350
+ }
351
+
352
+ if(!$restObj) {
353
+ $restObj = new Mage_Connect_Rest();
354
+ }
355
+
356
+ $updates = array();
357
+ foreach($channels as $chan) {
358
+
359
+ if(!$cacheObject->isChannel($chan)) {
360
+ continue;
361
+ }
362
+ $chanName = $cacheObject->chanName($chan);
363
+ $localPackages = $cacheObject->getInstalledPackages($chanName);
364
+ $localPackages = $localPackages[$chanName];
365
+
366
+ if(!count($localPackages)) {
367
+ continue;
368
+ }
369
+
370
+ $channel = $cacheObject->getChannel($chan);
371
+ $uri = $channel[Mage_Connect_Singleconfig::K_URI];
372
+ $restObj->setChannel($uri);
373
+ $remotePackages = $restObj->getPackagesHashed();
374
+
375
+ /**
376
+ * Iterate packages of channel $chan
377
+ */
378
+ $state = $configObj->preferred_state ? $configObj->preferred_state : "devel";
379
+
380
+ foreach($localPackages as $localName=>$localData) {
381
+ if(!isset($remotePackages[$localName])) {
382
+ continue;
383
+ }
384
+ $package = $remotePackages[$localName];
385
+ $neededToUpgrade = false;
386
+ $remoteVersion = $localVersion = trim($localData[Mage_Connect_Singleconfig::K_VER]);
387
+ foreach($package as $version => $s) {
388
+
389
+ if( $cacheObject->compareStabilities($s, $state) < 0 ) {
390
+ continue;
391
+ }
392
+
393
+ if(version_compare($version, $localVersion, ">")) {
394
+ $neededToUpgrade = true;
395
+ $remoteVersion = $version;
396
+ }
397
+
398
+ if($checkConflicts) {
399
+ $conflicts = $cacheObject->hasConflicts($chanName, $localName, $remoteVersion);
400
+ if(false !== $conflicts) {
401
+ $neededToUpgrade = false;
402
+ }
403
+ }
404
+ }
405
+ if(!$neededToUpgrade) {
406
+ continue;
407
+ }
408
+ if(!isset($updates[$chanName])) {
409
+ $updates[$chanName] = array();
410
+ }
411
+ $updates[$chanName][$localName] = array("from"=>$localVersion, "to"=>$remoteVersion);
412
+ }
413
+ }
414
+ return $updates;
415
+ }
416
+
417
+ /**
418
+ * Get uninstall list
419
+ * @param string $chanName
420
+ * @param string $package
421
+ * @param Mage_Connect_Singleconfig $cache
422
+ * @param Mage_Connect_Config $config
423
+ * @param bool $withDepsRecursive
424
+ * @return array
425
+ */
426
+ public function getUninstallList($chanName, $package, $cache, $config, $withDepsRecursive = true)
427
+ {
428
+ static $level = 0;
429
+ static $hash = array();
430
+
431
+ $chanName = $cache->chanName($chanName);
432
+ $keyOuter = $chanName . "/" . $package;
433
+ $level++;
434
+
435
+ try {
436
+ $chanName = $cache->chanName($chanName);
437
+ if(!$cache->hasPackage($chanName, $package)) {
438
+ $level--;
439
+ if($level == 0) {
440
+ $hash = array();
441
+ return array('list'=>array());
442
+ }
443
+ return;
444
+ }
445
+ $dependencies = $cache->getPackageDependencies($chanName, $package);
446
+ $data = $cache->getPackage($chanName, $package);
447
+ $version = $data['version'];
448
+ $keyOuter = $chanName . "/" . $package;
449
+
450
+ //print "Processing outer: {$keyOuter} \n";
451
+ $hash[$keyOuter] = array (
452
+ 'name' => $package,
453
+ 'channel' => $chanName,
454
+ 'version' => $version,
455
+ 'packages' => $dependencies,
456
+ );
457
+
458
+ if($withDepsRecursive) {
459
+ $flds = array('name','channel','min','max');
460
+ $fldsCount = count($flds);
461
+ foreach($dependencies as $row) {
462
+ foreach($flds as $key) {
463
+ $varName = "p".ucfirst($key);
464
+ $$varName = $row[$key];
465
+ }
466
+ $method = __FUNCTION__;
467
+ $keyInner = $pChannel . "/" . $pName;
468
+ if(!isset($hash[$keyInner])) {
469
+ $this->$method($pChannel, $pName, $cache, $config,
470
+ $withDepsRecursive, false);
471
+ }
472
+ }
473
+ }
474
+
475
+ } catch (Exception $e) {
476
+ //$this->_failed[] = array('name'=>$package, 'channel'=>$chanName, 'max'=>$versionMax, 'min'=>$versionMin, 'reason'=>$e->getMessage());
477
+ }
478
+
479
+ $level--;
480
+ if(0 === $level) {
481
+ $out = $this->processDepsHash($hash);
482
+ $hash = array();
483
+ return array('list'=>$out);
484
+ }
485
+ }
486
+
487
+ /**
488
+ * Get dependencies list/install order info
489
+ *
490
+ *
491
+ * @param string $chanName
492
+ * @param string $package
493
+ * @param Mage_Connect_Singleconfig $cache
494
+ * @param Mage_Connect_Config $config
495
+ * @param mixed $versionMax
496
+ * @param mixed $versionMin
497
+ * @return mixed
498
+ */
499
+ public function getDependenciesList( $chanName, $package, $cache, $config, $versionMax = false, $versionMin = false, $withDepsRecursive = true, $forceRemote = false)
500
+ {
501
+
502
+ static $level = 0;
503
+ static $_depsHash = array();
504
+ static $_deps = array();
505
+ static $_failed = array();
506
+
507
+ $level++;
508
+
509
+ try {
510
+ $chanName = $cache->chanName($chanName);
511
+
512
+ $rest = new Mage_Connect_Rest($config->protocol);
513
+ $rest->setChannel($cache->chanUrl($chanName));
514
+ $releases = $rest->getReleases($package);
515
+ if(!$releases || !count($releases)) {
516
+ throw new Exception("No releases for: '{$package}', skipping");
517
+ }
518
+ $state = $config->preffered_state ? $confg->preffered_state : 'devel';
519
+ $version = $cache->detectVersionFromRestArray($releases, $versionMin, $versionMax, $state);
520
+ if(!$version) {
521
+ throw new Exception("Version for '{$package}' was not detected");
522
+ }
523
+ $packageInfo = $rest->getPackageReleaseInfo($package, $version);
524
+ if(false === $packageInfo) {
525
+ throw new Exception("Package release '{$package}' not found on server");
526
+ }
527
+ unset($rest);
528
+ $dependencies = $packageInfo->getDependencyPackages();
529
+ $keyOuter = $chanName . "/" . $package;
530
+
531
+ //print "Processing outer: {$keyOuter} \n";
532
+ $_depsHash[$keyOuter] = array (
533
+ 'name' => $package,
534
+ 'channel' => $chanName,
535
+ 'downloaded_version' => $version,
536
+ 'min' => $versionMin,
537
+ 'max' => $versionMax,
538
+ 'packages' => $dependencies,
539
+ );
540
+
541
+ if($withDepsRecursive) {
542
+ $flds = array('name','channel','min','max');
543
+ $fldsCount = count($flds);
544
+ foreach($dependencies as $row) {
545
+ foreach($flds as $key) {
546
+ $varName = "p".ucfirst($key);
547
+ $$varName = $row[$key];
548
+ }
549
+ $method = __FUNCTION__;
550
+ $keyInner = $pChannel . "/" . $pName;
551
+ if(!isset($_depsHash[$keyInner])) {
552
+ $_deps[] = $row;
553
+ $this->$method($pChannel, $pName, $cache, $config,
554
+ $pMax, $pMin, $withDepsRecursive, $forceRemote, false);
555
+ } else {
556
+ $downloaded = $_depsHash[$keyInner]['downloaded_version'];
557
+ $hasMin = $_depsHash[$keyInner]['min'];
558
+ $hasMax = $_depsHash[$keyInner]['max'];
559
+ if($pMin === $hasMin && $pMax === $hasMax) {
560
+ //var_dump("Equal requirements, skipping");
561
+ continue;
562
+ }
563
+
564
+ if($cache->versionInRange($downloaded, $pMin, $pMax)) {
565
+ //var_dump("Downloaded package matches new range too");
566
+ continue;
567
+ }
568
+
569
+ $names = array("pMin","pMax","hasMin","hasMax");
570
+ for($i=0, $c=count($names); $i<$c; $i++) {
571
+ if(!isset($$names[$i])) {
572
+ continue;
573
+ }
574
+ if(false !== $$names[$i]) {
575
+ continue;
576
+ }
577
+ $$names[$i] = $i % 2 == 0 ? "0" : "999999999";
578
+ }
579
+
580
+ if(!$cache->hasVersionRangeIntersect($pMin,$pMax, $hasMin, $hasMax)) {
581
+ $reason = "Detected {$pName} conflict of versions: {$hasMin}-{$hasMax} and {$pMin}-{$pMax}";
582
+ unset($_depsHash[$keyInner]);
583
+ $_failed[] = array('name'=>$pName, 'channel'=>$pChannel, 'max'=>$pMax, 'min'=>$pMin, 'reason'=>$reason);
584
+ continue;
585
+ }
586
+ $newMaxIsLess = version_compare($pMax, $hasMax, "<");
587
+ $newMinIsGreater = version_compare($pMin, $hasMin, ">");
588
+ $forceMax = $newMaxIsLess ? $pMax : $hasMax;
589
+ $forceMin = $newMinIsGreater ? $pMin : $hasMin;
590
+ //var_dump("Trying to process {$pName} : max {$forceMax} - min {$forceMin}");
591
+ $this->$method($pChannel, $pName, $cache, $config,
592
+ $forceMax, $forceMin, $withDepsRecursive, $forceRemote);
593
+ }
594
+ }
595
+ }
596
+ } catch (Exception $e) {
597
+ $_failed[] = array('name'=>$package, 'channel'=>$chanName, 'max'=>$versionMax, 'min'=>$versionMin, 'reason'=>$e->getMessage());
598
+ }
599
+
600
+
601
+ $level--;
602
+ if($level == 0) {
603
+ $out = $this->processDepsHash($_depsHash);
604
+ $deps = $_deps;
605
+ $failed = $_failed;
606
+ $_depsHash = array();
607
+ $_deps = array();
608
+ $_failed = array();
609
+ return array('deps' => $deps, 'result' => $out, 'failed'=> $failed);
610
+ }
611
+
612
+ }
613
+
614
+
615
+ /**
616
+ * Process dependencies hash
617
+ * Makes topological sorting and gives operation order list
618
+ *
619
+ * @param array $depsHash
620
+ * @param bool $sortReverse
621
+ * @return array
622
+ */
623
+ protected function processDepsHash(&$depsHash, $sortReverse = true)
624
+ {
625
+ $nodes = array();
626
+ $graph = new Mage_Connect_Structures_Graph();
627
+
628
+ foreach($depsHash as $key=>$data) {
629
+ $packages = $data['packages'];
630
+ $node = new Mage_Connect_Structures_Node();
631
+ $nodes[$key] =& $node;
632
+ unset($data['packages']);
633
+ $node->setData($data);
634
+ $graph->addNode($node);
635
+ unset($node);
636
+ }
637
+
638
+ if(count($nodes) > 1) {
639
+ foreach($depsHash as $key=>$data) {
640
+ $packages = $data['packages'];
641
+ foreach($packages as $pdata) {
642
+ $pName = $pdata['channel'] . "/" . $pdata['name'];
643
+ if(isset($nodes[$key], $nodes[$pName])) {
644
+ $nodes[$key]->connectTo($nodes[$pName]);
645
+ }
646
+ }
647
+ }
648
+ }
649
+ $result = $graph->topologicalSort();
650
+ $sortReverse ? krsort($result) : ksort($result);
651
+ $out = array();
652
+ $total = 0;
653
+ foreach($result as $order=>$nodes) {
654
+ foreach($nodes as $n) {
655
+ $out[] = $n->getData();
656
+ }
657
+ }
658
+ unset($graph, $nodes);
659
+ return $out;
660
+ }
661
+
662
+ }
lib/Mage/Connect/Repository.php ADDED
File without changes
lib/Mage/Connect/Repository/Abstract.php ADDED
File without changes
lib/Mage/Connect/Repository/Channel.php ADDED
File without changes
lib/Mage/Connect/Repository/Channel/Abstract.php ADDED
File without changes
lib/Mage/Connect/Repository/Channel/Commercial.php ADDED
File without changes
lib/Mage/Connect/Repository/Channel/Community.php ADDED
File without changes
lib/Mage/Connect/Repository/Channel/Core.php ADDED
File without changes
lib/Mage/Connect/Repository/Local.php ADDED
File without changes
lib/Mage/Connect/Rest.php ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with remote REST interface
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+
35
+ class Mage_Connect_Rest
36
+ {
37
+
38
+ const CHANNELS_XML = "channels.xml";
39
+ const CHANNEL_XML = "channel.xml";
40
+ const PACKAGES_XML = "packages.xml";
41
+ const RELEASES_XML = "releases.xml";
42
+ const PACKAGE_XML = "package.xml";
43
+ const EXT = "tgz";
44
+
45
+ /**
46
+ * HTTP Loader
47
+ * @var Mage_HTTP_IClient
48
+ */
49
+ protected $_loader = null;
50
+
51
+
52
+ /**
53
+ * XML parser
54
+ * @var Mage_Xml_Parser
55
+ */
56
+ protected $_parser = null;
57
+
58
+ /**
59
+ * Channel URI
60
+ * @var string
61
+ */
62
+ protected $_chanUri = '';
63
+
64
+ /**
65
+ * Protocol HTTP or FTP
66
+ *
67
+ * @var string http or ftp
68
+ */
69
+ protected $_protocol = '';
70
+
71
+ /**
72
+ * Constructor
73
+ */
74
+ public function __construct($protocol="http")
75
+ {
76
+ switch ($protocol) {
77
+ case 'http':default:
78
+ $this->_protocol = 'http';
79
+ break;
80
+ case 'ftp':
81
+ $this->_protocol = 'ftp';
82
+ break;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Set channel info
88
+ *
89
+ * @param string $uri
90
+ * @param sting $name
91
+ */
92
+ public function setChannel($uri)
93
+ {
94
+ $this->_chanUri = $uri;
95
+ }
96
+
97
+ /**
98
+ * Get HTTP loader
99
+ * @return Mage_Connect_Loader
100
+ */
101
+ protected function getLoader()
102
+ {
103
+ if(is_null($this->_loader)) {
104
+ $this->_loader = Mage_Connect_Loader::getInstance($this->_protocol);
105
+ }
106
+ return $this->_loader;
107
+ }
108
+
109
+
110
+ /**
111
+ * Get parser
112
+ *
113
+ * @return Mage_Xml_Parser
114
+ */
115
+ protected function getParser()
116
+ {
117
+ if(is_null($this->_parser)) {
118
+ $this->_parser = new Mage_Xml_Parser();
119
+ }
120
+ return $this->_parser;
121
+ }
122
+
123
+ /**
124
+ * Load URI response
125
+ * @param string $uri
126
+ */
127
+ protected function loadChannelUri($uriSuffix)
128
+ {
129
+ $url = $this->_chanUri."/".$uriSuffix;
130
+ //print $url."\n";
131
+ $this->getLoader()->get($url);
132
+ $statusCode = $this->getLoader()->getStatus();
133
+ if($statusCode != 200) {
134
+ return false;
135
+ }
136
+ return $this->getLoader()->getBody();
137
+ }
138
+
139
+ /**
140
+ * Get channels list of URI
141
+ * @return array
142
+ */
143
+ public function getChannelInfo()
144
+ {
145
+ $out = $this->loadChannelUri(self::CHANNEL_XML);
146
+ $statusCode = $this->getLoader()->getStatus();
147
+ if($statusCode != 200) {
148
+ throw new Exception("Invalid server response for {$this->_chanUri}");
149
+ }
150
+ $parser = $this->getParser();
151
+ $out = $parser->loadXML($out)->xmlToArray();
152
+
153
+ // TODO: add channel validator
154
+ $vo = new Mage_Connect_Channel_VO();
155
+ $vo->fromArray($out['channel']);
156
+ if(!$vo->validate()) {
157
+ throw new Exception("Invalid channel.xml file");
158
+ }
159
+ return $vo;
160
+ }
161
+
162
+
163
+ /**
164
+ * Get packages list of channel
165
+ * @return array
166
+ */
167
+ public function getPackages()
168
+ {
169
+ $out = $this->loadChannelUri(self::PACKAGES_XML);
170
+ $statusCode = $this->getLoader()->getStatus();
171
+ if($statusCode != 200) {
172
+ return false;
173
+ }
174
+ $parser = $this->getParser();
175
+ $out = $parser->loadXML($out)->xmlToArray();
176
+
177
+
178
+ if(!isset($out['data']['p'])) {
179
+ return array();
180
+ }
181
+ if(isset($out['data']['p'][0])) {
182
+ return $out['data']['p'];
183
+ }
184
+ if(is_array($out['data']['p'])) {
185
+ return array($out['data']['p']);
186
+ }
187
+ return array();
188
+ }
189
+
190
+
191
+ public function getPackagesHashed()
192
+ {
193
+ $out = $this->loadChannelUri(self::PACKAGES_XML);
194
+ $statusCode = $this->getLoader()->getStatus();
195
+ if($statusCode != 200) {
196
+ return false;
197
+ }
198
+ $parser = $this->getParser();
199
+ $out = $parser->loadXML($out)->xmlToArray();
200
+
201
+ $return = array();
202
+ if(!isset($out['data']['p'])) {
203
+ return $return;
204
+ }
205
+ if(isset($out['data']['p'][0])) {
206
+ $return = $out['data']['p'];
207
+ }
208
+ if(is_array($out['data']['p'])) {
209
+ $return = array($out['data']['p']);
210
+ }
211
+ $c = count($return);
212
+ if($c) {
213
+ $output = array();
214
+ for($i=0,$c=count($return[0]); $i<$c; $i++) {
215
+ $element = $return[0][$i];
216
+ $output[$element['n']] = $element['r'];
217
+ }
218
+ $return = $output;
219
+ }
220
+
221
+ $out = array();
222
+ foreach($return as $name=>$package) {
223
+ $stabilities = array_map(array($this, 'shortStateToLong'), array_keys($package));
224
+ $versions = array_map('trim', array_values($package));
225
+ $package = array_combine($versions, $stabilities);
226
+ ksort($package);
227
+ $out[$name] = $package;
228
+ }
229
+ return $out;
230
+ }
231
+
232
+ /**
233
+ * Stub
234
+ * @param $n
235
+ * @return unknown_type
236
+ */
237
+ public function escapePackageName($n)
238
+ {
239
+ return $n;
240
+ }
241
+
242
+ /**
243
+ * Get releases list of package on current channel
244
+ * @param string $package package name
245
+ */
246
+ public function getReleases($package)
247
+ {
248
+ $out = $this->loadChannelUri($this->escapePackageName($package)."/".self::RELEASES_XML);
249
+ $statusCode = $this->getLoader()->getStatus();
250
+ if($statusCode != 200) {
251
+ return false;
252
+ }
253
+ $parser = $this->getParser();
254
+ $out = $parser->loadXML($out)->xmlToArray();
255
+ if(!isset($out['releases']['r'])) {
256
+ return array();
257
+ }
258
+ $src = $out['releases']['r'];
259
+ if(!array_key_exists(0, $src)) {
260
+ return array($src);
261
+ }
262
+ $this->sortReleases($src);
263
+ return $src;
264
+ }
265
+
266
+ /**
267
+ * Sort releases
268
+ * @param array $releases
269
+ * @return void
270
+ */
271
+ public function sortReleases(array &$releases)
272
+ {
273
+ usort($releases, array($this, 'sortReleasesCallback'));
274
+ $releases = array_reverse($releases);
275
+ }
276
+
277
+ /**
278
+ * Sort releases callback
279
+ * @param string $a
280
+ * @param srting $b
281
+ * @return int
282
+ */
283
+ protected function sortReleasesCallback($a, $b)
284
+ {
285
+ return version_compare($a['v'],$b['v']);
286
+ }
287
+
288
+ /**
289
+ * Get package info (package.xml)
290
+ *
291
+ * @param $package
292
+ * @return unknown_type
293
+ */
294
+ public function getPackageInfo($package)
295
+ {
296
+ $out = $this->loadChannelUri($this->escapePackageName($package)."/".self::PACKAGE_XML);
297
+ if(false === $out) {
298
+ return false;
299
+ }
300
+ return new Mage_Connect_Package($out);
301
+ }
302
+
303
+ /**
304
+ *
305
+ * @param $package
306
+ * @param $version
307
+ * @return Mage_Connect_Package
308
+ */
309
+ public function getPackageReleaseInfo($package, $version)
310
+ {
311
+ $out = $this->loadChannelUri($this->escapePackageName($package)."/".$version."/".self::PACKAGE_XML);
312
+ if(false === $out) {
313
+ return false;
314
+ }
315
+ return new Mage_Connect_Package($out);
316
+ }
317
+
318
+ /**
319
+ * Get package archive file of release
320
+ * @param string $package package name
321
+ * @param string $version version
322
+ */
323
+ public function downloadPackageFileOfRelease($package, $version, $targetFile)
324
+ {
325
+ $package = $this->escapePackageName($package);
326
+ $version = $this->escapePackageName($version);
327
+
328
+
329
+ if(file_exists($targetFile)) {
330
+ $chksum = $this->loadChannelUri($package."/".$version."/checksum");
331
+ $statusCode = $this->getLoader()->getStatus();
332
+ if($statusCode == 200) {
333
+ if(md5_file($targetFile) == $chksum) {
334
+ return true;
335
+ }
336
+ }
337
+ }
338
+
339
+
340
+ $out = $this->loadChannelUri($package."/".$version."/".$package."-".$version.".".self::EXT);
341
+
342
+ $statusCode = $this->getLoader()->getStatus();
343
+ if($statusCode != 200) {
344
+ throw new Exception("Package not found: {$package} {$version}");
345
+ }
346
+ $dir = dirname($targetFile);
347
+ @mkdir($dir, 0777, true);
348
+ $result = @file_put_contents($targetFile, $out);
349
+ if(false === $result) {
350
+ throw new Exception("Cannot write to file {$targetFile}");
351
+ }
352
+ return true;
353
+ }
354
+
355
+ protected $states = array('b'=>'beta', 'd'=>'dev', 's'=>'stable', 'a'=>'alpha');
356
+
357
+ public function shortStateToLong($s)
358
+ {
359
+ return isset($this->states[$s]) ? $this->states[$s] : 'dev';
360
+ }
361
+
362
+
363
+ }
364
+
lib/Mage/Connect/Singleconfig.php ADDED
@@ -0,0 +1,865 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to manipulate with channel/package cache file
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+
35
+ class Mage_Connect_Singleconfig
36
+ {
37
+
38
+ /**
39
+ * Cache data
40
+ * @var array
41
+ */
42
+ protected $_data = array();
43
+
44
+ /**
45
+ * Filename
46
+ * @var string
47
+ */
48
+ protected $_readFilename = false;
49
+
50
+ /**
51
+ *
52
+ * @var unknown_type
53
+ */
54
+ protected $_debug = false;
55
+
56
+ /**
57
+ *
58
+ * @var unknown_type
59
+ */
60
+ protected $_validator;
61
+
62
+ /**
63
+ * Internal keys constants
64
+ */
65
+ const K_CHAN = 'channels_by_name';
66
+ const K_CHAN_URI = 'channels_by_uri';
67
+ const K_CHAN_ALIAS = 'channel_aliases';
68
+ const K_PACK = 'packages';
69
+ const K_URI = 'uri';
70
+ const K_CHAN_DATA = 'channel_data';
71
+ const K_NAME = 'name';
72
+ const K_VER = 'version';
73
+ const K_STATE = 'stability';
74
+ const K_XML = 'xml';
75
+ const K_DEPS = 'deps';
76
+ const K_PACK_DEPS = 'pack_deps';
77
+ const K_CONFIG = 'config';
78
+
79
+ public function getValidUri($str)
80
+ {
81
+ $data = @parse_url($str);
82
+ if(isset($data['path'])) {
83
+ return $data['path'];
84
+ }
85
+ return false;
86
+ }
87
+
88
+ public function getFilename()
89
+ {
90
+ return $this->_readFilename;
91
+ }
92
+
93
+ public function formatUri($uri)
94
+ {
95
+ $uri = rtrim($uri, "/");
96
+ $uri = str_replace("http://", '', $uri);
97
+ $uri = str_replace("ftp://", '', $uri);
98
+ return $uri;
99
+ }
100
+
101
+ /**
102
+ * Get data
103
+ * @return unknown_type
104
+ */
105
+ public function getData()
106
+ {
107
+ return $this->_data;
108
+ }
109
+
110
+ /**
111
+ * Constructor
112
+ * @param srting $file
113
+ * @return void
114
+ */
115
+ public function __construct($file = "cache.cfg")
116
+ {
117
+ $this->setEmptyConfig();
118
+ if($file) {
119
+ $this->_readFilename = $file;
120
+ $this->load();
121
+ }
122
+ }
123
+
124
+
125
+ /**
126
+ * Load cache from file
127
+ * @param string $file
128
+ * @return void
129
+ */
130
+ public function load($file = false)
131
+ {
132
+ if(false === $file) {
133
+ $file = $this->_readFilename;
134
+ }
135
+ if(false === $file) {
136
+ return;
137
+ }
138
+
139
+ if(!file_exists($file)) {
140
+ $this->save($file);
141
+ return;
142
+ }
143
+
144
+ if(!is_readable($file)) {
145
+ return $this->doError("File is not readable: '{$file}'");
146
+ }
147
+
148
+ $this->_readFilename = $file;
149
+
150
+ $data = @file_get_contents($file);
151
+ if(false === $data) {
152
+ return $this->doError("Cannot get file contents: '{$file}'");
153
+ }
154
+
155
+ if(!$this->_debug) {
156
+ $data = @gzuncompress($data);
157
+ if(false === $data) {
158
+ return $this->doError("Cannot unpack gzipped data in file contents: '{$file}'");
159
+ }
160
+ }
161
+ $data = @unserialize($data);
162
+ if(unserialize(false) === $data) {
163
+ return $this->doError("Cannot unserialize data in file contents: '{$file}'");
164
+ }
165
+
166
+
167
+ $validData = true;
168
+ foreach(array_keys($this->_data) as $k) {
169
+ if(!isset($data[$k])) {
170
+ $validData = false;
171
+ } else {
172
+ $this->_data[$k] = $data[$k];
173
+ }
174
+ }
175
+ if($validData) {
176
+ $this->_data = $data;
177
+ } else {
178
+ $this->save();
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Save contents
184
+ * @param string $file
185
+ * @return void
186
+ */
187
+ public function save($file = false)
188
+ {
189
+ if(false === $file) {
190
+ $file = $this->_readFilename;
191
+ }
192
+ if(false === $file) {
193
+ return;
194
+ }
195
+ $data = @serialize($this->_data);
196
+ if(!$this->_debug) {
197
+ $data = @gzcompress($data);
198
+ }
199
+ $res = @file_put_contents($file, $data);
200
+ if(!$res) {
201
+ $this->doError("Cannot save: '{$file}'");
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Set empty config skeleton
207
+ * @return void
208
+ */
209
+ public function setEmptyConfig()
210
+ {
211
+ $this->_data = array(
212
+ self::K_CHAN => array (),
213
+ self::K_CHAN_URI => array (),
214
+ self::K_CHAN_ALIAS => array (),
215
+ );
216
+ }
217
+
218
+
219
+ public function isChannel($chanName)
220
+ {
221
+ if($this->isChannelName($chanName)) {
222
+ return true;
223
+ }
224
+ if($this->isChannelUri($chanName)) {
225
+ return true;
226
+ }
227
+ if($this->isChannelAlias($chanName)) {
228
+ return true;
229
+ }
230
+ return false;
231
+ }
232
+
233
+ /**
234
+ * Get channel
235
+ * @param string $chanName
236
+ * @return array
237
+ */
238
+ public function getChannel($chanName)
239
+ {
240
+ if($this->isChannelAlias($chanName)) {
241
+ $chanName = $this->getChannelNameByAlias($chanName);
242
+ } elseif($this->isChannelUri($chanName)) {
243
+ $chanName = $this->getChannelUriRecord($chanName);
244
+ }
245
+ if($this->isChannelName($chanName)) {
246
+ return $this->_data[self::K_CHAN][$chanName];
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Is channel name?
252
+ * @param $chanName
253
+ * @return bool
254
+ */
255
+ public function isChannelName($chanName)
256
+ {
257
+ return isset($this->_data[self::K_CHAN][$chanName]);
258
+ }
259
+
260
+ /**
261
+ * Is channel alias?
262
+ * @param string $chanName
263
+ * @return bool
264
+ */
265
+ public function isChannelAlias($chanName)
266
+ {
267
+ return isset($this->_data[self::K_CHAN_ALIAS][$chanName]);
268
+ }
269
+
270
+ /**
271
+ * Is channel uri?
272
+ * @param $uri
273
+ * @return bool
274
+ */
275
+ public function isChannelUri($uri)
276
+ {
277
+ $uri = $this->formatUri($uri);
278
+ return isset($this->_data[self::K_CHAN_URI][$uri]);
279
+ }
280
+
281
+ /**
282
+ * Unset channel uri record
283
+ * @param string $uri
284
+ * @return void
285
+ */
286
+ protected function unsetChannelUriRecord($uri)
287
+ {
288
+ $uri = $this->formatUri($uri);
289
+ unset($this->_data[self::K_CHAN_URI][$uri]);
290
+ }
291
+
292
+ /**
293
+ * Set channel uri record: uri maps to channel record
294
+ * @param string $chanName
295
+ * @param string $uri
296
+ * @return void
297
+ */
298
+ protected function setChannelUriRecord($chanName, $uri)
299
+ {
300
+ $uri = $this->formatUri($uri);
301
+ $this->_data[self::K_CHAN_URI][$uri] = $chanName;
302
+ }
303
+
304
+ /**
305
+ * Get channel name by uri record
306
+ * @param string $uri
307
+ * @return string
308
+ */
309
+ protected function getChannelUriRecord($uri)
310
+ {
311
+ $uri = $this->formatUri($uri);
312
+ return $this->_data[self::K_CHAN_URI][$uri];
313
+ }
314
+
315
+
316
+ /**
317
+ * Unset channel record
318
+ * @param string $chanName
319
+ * @return void
320
+ */
321
+ protected function unsetChannelRecord($chanName)
322
+ {
323
+ unset($this->_data[self::K_CHAN][$chanName]);
324
+ }
325
+
326
+ /**
327
+ * Get channel record
328
+ * @param string $chanName
329
+ * @return array
330
+ */
331
+ protected function getChannelRecord($chanName)
332
+ {
333
+ return $this->_data[self::K_CHAN][$chanName];
334
+ }
335
+
336
+ /**
337
+ * Set channel record
338
+ * @param string $chanName
339
+ * @param string $uri
340
+ * @param mixed $data
341
+ * @param array $packages
342
+ * @return void
343
+ */
344
+ protected function setChannelRecord($chanName, $uri, $data, $packages = array())
345
+ {
346
+ $this->_data[self::K_CHAN][$chanName] = array(
347
+ self::K_NAME=>$chanName,
348
+ self::K_URI=>$uri,
349
+ self::K_CHAN_DATA=>$data,
350
+ self::K_PACK=>$packages
351
+ );
352
+ }
353
+
354
+ /**
355
+ * Set package record
356
+ * @param string $chanName
357
+ * @param string $packageName
358
+ * @param mixed $data
359
+ * @return void
360
+ */
361
+ protected function setPackageRecord($chanName, $packageName, $data, $oneField = null)
362
+ {
363
+ if(null === $oneField) {
364
+ $this->_data[self::K_CHAN][$chanName][self::K_PACK][$packageName] = $data;
365
+ } else {
366
+ $this->_data[self::K_CHAN][$chanName][self::K_PACK][$packageName][$oneField] = $data;
367
+ }
368
+ }
369
+
370
+
371
+
372
+ /**
373
+ * Unset package record
374
+ * @param string $chanName
375
+ * @param string $packageName
376
+ * @return void
377
+ */
378
+ protected function unsetPackageRecord($chanName, $packageName)
379
+ {
380
+ unset($this->_data[self::K_CHAN][$chanName][self::K_PACK][$packageName]);
381
+ }
382
+
383
+ /**
384
+ * Get package record
385
+ * @param string $chanName
386
+ * @param string $packageName
387
+ * @return array
388
+ */
389
+ protected function fetchPackage($chanName, $packageName, $field = null)
390
+ {
391
+ if(null === $field) {
392
+ return $this->_data[self::K_CHAN][$chanName][self::K_PACK][$packageName];
393
+ } else {
394
+ return $this->_data[self::K_CHAN][$chanName][self::K_PACK][$packageName][$field];
395
+ }
396
+ }
397
+
398
+ /**
399
+ * Has package record
400
+ * @param string $chanName
401
+ * @param string $packageName
402
+ * @return bool
403
+ */
404
+ protected function hasPackageRecord($chanName, $packageName)
405
+ {
406
+ return isset($this->_data[self::K_CHAN][$chanName][self::K_PACK][$packageName]);
407
+ }
408
+
409
+ /**
410
+ * Get channel name by alias
411
+ * @param string $alias
412
+ * @return array
413
+ */
414
+ protected function getChannelNameByAlias($alias)
415
+ {
416
+ return $this->_data[self::K_CHAN_ALIAS][$alias];
417
+ }
418
+
419
+ /**
420
+ * Set channel alias
421
+ * @param string $alias
422
+ * @param string $chanName
423
+ * @return void
424
+ */
425
+ protected function setChannelAlias($alias, $chanName)
426
+ {
427
+ $this->_data[self::K_CHAN_ALIAS][$alias] = $chanName;
428
+ }
429
+
430
+ /**
431
+ * Unset channel alias
432
+ * @param string $alias
433
+ * @return void
434
+ */
435
+ protected function unsetChannelAlias($alias)
436
+ {
437
+ unset($this->_data[self::K_CHAN_ALIAS][$alias]);
438
+ }
439
+
440
+ /**
441
+ * Clear all aliases of channel
442
+ * @param string $chanName channel name
443
+ * @return void
444
+ */
445
+ protected function clearAliases($chanName)
446
+ {
447
+ $keys = array_keys($this->_data[self::K_CHAN_ALIAS]);
448
+ foreach ($keys as $key) {
449
+ if($this->_data[self::K_CHAN_ALIAS][$key] == $chanName) {
450
+ unset($this->_data[self::K_CHAN_ALIAS][$key]);
451
+ }
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Add channel alias
457
+ * @param string $chanName
458
+ * @param string $alias
459
+ * @return void
460
+ */
461
+ public function addChannelAlias($chanName, $alias)
462
+ {
463
+ if($this->isChannelName($alias)) {
464
+ return $this->doError("Alias '{$alias}' is existant channel name!");
465
+ }
466
+
467
+ if(!$this->isChannelName($chanName)) {
468
+ return $this->doError("Channel '{$chanName}' doesn't exist");
469
+ }
470
+ $this->setChannelAlias($alias, $chanName);
471
+ $this->save();
472
+ }
473
+
474
+
475
+
476
+ /**
477
+ * Add channel
478
+ * @param $chanName
479
+ * @param $uri
480
+ * @param $data
481
+ * @return void
482
+ */
483
+ public function addChannel($chanName, $uri, $data = array())
484
+ {
485
+ if($this->isChannelName($chanName)) {
486
+ return $this->doError("Channel '{$chanName}' already exist!");
487
+ }
488
+ if($this->isChannelUri($uri)) {
489
+ return $this->doError("Channel with uri= '{$uri}' already exist!");
490
+ }
491
+ if($this->isChannelAlias($chanName)) {
492
+ $this->unsetChannelAlias($chanName);
493
+ }
494
+ $uri = $this->formatUri($uri);
495
+ $this->setChannelRecord($chanName, $uri, $data);
496
+ $this->setChannelUriRecord($chanName, $uri);
497
+ $this->save();
498
+ }
499
+
500
+
501
+
502
+ /**
503
+ * Delete channel
504
+ * @param $chanName
505
+ * @return void
506
+ */
507
+ public function deleteChannel($chanName)
508
+ {
509
+ if($this->isChannelName($chanName)) {
510
+ $record = $this->getChannelRecord($chanName);
511
+ $this->unsetChannelUriRecord($record[self::K_URI]);
512
+ $this->unsetChannelRecord($chanName);
513
+ $this->clearAliases($chanName);
514
+ } elseif($this->isChannelUri($chanName)) {
515
+ $uri = $chanName;
516
+ $chanName = $this->getChannelUriRecord($uri);
517
+ $this->unsetChannelUriRecord($uri);
518
+ $this->unsetChannelRecord($chanName);
519
+ $this->clearAliases($chanName);
520
+ } elseif($this->isChannelAlias($chanName)) {
521
+ $this->unsetChannelAlias($chanName);
522
+ } else {
523
+ return $this->doError("'{$chanName}' was not found in aliases, channel names, channel uris");
524
+ }
525
+ $this->save();
526
+ }
527
+
528
+
529
+ /**
530
+ * Converts channel name, url or alias to channel name
531
+ * throws exception if not found
532
+ * @param srting $chanName
533
+ * @return string
534
+ */
535
+ public function chanName($chanName)
536
+ {
537
+ $channelData = $this->getChannel($chanName);
538
+ if(!$channelData) {
539
+ return $this->doError("Channel '{$chanName}' doesn't exist");
540
+ }
541
+ return $channelData[self::K_NAME];
542
+ }
543
+
544
+ public function chanUrl($chan)
545
+ {
546
+ $channelData = $this->getChannel($chan);
547
+ if(!$channelData) {
548
+ return $this->doError("Channel '{$chan}' doesn't exist");
549
+ }
550
+ return $channelData[self::K_URI];
551
+ }
552
+
553
+
554
+ /**
555
+ * Add package
556
+ * @param Mage_Connect_Package $package
557
+ * @return void
558
+ */
559
+ public function addPackage($package)
560
+ {
561
+ $channel = $this->chanName($package->getChannel());
562
+ $name = $package->getName();
563
+ $record = array (
564
+ self::K_VER => $package->getVersion(),
565
+ self::K_STATE => $package->getStability(),
566
+ self::K_XML => $package->getPackageXml(),
567
+ self::K_NAME => $name,
568
+ self::K_DEPS => array(),
569
+ self::K_PACK_DEPS => array(),
570
+ );
571
+ $this->setPackageRecord($channel, $name, $record);
572
+ $this->setPackageDependencies($channel, $name, $package->getDependencyPackages());
573
+ $this->save();
574
+ }
575
+
576
+
577
+
578
+
579
+ /**
580
+ * Delete package
581
+ * @param string $chanName
582
+ * @param string $package
583
+ * @return void
584
+ */
585
+ public function deletePackage($chanName, $package)
586
+ {
587
+ $chanName = $this->chanName($chanName);
588
+ $this->unsetPackageRecord($chanName, $package);
589
+ $this->save();
590
+ }
591
+
592
+ /**
593
+ * Get package
594
+ * @param sting $chanName
595
+ * @param string $package
596
+ * @return void
597
+ */
598
+ public function getPackage($chanName, $package)
599
+ {
600
+ $chanName = $this->chanName($chanName);
601
+ if($this->hasPackageRecord($chanName, $package)) {
602
+ return $this->fetchPackage($chanName, $package);
603
+ }
604
+ return null;
605
+ }
606
+
607
+ public function getPackageObject($chanName, $package)
608
+ {
609
+ $chanName = $this->chanName($chanName);
610
+ if($this->hasPackageRecord($chanName, $package)) {
611
+ $data = $this->fetchPackage($chanName, $package);
612
+ return new Mage_Connect_Package($data[self::K_XML]);
613
+ }
614
+ throw new Exception("Cannot get package: '{$package}'");
615
+ }
616
+
617
+
618
+ public function hasPackage($chanName, $package, $versionMin = false, $versionMax = false)
619
+ {
620
+ $chanName = $this->chanName($chanName);
621
+ $data = $this->getPackage($chanName, $package);
622
+ if(null === $data) {
623
+ return false;
624
+ }
625
+ $installedVersion = $data[self::K_VER];
626
+ return $this->versionInRange($installedVersion, $versionMin, $versionMax);
627
+ }
628
+
629
+ public function versionInRange($version, $versionMin = false, $versionMax = false)
630
+ {
631
+ if(false === $versionMin) {
632
+ $minOk = true;
633
+ } else {
634
+ $minOk = version_compare($version, $versionMin, ">=");
635
+ }
636
+ if(false === $versionMax) {
637
+ $maxOk = true;
638
+ } else {
639
+ $maxOk = version_compare($version, $versionMax, "<=");
640
+ }
641
+ return $minOk && $maxOk;
642
+ }
643
+
644
+ public function hasVersionRangeIntersect($min1, $max1, $min2, $max2)
645
+ {
646
+ if(version_compare($min1, $min2, ">") && version_compare($max1, $max2, ">")) {
647
+ return false;
648
+ } elseif(version_compare($min1, $min2, "<") && version_compare($max1, $max2, "<")) {
649
+ return false;
650
+ } elseif(version_compare($min1, $min2, ">=") && version_compare($max1, $max2, "<=")) {
651
+ return true;
652
+ } elseif(version_compare($min1, $min2, "<=") && version_compare($max1, $max2, ">=")) {
653
+ return true;
654
+ }
655
+ return false;
656
+ }
657
+
658
+ /**
659
+ * Clear contents to defaults and save
660
+ * @return void
661
+ */
662
+ public function clear()
663
+ {
664
+ $this->setEmptyConfig();
665
+ $this->save();
666
+ }
667
+
668
+ /**
669
+ * Output error - throw exception
670
+ * @param $message
671
+ * @throws Exception
672
+ * @return void
673
+ */
674
+ protected function doError($message)
675
+ {
676
+ throw new Exception($message);
677
+ }
678
+
679
+
680
+
681
+
682
+
683
+ public function compareStabilities($s1, $s2)
684
+ {
685
+ if(!$this->_validator) {
686
+ $this->_validator = new Mage_Connect_Validator();
687
+ }
688
+ return $this->_validator->compareStabilities($s1, $s2);
689
+ }
690
+
691
+
692
+
693
+ public function detectVersionFromRestArray($restData, $argVersionMin = false, $argVersionMax = false, $preferredStability = 'devel')
694
+ {
695
+
696
+ if(!is_array($restData)) {
697
+ return false;
698
+ }
699
+
700
+ foreach($restData as $vData) {
701
+ $stability = trim($vData['s']);
702
+ $version = trim($vData['v']);
703
+ $goodStability = $this->compareStabilities($stability, $preferredStability) >= 0;
704
+ if($goodStability && $this->versionInRange($version, $argVersionMin, $argVersionMax)) {
705
+ return $version;
706
+ }
707
+ }
708
+ return false;
709
+ }
710
+
711
+
712
+ public function setPackageDependencies($chanName, $package, $data)
713
+ {
714
+ $chanName = $this->chanName($chanName);
715
+ if($this->hasPackageRecord($chanName, $package)) {
716
+ $this->setPackageRecord($chanName, $package, $data, self::K_PACK_DEPS);
717
+ $this->save();
718
+ return true;
719
+ }
720
+ return false;
721
+ }
722
+
723
+ public function getPackageDependencies($chanName, $package)
724
+ {
725
+ $chanName = $this->chanName($chanName);
726
+ if($this->hasPackageRecord($chanName, $package)) {
727
+ return $this->fetchPackage($chanName, $package, self::K_PACK_DEPS);
728
+ }
729
+ return false;
730
+ }
731
+
732
+
733
+
734
+ public function setDependencyInfo($chanName, $package, $data)
735
+ {
736
+ $chanName = $this->chanName($chanName);
737
+ if($this->hasPackageRecord($chanName, $package)) {
738
+ $this->setPackageRecord($chanName, $package, $data, self::K_DEPS);
739
+ $this->save();
740
+ return true;
741
+ }
742
+ return false;
743
+ }
744
+
745
+ public function getDependencyInfo($chanName, $package)
746
+ {
747
+ $chanName = $this->chanName($chanName);
748
+ if($this->hasPackageRecord($chanName, $package)) {
749
+ return $this->fetchPackage($chanName, $package, self::K_DEPS);
750
+ }
751
+ return false;
752
+ }
753
+
754
+
755
+
756
+ public function getChannelNames()
757
+ {
758
+ return array_keys($this->_data[self::K_CHAN]);
759
+ }
760
+
761
+ public function getPackagesData($channel = false)
762
+ {
763
+ if(false == $channel) {
764
+ return $this->_data[self::K_CHAN];
765
+ }
766
+
767
+ if(!$this->isChannel($channel)) {
768
+ return array();
769
+ }
770
+ return $this->getChannel($channel);
771
+ }
772
+
773
+ public function specifiedInDependencyList($deps, $chanName, $packageName)
774
+ {
775
+ foreach($deps as $dep) {
776
+ if($chanName == $dep['channel'] && $packageName == $dep['name']) {
777
+ return true;
778
+ }
779
+ }
780
+ return false;
781
+ }
782
+
783
+ public function requiredByOtherPackages($chanName, $packageName, $excludeList = array())
784
+ {
785
+ $out = array();
786
+ foreach($this->_data[self::K_CHAN] as $channel=>$data) {
787
+ foreach($data[self::K_PACK] as $package) {
788
+ if($this->specifiedInDependencyList($excludeList, $channel, $package['name'])) {
789
+ continue;
790
+ }
791
+ $deps = $package[self::K_PACK_DEPS];
792
+ if($this->specifiedInDependencyList($deps, $chanName, $packageName)) {
793
+ $out[] = array('channel'=>$channel, 'name' =>$package['name'], 'version'=>$package['version']);
794
+ }
795
+ }
796
+ }
797
+ return $out;
798
+ }
799
+
800
+
801
+
802
+
803
+ public function getInstalledPackages($chanName = false)
804
+ {
805
+ if(false == $chanName) {
806
+ $data = $this->getChannelNames();
807
+ } elseif($this->isChannel($chanName)) {
808
+ $tmp = $this->getChannel($chanName);
809
+ $data = array($tmp[self::K_NAME]);
810
+ }
811
+ $out = array();
812
+ foreach( $data as $chanName) {
813
+ $channel = $this->getChannel($chanName);
814
+ $out[$chanName] = array();
815
+ foreach($channel[self::K_PACK] as $package=>$data) {
816
+ $out[$chanName][$package] = array();
817
+ foreach(array(self::K_VER, self::K_STATE) as $k) {
818
+ $out[$chanName][$package][$k] = $data[$k];
819
+ }
820
+ }
821
+ }
822
+ return $out;
823
+ }
824
+
825
+
826
+
827
+ /**
828
+ * Check if package conflicts with installed packages
829
+ * Returns:
830
+ * array with conflicts
831
+ * false if no conflicts
832
+ *
833
+ * @param string $chanName
834
+ * @param string $packageName
835
+ * @param string $version
836
+ * @return array|false
837
+ */
838
+ public function hasConflicts($chanName, $packageName, $version)
839
+ {
840
+ $conflicts = array();
841
+ foreach($this->_data[self::K_CHAN] as $channel=>$data) {
842
+ foreach($data[self::K_PACK] as $package) {
843
+ if($channel != $chanName) {
844
+ continue;
845
+ }
846
+ $deps = $package[self::K_PACK_DEPS];
847
+ foreach($deps as $dep) {
848
+ if($dep['name'] != $packageName) {
849
+ continue;
850
+ }
851
+
852
+ if(!$this->versionInRange($version, $dep['min'], $dep['max'])) {
853
+ //var_dump($version, $dep['min'], $dep['max']);
854
+ $conflicts[] = $channel . "/". $package['name'] ." ". $package['version'];
855
+ }
856
+ }
857
+ }
858
+ }
859
+ return count($conflicts) ? $conflicts : false;
860
+ }
861
+
862
+
863
+
864
+
865
+ }
lib/Mage/Connect/Structures/Graph.php ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ class Mage_Connect_Structures_Graph
28
+ {
29
+ protected $_nodes = array();
30
+ protected $_directed = false;
31
+ protected $_nodeClassName = 'Mage_Connect_Structures_Node';
32
+
33
+ const ACYCLIC_VISITED_KEY = 'acyclic-test-visited';
34
+ const SORT_VISITED_KEY = 'topological-sort-visited';
35
+ const SORT_LEVEL_KEY = 'topological-sort-level';
36
+
37
+ /**
38
+ * Constructor
39
+ * @param bool $directed directed graph?
40
+ * @return void
41
+ */
42
+ public function __construct($directed = true)
43
+ {
44
+ $this->_directed = $directed;
45
+ }
46
+
47
+
48
+ /**
49
+ * Is graph directed?
50
+ *
51
+ * @return bool
52
+ */
53
+ public function isDirected()
54
+ {
55
+ return (boolean) $this->_directed;
56
+ }
57
+
58
+ /**
59
+ * Add node to list
60
+ *
61
+ * @param Mage_Connect_Structures_Graph_Node $newNode
62
+ * @return void
63
+ */
64
+ public function addNode(&$newNode)
65
+ {
66
+ if(!$newNode instanceof $this->_nodeClassName) {
67
+ throw new Exception(__METHOD__." : invalid node class, should be instance of: ".$this->_nodeClassName);
68
+ }
69
+ foreach($this->_nodes as $key => $node) {
70
+ if($newNode === $node) {
71
+ throw new Exception(__METHOD__." : received duplicate object");
72
+ }
73
+ }
74
+ $this->_nodes[] =& $newNode;
75
+ $newNode->setGraph($this);
76
+ }
77
+
78
+ /**
79
+ * Remove a Node from the Graph
80
+ * @param Mage_Connect_Structures_Graph_Node $node
81
+ */
82
+ public function removeNode(&$node)
83
+ {
84
+
85
+ }
86
+
87
+ /**
88
+ * Return set of nodes
89
+ * @return array
90
+ */
91
+ public function &getNodes()
92
+ {
93
+ return $this->_nodes;
94
+ }
95
+
96
+ /**
97
+ * Is asyclic
98
+ * @return unknown_type
99
+ */
100
+ public function isAcyclic()
101
+ {
102
+ if (!$this->isDirected()) {
103
+ return false;
104
+ }
105
+ return self::_isAcyclic($this);
106
+ }
107
+
108
+ /**
109
+ *
110
+ * This is a variant of Graph::inDegree which does
111
+ * not count nodes marked as visited.
112
+ *
113
+ * @return integer
114
+ */
115
+ protected static function _nonVisitedInDegree(&$node, $metadataKey)
116
+ {
117
+ $result = 0;
118
+ $graphNodes =& $node->getGraph()->getNodes();
119
+ foreach (array_keys($graphNodes) as $key) {
120
+ if ((!$graphNodes[$key]->getMetadata($metadataKey)) && $graphNodes[$key]->connectsTo($node)) {
121
+ $result++;
122
+ }
123
+ }
124
+ return $result;
125
+ }
126
+
127
+ /**
128
+ * Is graph acyclic?
129
+ * @param $graph
130
+ * @return bool
131
+ */
132
+ protected static function _isAcyclic(&$graph)
133
+ {
134
+ // Mark every node as not visited
135
+ $nodes =& $graph->getNodes();
136
+ $nodeKeys = array_keys($nodes);
137
+ $refGenerator = array();
138
+ foreach($nodeKeys as $key) {
139
+ $refGenerator[] = false;
140
+ $nodes[$key]->setMetadata(self::ACYCLIC_VISITED_KEY, $refGenerator[sizeof($refGenerator) - 1]);
141
+ }
142
+
143
+ // Iteratively peel off leaf nodes
144
+ do {
145
+ // Find out which nodes are leafs (excluding visited nodes)
146
+ $leafNodes = array();
147
+ foreach($nodeKeys as $key) {
148
+ if ((!$nodes[$key]->getMetadata(self::ACYCLIC_VISITED_KEY)) &&
149
+ self::_nonVisitedInDegree($nodes[$key], self::ACYCLIC_VISITED_KEY) == 0) {
150
+ $leafNodes[] =& $nodes[$key];
151
+ }
152
+ }
153
+ // Mark leafs as visited
154
+ for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
155
+ $visited =& $leafNodes[$i]->getMetadata(self::ACYCLIC_VISITED_KEY);
156
+ $visited = true;
157
+ $leafNodes[$i]->setMetadata(self::ACYCLIC_VISITED_KEY, $visited);
158
+ }
159
+ } while (sizeof($leafNodes) > 0);
160
+
161
+
162
+ // If graph is a DAG, there should be no non-visited nodes.
163
+ // Let's try to prove otherwise
164
+ $result = true;
165
+ foreach($nodeKeys as $key) {
166
+ if (!$nodes[$key]->getMetadata(self::ACYCLIC_VISITED_KEY)) {
167
+ $result = false;
168
+ break;
169
+ }
170
+ }
171
+
172
+ // Cleanup visited marks
173
+ foreach($nodeKeys as $key) {
174
+ $nodes[$key]->unsetMetadata(self::ACYCLIC_VISITED_KEY);
175
+ }
176
+
177
+ return $result;
178
+ }
179
+
180
+ /**
181
+ *
182
+ * sort returns the graph's nodes, sorted by topological order.
183
+ *
184
+ * The result is an array with
185
+ * as many entries as topological levels.
186
+ *
187
+ * Each entry in this array is an array of nodes within
188
+ * the given topological level.
189
+ *
190
+ * @return array
191
+ */
192
+ public function topologicalSort()
193
+ {
194
+ // We only sort graphs
195
+ self::_topologicalSort($this);
196
+ $result = array();
197
+ // Fill out result array
198
+ $nodes =& $this->getNodes();
199
+ $nodeKeys = array_keys($nodes);
200
+ foreach($nodeKeys as $key) {
201
+ $k = $nodes[$key]->getMetadata(self::SORT_LEVEL_KEY);
202
+ if (!array_key_exists($k, $result)) {
203
+ $result[$k] = array();
204
+ }
205
+ $result[$k][] =& $nodes[$key];
206
+ $nodes[$key]->unsetMetadata(self::SORT_LEVEL_KEY);
207
+ }
208
+ return $result;
209
+ }
210
+
211
+ protected static function _topologicalSort(&$graph)
212
+ {
213
+ // Mark every node as not visited
214
+ $nodes =& $graph->getNodes();
215
+ $nodeKeys = array_keys($nodes);
216
+ $refGenerator = array();
217
+ foreach($nodeKeys as $key) {
218
+ $refGenerator[] = false;
219
+ $nodes[$key]->setMetadata(self::SORT_VISITED_KEY, $refGenerator[sizeof($refGenerator) - 1]);
220
+ }
221
+
222
+ // Iteratively peel off leaf nodes
223
+ $topologicalLevel = 0;
224
+ do {
225
+ // Find out which nodes are leafs (excluding visited nodes)
226
+ $leafNodes = array();
227
+ foreach($nodeKeys as $key) {
228
+ if ((!$nodes[$key]->getMetadata(self::SORT_VISITED_KEY)) && self::_nonVisitedInDegree($nodes[$key], self::SORT_VISITED_KEY) == 0) {
229
+ $leafNodes[] =& $nodes[$key];
230
+ }
231
+ }
232
+ // Mark leafs as visited
233
+ $refGenerator[] = $topologicalLevel;
234
+ for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
235
+ $visited =& $leafNodes[$i]->getMetadata(self::SORT_VISITED_KEY);
236
+ $visited = true;
237
+ $leafNodes[$i]->setMetadata(self::SORT_VISITED_KEY, $visited);
238
+ $leafNodes[$i]->setMetadata(self::SORT_LEVEL_KEY, $refGenerator[sizeof($refGenerator) - 1]);
239
+ }
240
+ $topologicalLevel++;
241
+ } while (sizeof($leafNodes) > 0);
242
+
243
+ foreach($nodeKeys as $key) {
244
+ $nodes[$key]->unsetMetadata(self::SORT_VISITED_KEY);
245
+ }
246
+ }
247
+
248
+ }
lib/Mage/Connect/Structures/Node.php ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ class Mage_Connect_Structures_Node
28
+ {
29
+
30
+ protected $_data = null;
31
+ protected $_metadata = array();
32
+ protected $_arcs = array();
33
+ protected $_graph = null;
34
+
35
+ /**
36
+ * Node graph getter
37
+ *
38
+ * @return Mage_Connect_Structures_Graph
39
+ */
40
+ public function &getGraph()
41
+ {
42
+ return $this->_graph;
43
+ }
44
+
45
+ /**
46
+ *
47
+ * Node graph setter.
48
+ * This method should not be called directly.
49
+ * Use Graph::addNode instead.
50
+ *
51
+ * @param $graph
52
+ */
53
+ public function setGraph(&$graph)
54
+ {
55
+ $this->_graph =& $graph;
56
+ }
57
+
58
+ /**
59
+ *
60
+ * Node data getter.
61
+ *
62
+ * Each graph node can contain a reference to one variable. This is the getter for that reference.
63
+ *
64
+ * @return mixed Data stored in node
65
+ * @access public
66
+ */
67
+ public function &getData()
68
+ {
69
+ return $this->_data;
70
+ }
71
+
72
+ /**
73
+ * Node data setter
74
+ *
75
+ * Each graph node can contain a reference to one variable. This is the setter for that reference.
76
+ *
77
+ * @return mixed Data to store in node
78
+ */
79
+ public function setData($data)
80
+ {
81
+ $this->_data =& $data;
82
+ }
83
+
84
+ /**
85
+ *
86
+ * Test for existence of metadata under a given key.
87
+ *
88
+ * @param string Key to test
89
+ * @return boolean
90
+ * @access public
91
+ */
92
+ public function metadataKeyExists($key)
93
+ {
94
+ return array_key_exists($key, $this->_metadata);
95
+ }
96
+
97
+ /**
98
+ *
99
+ * Get node metadata
100
+ *
101
+ * @param string $key
102
+ * @param boolean $nullIfNonexistent (defaults to false).
103
+ * @return mixed
104
+ */
105
+ public function & getMetadata($key, $nullIfNonexistent = false)
106
+ {
107
+ if (array_key_exists($key, $this->_metadata)) {
108
+ return $this->_metadata[$key];
109
+ } elseif ($nullIfNonexistent) {
110
+ $a = null;
111
+ return $a;
112
+ } else {
113
+ throw new Exception(__METHOD__." : requested key doesn't exist: {$key}");
114
+ }
115
+ }
116
+
117
+ /**
118
+ *
119
+ * Delete metadata by key
120
+ *
121
+ * @param string Key
122
+ */
123
+ public function unsetMetadata($key)
124
+ {
125
+ if (array_key_exists($key, $this->_metadata)) {
126
+ unset($this->_metadata[$key]);
127
+ }
128
+
129
+ }
130
+
131
+ /**
132
+ *
133
+ * Node metadata setter
134
+ *
135
+ * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an
136
+ * associative array or in a dictionary. This method stores data under the given key. If the key already exists,
137
+ * previously stored data is discarded.
138
+ *
139
+ * @param string $key
140
+ * @param mixed $data
141
+ */
142
+ public function setMetadata($key, $data)
143
+ {
144
+ $this->_metadata[$key] =& $data;
145
+ }
146
+
147
+ protected function _connectTo(&$destinationNode)
148
+ {
149
+ $this->_arcs[] =& $destinationNode;
150
+ }
151
+
152
+ /**
153
+ * Connect this node to another one.
154
+ * If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created.
155
+ * @param Structures_Graph Node to connect to
156
+ */
157
+ public function connectTo(&$destinationNode)
158
+ {
159
+ $class = get_class($this);
160
+ if(!$destinationNode instanceof $class) {
161
+ throw new Exception(__METHOD__." : argument should be instance of {$class}");
162
+ }
163
+
164
+ // Nodes must already be in graphs to be connected
165
+ if ($this->_graph == null) {
166
+ throw new Exception(__METHOD__." : tried to connect to null graph");
167
+ }
168
+
169
+ if ($destinationNode->getGraph() == null) {
170
+ throw new Exception(__METHOD__." : tried to connect to node that is not connected to any graph");
171
+ }
172
+
173
+ // Connect here
174
+ $this->_connectTo($destinationNode);
175
+ // If graph is undirected, connect back
176
+ if (!$this->_graph->isDirected()) {
177
+ $destinationNode->_connectTo($this);
178
+ }
179
+ }
180
+
181
+
182
+ /**
183
+ * Return nodes connected to this one.
184
+ * @return array
185
+ */
186
+ public function getNeighbours()
187
+ {
188
+ return $this->_arcs;
189
+ }
190
+
191
+ /**
192
+ * Test wether this node has an arc to the target node
193
+ * Returns true if the two nodes are connected
194
+ * @return boolean
195
+ */
196
+ public function connectsTo(&$target)
197
+ {
198
+ $arcKeys = array_keys($this->_arcs);
199
+ foreach($arcKeys as $key) {
200
+ $arc =& $this->_arcs[$key];
201
+ if ($target === $arc) {
202
+ return true;
203
+ }
204
+ }
205
+ return false;
206
+ }
207
+
208
+ /**
209
+ * Calculate the in degree of the node.
210
+ *
211
+ * The indegree for a node is the number of arcs
212
+ * entering the node.
213
+ *
214
+ * For non directed graphs:
215
+ * always outdegree = indegree.
216
+ *
217
+ * @return int
218
+ */
219
+ public function inDegree()
220
+ {
221
+ $result = 0;
222
+
223
+ if ($this->_graph == null) {
224
+ return $result;
225
+ }
226
+ if (!$this->_graph->isDirected()) {
227
+ return $this->outDegree();
228
+ }
229
+
230
+ $graphNodes =& $this->_graph->getNodes();
231
+ foreach (array_keys($graphNodes) as $key) {
232
+ if ($graphNodes[$key]->connectsTo($this)) {
233
+ $result++;
234
+ }
235
+ }
236
+ return $result;
237
+
238
+ }
239
+
240
+ /**
241
+ * Calculate the out degree of the node.
242
+ *
243
+ * The outdegree for a node is the number of arcs exiting the node.
244
+ * For non directed graphs:
245
+ * always outdegree = indegree.
246
+ *
247
+ * @return int
248
+ */
249
+ public function outDegree()
250
+ {
251
+ if ($this->_graph == null) {
252
+ return 0;
253
+ }
254
+ return count($this->_arcs);
255
+ }
256
+
257
+ }
lib/Mage/Connect/Validator.php ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to validate string resources
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+
35
+ class Mage_Connect_Validator
36
+ {
37
+ protected static $_stability = array(0=>'devel',1=>'alpha',2=>'beta',3=>'stable');
38
+
39
+
40
+ public static function getStabilities()
41
+ {
42
+ return self::$_stability;
43
+ }
44
+
45
+
46
+
47
+ /**
48
+ * Compare stabilities. Returns:
49
+ *
50
+ * -1 if the first stability is lower than the second
51
+ * 0 if they are equal
52
+ * 1 if the second is lower.
53
+ * @param $s1
54
+ * @param $s2
55
+ * @return int
56
+ */
57
+ public function compareStabilities($s1, $s2)
58
+ {
59
+ $list = $this->getStabilities();
60
+ $tmp = array_combine(array_values($list),array_keys($list));
61
+
62
+ if(!isset($tmp[$s1], $tmp[$s2])) {
63
+ throw new Exception("Invalid stability in compareStabilities argument");
64
+ }
65
+
66
+
67
+ // 'stable' turns to 3
68
+ // 'devel' turns to 0
69
+ $s1 = $tmp[$s1];
70
+ $s2 = $tmp[$s2];
71
+ if($s1 === $s2) {
72
+ return 0;
73
+ } elseif($s1 > $s2) {
74
+ return 1;
75
+ } elseif($s1 < $s2) {
76
+ return -1;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Constructor
82
+ */
83
+ public function __construct()
84
+ {
85
+
86
+ }
87
+
88
+ /**
89
+ * Validate max len of string
90
+ * @param string $str
91
+ * @param int $maxLen
92
+ * @return bool
93
+ */
94
+ public function validateMaxLen($str, $maxLen)
95
+ {
96
+ return strlen((string) $str) <= (int) $maxLen;
97
+ }
98
+
99
+ /**
100
+ * Validate channel name and url
101
+ *
102
+ * @param mixed $str
103
+ * @return bool
104
+ */
105
+ public function validateChannelNameOrUri($str)
106
+ {
107
+ return ( $this->validateUrl($str) || $this->validatePackageName($str));
108
+ }
109
+
110
+ /**
111
+ * Validate License url
112
+ *
113
+ * @param mixed $str
114
+ * @return boolean
115
+ */
116
+ public function validateLicenseUrl($str)
117
+ {
118
+ if ($str) {
119
+ return ( $this->validateUrl($str) || $this->validatePackageName($str));
120
+ }
121
+ return true;
122
+ }
123
+
124
+ /**
125
+ * Validate compatible data
126
+ * @param array $data
127
+ * @return bool
128
+ */
129
+ public function validateCompatible(array $data)
130
+ {
131
+ if(!count($data)) {
132
+ /**
133
+ * Allow empty
134
+ */
135
+ return true;
136
+ }
137
+ $count = 0;
138
+ foreach($data as $k=>$v) {
139
+ foreach(array('name','channel','min','max') as $fld) {
140
+ $$fld = trim($v[$fld]);
141
+ }
142
+ $count++;
143
+
144
+ $res = $this->validateUrl($channel) && strlen($channel);
145
+ if(!$res) {
146
+ $this->addError("Invalid or empty channel in compat. #{$count}");
147
+ }
148
+
149
+ $res = $this->validatePackageName($name) && strlen($name);
150
+ if(!$res) {
151
+ $this->addError("Invalid or empty name in compat. #{$count}");
152
+ }
153
+ $res1 = $this->validateVersion($min);
154
+ if(!$res1) {
155
+ $this->addError("Invalid or empty minVersion in compat. #{$count}");
156
+ }
157
+ $res2 = $this->validateVersion($max);
158
+ if(!$res2) {
159
+ $this->addError("Invalid or empty maxVersion in compat. #{$count}");
160
+ }
161
+ if($res1 && $res2 && $this->versionLower($max, $min)) {
162
+ $this->addError("Max version is lower than min in compat #{$count}");
163
+ }
164
+
165
+ }
166
+ return ! $this->hasErrors();
167
+ }
168
+
169
+ /**
170
+ * Validate authors of package
171
+ * @param array $authors
172
+ * @return bool
173
+ */
174
+ public function validateAuthors(array $authors)
175
+ {
176
+ if(!count($authors)) {
177
+ $this->addError('Empty authors section');
178
+ return false;
179
+ }
180
+ $count = 0;
181
+ foreach($authors as $k=>$v) {
182
+ $count++;
183
+ array_map('trim', $v);
184
+ $name = $v['name'];
185
+ $login = $v['user'];
186
+ $email = $v['email'];
187
+ $res = $this->validateMaxLen($name, 256) && strlen($name);
188
+ if(!$res) {
189
+ $this->addError("Invalid or empty name for author #{$count}");
190
+ }
191
+ $res = $this->validatePackageName($login) && strlen($login);
192
+ if(!$res) {
193
+ $this->addError("Invalid or empty login for author #{$count}");
194
+ }
195
+ $res = $this->validateEmail($email);
196
+ if(!$res) {
197
+ $this->addError("Invalid or empty email for author #{$count}");
198
+ }
199
+ }
200
+ return ! $this->hasErrors();
201
+ }
202
+
203
+
204
+ /**
205
+ * Validator errors
206
+ * @var array
207
+ */
208
+ private $_errors = array();
209
+
210
+ /**
211
+ * Add error
212
+ * @param string $err
213
+ * @return
214
+ */
215
+ private function addError($err)
216
+ {
217
+ $this->_errors[] = $err;
218
+ }
219
+
220
+ /**
221
+ * Set validator errors
222
+ * @param array $err
223
+ * @return
224
+ */
225
+
226
+ private function setErrors(array $err)
227
+ {
228
+ $this->_errors = $err;
229
+ }
230
+
231
+ /**
232
+ * Clear validator errors
233
+ * @return
234
+ */
235
+ private function clearErrors()
236
+ {
237
+ $this->_errors = array();
238
+ }
239
+
240
+ /**
241
+ * Check if there are validator errors set
242
+ * @return unknown_type
243
+ */
244
+ public function hasErrors()
245
+ {
246
+ return count($this->_errors) != 0;
247
+ }
248
+
249
+
250
+ /**
251
+ * Get errors
252
+ * @param bool $clear if true after this call erros will be cleared
253
+ * @return array
254
+ */
255
+ public function getErrors($clear = true)
256
+ {
257
+ $out = $this->_errors;
258
+ if($clear) {
259
+ $this->clearErrors();
260
+ }
261
+ return $out;
262
+ }
263
+
264
+ /**
265
+ * Validate URL
266
+ * @param string $str
267
+ * @return bool
268
+ */
269
+ public function validateUrl($str)
270
+ {
271
+ $regex = "@([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}|"
272
+ ."(((news|telnet|nttp|file|http|ftp|https)://)|(www|ftp)"
273
+ ."[-A-Za-z0-9]*\\.)[-A-Za-z0-9\\.]+)(:[0-9]*)?@i";
274
+ return preg_match($regex, $str);
275
+ }
276
+
277
+
278
+ /**
279
+ * Validates package stability
280
+ * @param string $str
281
+ * @return bool
282
+ */
283
+ public function validateStability($str)
284
+ {
285
+ return in_array(strval($str), self::$_stability);
286
+ }
287
+
288
+ /**
289
+ * Validate date format
290
+ * @param $date
291
+ * @return bool
292
+ */
293
+ public function validateDate($date)
294
+ {
295
+ $subs = null;
296
+ $check1 = preg_match("/^([\d]{4})-([\d]{2})-([\d]{2})$/i", $date, $subs);
297
+ if(!$check1) {
298
+ return false;
299
+ }
300
+ return checkdate($subs[2], $subs[3], $subs[1]);
301
+ }
302
+
303
+
304
+ /**
305
+ * Validate email
306
+ * @param string $email
307
+ * @return bool
308
+ */
309
+ public function validateEmail($email)
310
+ {
311
+ return preg_match("/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $email);
312
+ }
313
+
314
+ /**
315
+ * Validate package name
316
+ * @param $name
317
+ * @return bool
318
+ */
319
+ public function validatePackageName($name)
320
+ {
321
+ return preg_match("/^[a-zA-Z0-9_-]+$/i", $name);
322
+ }
323
+
324
+ /**
325
+ * Validate version number
326
+ * @param string $version
327
+ * @return bool
328
+ */
329
+ public function validateVersion($version)
330
+ {
331
+ return preg_match("/^[\d]+\.[\d]+\.[\d]+([[:alnum:]\.\-\_]+)?$/i", $version);
332
+ }
333
+
334
+ /**
335
+ * Check versions are equal
336
+ * @param string $v1
337
+ * @param string $v2
338
+ * @return bool
339
+ */
340
+ public function versionEqual($v1, $v2)
341
+ {
342
+ return version_compare($v1, $v2, "==");
343
+ }
344
+
345
+ /**
346
+ * Check version $v1 <= $v2
347
+ * @param string $v1
348
+ * @param string $v2
349
+ * @return bool
350
+ */
351
+ public function versionLowerEqual($v1, $v2)
352
+ {
353
+ return version_compare($v1, $v2, "le");
354
+ }
355
+
356
+
357
+ /**
358
+ * Check if version $v1 lower than $v2
359
+ * @param string $v1
360
+ * @param string $v2
361
+ * @return bool
362
+ */
363
+ public function versionLower($v1, $v2)
364
+ {
365
+ return version_compare($v1, $v2, "<");
366
+ }
367
+
368
+ /**
369
+ * Check version $v1 >= $v2
370
+ * @param string $v1
371
+ * @param string $v2
372
+ * @return bool
373
+ */
374
+ public function versionGreaterEqual($v1, $v2)
375
+ {
376
+ return version_compare($v1, $v2, "ge");
377
+ }
378
+
379
+
380
+ /**
381
+ * Generic regex validation
382
+ * @param string $str
383
+ * @param string $regex
384
+ * @return bool
385
+ */
386
+ public function validateRegex($str, $regex)
387
+ {
388
+ return preg_match($regex, $str);
389
+ }
390
+
391
+
392
+ /**
393
+ * Check if PHP extension loaded
394
+ * @param string $name Extension name
395
+ * @return bool
396
+ */
397
+ public function validatePhpExtension($name)
398
+ {
399
+ return extension_loaded($name);
400
+ }
401
+
402
+
403
+ public function validatePHPVersion($min, $max, $ver = PHP_VERSION)
404
+ {
405
+ $minAccepted = true;
406
+ if($min) {
407
+ $minAccepted = version_compare($ver, $min, ">=");
408
+ }
409
+ $maxAccepted = true;
410
+ if($max) {
411
+ $maxAccepted = version_compate($ver, $max, "<=");
412
+ }
413
+ return (bool) $minAccepted && $maxAccepted;
414
+ }
415
+
416
+
417
+ }
lib/Mage/DB/Exception.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Db
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * TODO
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Db
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_DB_Exception extends Exception {
35
+
36
+ }
lib/Mage/DB/Mysqli.php ADDED
@@ -0,0 +1,532 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Db
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Mysqli database connector
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Db
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_DB_Mysqli
35
+ {
36
+ /**
37
+ * Default port
38
+ * @var int
39
+ */
40
+ const DEFAULT_PORT = 3306;
41
+
42
+ /**
43
+ * Table name escaper
44
+ * @var string
45
+ */
46
+ const TABLE_ESCAPER = '`';
47
+
48
+ /**
49
+ * Value escaper
50
+ * @var unknown_type
51
+ */
52
+ const VALUE_ESCAPER = '"';
53
+
54
+ /**
55
+ * Connection
56
+ * @var mysqli
57
+ */
58
+ protected $conn;
59
+ /**
60
+ * Fetch mode
61
+ * @var unknown_type
62
+ */
63
+ private $fetch_mode = MYSQLI_ASSOC;
64
+
65
+
66
+ /**
67
+ * Constructor
68
+ */
69
+ public function __construct()
70
+ {
71
+ $this->conn = new mysqli();
72
+ }
73
+
74
+ /**
75
+ * Connect
76
+ * @param string $host
77
+ * @param string $user
78
+ * @param string $paswd
79
+ * @param string $db
80
+ * @param int $port
81
+ * @return mixed
82
+ */
83
+ public function connect($host, $user, $paswd, $db, $port = self::DEFAULT_PORT)
84
+ {
85
+ $port = (int) $port;
86
+ $res = @$this->conn->connect($host, $user, $paswd, $db, $port);
87
+ if(0 !== mysqli_connect_errno($this->conn)) {
88
+ throw new Mage_DB_Exception(mysqli_connect_error($this->conn));
89
+ }
90
+ return $res;
91
+ }
92
+
93
+ /**
94
+ * Select database
95
+ * @param $db db name
96
+ * @return mixed
97
+ */
98
+ public function selectDb($db)
99
+ {
100
+ $res = mysqli_select_db($this->conn, $db);
101
+ return $res;
102
+ }
103
+
104
+ /**
105
+ * Escape string
106
+ * @param string $str
107
+ * @return string
108
+ */
109
+ public function escapeString($str)
110
+ {
111
+ return mysqli_real_escape_string($this->conn, $str);
112
+ }
113
+
114
+ /**
115
+ * Escape table name
116
+ * @param string $table
117
+ * @return string
118
+ */
119
+ public function escapeTableName($table)
120
+ {
121
+ return self::TABLE_ESCAPER.$this->escapeString($table).self::TABLE_ESCAPER;
122
+ }
123
+
124
+ /**
125
+ * Escape field name
126
+ * @param stirng $fld
127
+ * @return string
128
+ */
129
+ public function escapeFieldName($fld)
130
+ {
131
+ return self::TABLE_ESCAPER.$this->escapeString($fld).self::TABLE_ESCAPER;
132
+ }
133
+
134
+ /**
135
+ * Escape field value
136
+ * @param $data
137
+ * @return string
138
+ */
139
+ public function escapeFieldValue($data)
140
+ {
141
+ return self::VALUE_ESCAPER.$this->escapeString($data).self::VALUE_ESCAPER;
142
+ }
143
+
144
+ /**
145
+ * Fetch all rows
146
+ * @param $sql
147
+ * @return array
148
+ */
149
+ public function fetchAll($sql)
150
+ {
151
+ $res = $this->query($sql);
152
+ for($out = array(); $row = $res->fetch_array($this->fetch_mode); $out[] = $row);
153
+ return $out;
154
+ }
155
+
156
+ /**
157
+ * Fetch one row
158
+ * @param $sql
159
+ * @return array
160
+ */
161
+ public function fetchOne($sql)
162
+ {
163
+ $res = $this->query($sql);
164
+ return $res->fetch_array($this->fetch_mode);
165
+ }
166
+
167
+ /**
168
+ * Fetch rows grouped by key
169
+ * @param $sql
170
+ * @param $key
171
+ * @param $arrayMode force Array mode
172
+ * @return array
173
+ */
174
+ public function fetchGroupedArrayByKey($sql, $key, $arrayMode = true)
175
+ {
176
+ $res = $this->query($sql);
177
+ $out = array();
178
+ while($row = $res->fetch_array(MYSQLI_ASSOC)) {
179
+ if($arrayMode) {
180
+ if(!isset($out[$row[$key]])) {
181
+ $out[$row[$key]] = array();
182
+ }
183
+ $out[$row[$key]][] = $row;
184
+ } else {
185
+ $out[$row[$key]] = $row;
186
+ }
187
+ }
188
+ return $out;
189
+ }
190
+
191
+ /**
192
+ * Fetch one field from all rows and place to list
193
+ * @param string $sql
194
+ * @param string $fld
195
+ * @return array
196
+ */
197
+ public function fetchOneFieldAll($sql, $fld)
198
+ {
199
+ $res = $this->query($sql);
200
+ for($out = array(); $row = $res->fetch_array($this->fetch_mode); $out[] = $row[$fld]);
201
+ return $out;
202
+ }
203
+
204
+ /**
205
+ * List one item
206
+ * @param $table
207
+ * @param $condition
208
+ * @return array
209
+ */
210
+ public function listOne($table, $condition)
211
+ {
212
+ $table = $this->escapeTableName($table);
213
+ $sql = "SELECT * FROM {$table} WHERE {$condition}";
214
+ return $this->fetchOne($sql);
215
+ }
216
+
217
+ /**
218
+ * List items in table by condition
219
+ * @param string $table table name
220
+ * @param string $condition optional, if empty 1=1 is used
221
+ * @return array
222
+ */
223
+ public function listAll($table, $condition = '1=1')
224
+ {
225
+ $table = $this->escapeTableName($table);
226
+ $sql = "SELECT * FROM {$table} WHERE {$condition}";
227
+ return $this->fetchAll($sql);
228
+ }
229
+
230
+ /**
231
+ * List by key single entry
232
+ * @param string $table table name
233
+ * @param string $value field value
234
+ * @param string $key field name
235
+ * @return array
236
+ */
237
+ public function listByKeyOne($table, $value, $key = 'id')
238
+ {
239
+ $table = $this->escapeTableName($table);
240
+ $key = $this->escapeFieldName($key);
241
+ $value = $this->escapeFieldValue($value);
242
+ $sql = "SELECT * FROM {$table} WHERE {$key} = {$value}";
243
+ return $this->fetchOne($sql);
244
+ }
245
+
246
+ /**
247
+ * List by key all rows in table
248
+ * @param string $table table name
249
+ * @param string $value value of key field
250
+ * @param string $key key field name
251
+ * @param string $add additional conditions
252
+ * @return array
253
+ */
254
+ public function listByKeyAll($table, $value, $key = 'id', $add = '')
255
+ {
256
+ $table = $this->escapeTableName($table);
257
+ $key = $this->escapeFieldName($key);
258
+ $value = $this->escapeFieldValue($value);
259
+ $sql = "SELECT * FROM {$table} WHERE {$key} = {$value} {$add}";
260
+ return $this->fetchAll($sql);
261
+ }
262
+
263
+ /**
264
+ * List by key grouped
265
+ * @param string $table
266
+ * @param string $key
267
+ * @param bool $forcedArrayMode
268
+ * @return array
269
+ */
270
+ public function listByKeyGrouped($table, $key = 'id', $forcedArrayMode = false)
271
+ {
272
+ $table = $this->escapeTableName($table);
273
+ $sql = "SELECT * FROM {$table}";
274
+ return $this->fetchGroupedArrayByKey($sql, $key, $forcedArrayMode);
275
+ }
276
+
277
+
278
+ /**
279
+ * Escape field names
280
+ * @param array $arrNames
281
+ * @return array
282
+ */
283
+ public function escapeFieldNames(array $arrNames)
284
+ {
285
+ $out = array();
286
+ for ($i=0, $c = count($arrNames) ; $i<$c; $i++) {
287
+ $out[] = $this->escapeFieldName($arrNames[$i]);
288
+ }
289
+ return $out;
290
+ }
291
+
292
+ /**
293
+ * Escape field values
294
+ * @param array $arrNames
295
+ * @return array
296
+ */
297
+ public function escapeFieldValues(array $arrNames)
298
+ {
299
+ $out = array();
300
+ for ($i=0, $c = count($arrNames) ; $i<$c; $i++) {
301
+ if($arrNames[$i] !== 'LAST_INSERT_ID()') {
302
+ $out[] = $this->escapeFieldValue($arrNames[$i]);
303
+ } else {
304
+ $out[] = $arrNames[$i];
305
+ }
306
+ }
307
+ return $out;
308
+ }
309
+
310
+
311
+ /**
312
+ * Throw connect exception
313
+ * @throws Mage_DB_Exception
314
+ * @return void
315
+ */
316
+ protected function throwConnectException()
317
+ {
318
+ throw new Mage_DB_Exception($this->conn->connect_error);
319
+ }
320
+
321
+ /**
322
+ * Query - perform with throwing exception on error
323
+ * @param sting $sql query
324
+ * @throws Mage_DB_Exception
325
+ * @return mixed
326
+ */
327
+ public function query($sql)
328
+ {
329
+ $res = $this->unsafeQuery($sql);
330
+ if(!$res) {
331
+ throw new Mage_DB_Exception($this->conn->error);
332
+ }
333
+ return $res;
334
+ }
335
+
336
+ /**
337
+ * Unsafe query - perform without error checking
338
+ * @param string $sql query
339
+ * @return mixed
340
+ */
341
+ public function unsafeQuery($sql)
342
+ {
343
+ return $this->conn->query($sql);
344
+ }
345
+
346
+ /**
347
+ * Insert assoc array to table
348
+ * @param string $table
349
+ * @param array $data
350
+ * @param bool $replace
351
+ * @return mixed
352
+ */
353
+ public function insertAssocOne($table, array $data, $replace = false) {
354
+ $keys = $this->escapeFieldNames(array_keys($data));
355
+ $keys = "(" . implode (",", $keys) . ")";
356
+ $table = $this->escapeTableName($table);
357
+ $sql = $replace ? "REPLACE INTO {$table} " : "INSERT INTO {$table} ";
358
+ $values = $this->escapeFieldValues(array_values($data));
359
+ $values = " VALUES (" . implode (",", $values) . ")";
360
+ $sql .= $keys . $values;
361
+ return $this->query($sql);
362
+ }
363
+
364
+ /**
365
+ * Insert several records to table
366
+ * @param string $table
367
+ * @param array $data
368
+ * @param bool $replace use REPLACE INTO instead of INSERT INTO
369
+ * @return array
370
+ */
371
+ public function insertAssocMultiple($table, array $data, $replace = false, $excludeFields = array())
372
+ {
373
+ $table = $this->escapeTableName($table);
374
+ $sql = $replace ? "REPLACE INTO {$table} " : "INSERT INTO {$table} ";
375
+ $keys = array_keys($data[0]);
376
+ $excluded = array();
377
+ for($i = 0, $c = count($excludeFields); $i < $c; $i++) {
378
+ $k = $excludeFields[$i];
379
+ if(isset($keys[$k])) {
380
+ $excluded [] = $k;
381
+ unset($keys[$k]);
382
+ }
383
+ }
384
+
385
+ $keys = $this->escapeFieldNames($keys);
386
+ $sql .= " ( ";
387
+ for($i = 0, $c = count($keys); $i<$c; $i++) {
388
+ $sql .= $keys[$i];
389
+ if($i!=$c-1) {
390
+ $sql .= ",";
391
+ }
392
+ }
393
+ $sql .= " ) VALUES ";
394
+ for($i = 0, $c = count($data); $i<$c; $i++) {
395
+ $row = $data[$i];
396
+ for ($j = 0, $jc = count($excluded); $j<$jc; $j++) {
397
+ unset($data[$excluded[$j]]);
398
+ }
399
+ $values = $this->escapeFieldValues(array_values($row));
400
+ $sql .= "( ";
401
+ for ($j = 0, $jc = count($values); $j < $jc; $j++) {
402
+ $sql .= $values[$j];
403
+ if($j != $jc-1) {
404
+ $sql .= ",";
405
+ }
406
+ }
407
+ $sql .= " )";
408
+ if($i!=$c-1) {
409
+ $sql .= ",";
410
+ }
411
+ }
412
+ return $this->query($sql);
413
+ }
414
+
415
+
416
+ /**
417
+ * Set table data by condition
418
+ * @param $table
419
+ * @param $data
420
+ * @param $condition
421
+ * @return mixed
422
+ */
423
+ public function updateAssoc($table, array $data, $condition = '1=1')
424
+ {
425
+ $table = $this->escapeTableName($table);
426
+ $set = array();
427
+ foreach($data as $k=>$v) {
428
+ $k = $this->escapeFieldName($k);
429
+ $v = $this->escapeFieldValue($v);
430
+ $set[] = $k . " = " . $v;
431
+ }
432
+ $set = implode(",", $set);
433
+ $sql = "UPDATE {$table} SET {$set} WHERE {$condition}";
434
+ return $this->query($sql);
435
+ }
436
+
437
+
438
+ /**
439
+ * Update entry by pk
440
+ * @param string $table
441
+ * @param array $data
442
+ * @param string $value
443
+ * @param string $key
444
+ * @return mixed
445
+ */
446
+ public function updateAssocByKey($table, array $data, $value, $key = 'id')
447
+ {
448
+ $table = $this->escapeTableName($table);
449
+ $key = $this->escapeFieldName($key);
450
+ $value = $this->escapeFieldValue($value);
451
+ $set = array();
452
+ foreach($data as $k=>$v) {
453
+ $k = $this->escapeFieldName($k);
454
+ $v = $this->escapeFieldValue($v);
455
+ $set[] = $k . " = " . $v;
456
+ }
457
+ $set = implode(",", $set);
458
+ $sql = "UPDATE {$table} SET {$set} WHERE {$key} = {$value}";
459
+ return $this->query($sql);
460
+ }
461
+
462
+
463
+ /**
464
+ * Convert ids to string
465
+ * @param array|string $ids
466
+ * @return string
467
+ */
468
+ public function idsToString($ids)
469
+ {
470
+ if(is_scalar($ids)) {
471
+ return $this->escapeFieldValue(strval($ids));
472
+ }
473
+ $out = array();
474
+ foreach ($values as $id) {
475
+ $out .= $this->escapeFieldValue($id);
476
+ }
477
+ return implode(",", $out);
478
+ }
479
+
480
+ /**
481
+ * Ids equality condition
482
+ * @param mixed $ids array or string
483
+ * @return string
484
+ */
485
+ public function idsEqualCondition($ids)
486
+ {
487
+ $vals = $this->idsToString($ids);
488
+ $condition = is_scalar($ids) ? " = {$vals} " : " IN ({$vals}) ";
489
+ return $condition;
490
+ }
491
+
492
+
493
+ /**
494
+ * Delete items by id
495
+ * @param string $table
496
+ * @param mixed $ids array or string
497
+ * @param string $key key field
498
+ * @return mixed
499
+ */
500
+ public function deleteById($table, $ids, $key = 'id')
501
+ {
502
+ $key = $this->escapeFieldName($key);
503
+ $cond = $this->idsEqualCondition($ids);
504
+ $table = $this->escapeTableName($table);
505
+ $sql = "DELETE FROM {$table} WHERE {$key} {$cond}";
506
+ return $this->query($sql);
507
+ }
508
+
509
+ /**
510
+ * Count items in table by condition
511
+ * @param string $table
512
+ * @param string $condition ex: "a>0"
513
+ * @return int
514
+ */
515
+ public function simpleCount($table, $condition) {
516
+ $sql = "SELECT count(*) AS `cnt` WHERE {$condition}";
517
+ $data = $this->fetchOne($sql);
518
+ if(empty($data['cnt'])) {
519
+ return 0;
520
+ }
521
+ return intval($data['cnt']);
522
+
523
+ }
524
+
525
+ public function lastInsertId()
526
+ {
527
+ $sql = "SELECT LAST_INSERT_ID() as `id`";
528
+ $data = $this->fetchOne($sql);
529
+ return $data['id'];
530
+ }
531
+
532
+ }
lib/Mage/Exception.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class for Exception
29
+ *
30
+ * @category Mage
31
+ * @package Mage
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Exception extends Exception
35
+ {}
lib/Mage/HTTP/Client.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Factory for HTTP client classes
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+
35
+ class Mage_HTTP_Client
36
+ {
37
+
38
+ /**
39
+ * Disallow to instantiate - pvt constructor
40
+ */
41
+ private function __construct()
42
+ {
43
+
44
+ }
45
+
46
+
47
+ /**
48
+ * Factory for HTTP client
49
+ * @param string/false $frontend 'curl'/'socket' or false for auto-detect
50
+ * @return Mage_HTTP_IClient
51
+ */
52
+ public static function getInstance($frontend = false)
53
+ {
54
+ if(false === $frontend)
55
+ {
56
+ $frontend = self::detectFrontend();
57
+ }
58
+ if(false === $frontend)
59
+ {
60
+ throw new Exception("Cannot find frontend automatically, set it manually");
61
+ }
62
+
63
+ $class = __CLASS__."_".str_replace(' ', DIRECTORY_SEPARATOR, ucwords(str_replace('_', ' ', $frontend)));
64
+ $obj = new $class();
65
+ return $obj;
66
+ }
67
+
68
+ /**
69
+ * Detects frontend type.
70
+ * Priority is given to CURL
71
+ *
72
+ * @return string/bool
73
+ */
74
+ protected static function detectFrontend()
75
+ {
76
+ if(function_exists("curl_init")) {
77
+ return "curl";
78
+ }
79
+ if(function_exists("fsockopen")) {
80
+ return "socket";
81
+ }
82
+ return false;
83
+ }
84
+ }
lib/Mage/HTTP/Client/Curl.php ADDED
@@ -0,0 +1,499 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not recreive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with HTTP protocol using curl library
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_HTTP_Client_Curl
35
+ implements Mage_HTTP_IClient
36
+ {
37
+ /**
38
+ * Hostname
39
+ * @var string
40
+ */
41
+ protected $_host = 'localhost';
42
+
43
+ /**
44
+ * Port
45
+ * @var int
46
+ */
47
+ protected $_port = 80;
48
+
49
+ /**
50
+ * Stream resource
51
+ * @var object
52
+ */
53
+ protected $_sock = null;
54
+
55
+ /**
56
+ * Request headers
57
+ * @var array
58
+ */
59
+ protected $_headers = array();
60
+
61
+
62
+ /**
63
+ * Fields for POST method - hash
64
+ * @var array
65
+ */
66
+ protected $_postFields = array();
67
+
68
+ /**
69
+ * Request cookies
70
+ * @var array
71
+ */
72
+ protected $_cookies = array();
73
+
74
+ /**
75
+ * Response headers
76
+ * @var array
77
+ */
78
+ protected $_responseHeaders = array();
79
+
80
+ /**
81
+ * Response body
82
+ * @var string
83
+ */
84
+ protected $_responseBody = '';
85
+
86
+ /**
87
+ * Response status
88
+ * @var int
89
+ */
90
+ protected $_responseStatus = 0;
91
+
92
+
93
+ /**
94
+ * Request timeout
95
+ * @var intunknown_type
96
+ */
97
+ protected $_timeout = 300;
98
+
99
+ /**
100
+ * TODO
101
+ * @var int
102
+ */
103
+ protected $_redirectCount = 0;
104
+
105
+ /**
106
+ * Curl
107
+ * @var object
108
+ */
109
+ protected $_ch;
110
+
111
+
112
+ /**
113
+ * User ovverides options hash
114
+ * Are applied before curl_exec
115
+ *
116
+ * @var array();
117
+ */
118
+ protected $_curlUserOptions = array();
119
+
120
+
121
+ /**
122
+ * Header count, used while parsing headers
123
+ * in CURL callback function
124
+ * @var int
125
+ */
126
+ protected $_headerCount = 0;
127
+
128
+ /**
129
+ * Set request timeout, msec
130
+ *
131
+ * @param int $value
132
+ */
133
+ public function setTimeout($value)
134
+ {
135
+ $this->_timeout = (int) $value;
136
+ }
137
+
138
+ /**
139
+ * Constructor
140
+ */
141
+ public function __construct()
142
+ {
143
+
144
+ }
145
+
146
+ /**
147
+ * Set headers from hash
148
+
149
+ * @param array $headers
150
+ */
151
+ public function setHeaders($headers)
152
+ {
153
+ $this->_headers = $headers;
154
+
155
+ }
156
+
157
+ /**
158
+ * Add header
159
+ *
160
+ * @param $name name, ex. "Location"
161
+ * @param $value value ex. "http://google.com"
162
+ */
163
+ public function addHeader($name, $value)
164
+ {
165
+ $this->_headers[$name] = $value;
166
+ }
167
+
168
+ /**
169
+ * Remove specified header
170
+ *
171
+ * @param string $name
172
+ */
173
+ public function removeHeader($name)
174
+ {
175
+ unset($this->_headers[$name]);
176
+
177
+ }
178
+
179
+ /**
180
+ * Authorization: Basic header
181
+ * Login credentials support
182
+ *
183
+ * @param string $login username
184
+ * @param string $pass password
185
+ */
186
+ public function setCredentials($login, $pass)
187
+ {
188
+ $val= base64_encode( "$login:$pass" );
189
+ $this->addHeader( "Authorization", "Basic $val" );
190
+ }
191
+
192
+ /**
193
+ * Add cookie
194
+ *
195
+ * @param string $name
196
+ * @param string $value
197
+ */
198
+ public function addCookie($name, $value)
199
+ {
200
+ $this->_cookies[$name] = $value;
201
+ }
202
+
203
+ /**
204
+ * Remove cookie
205
+ *
206
+ * @param string $name
207
+ */
208
+ public function removeCookie($name)
209
+ {
210
+ unset($this->_cookies[$name]);
211
+ }
212
+
213
+ /**
214
+ * Set cookies array
215
+ *
216
+ * @param array $cookies
217
+ */
218
+ public function setCookies($cookies)
219
+ {
220
+ $this->_cookies = $cookies;
221
+ }
222
+
223
+ /**
224
+ * Clear cookies
225
+ */
226
+ public function removeCookies()
227
+ {
228
+ $this->setCookies(array());
229
+ }
230
+
231
+
232
+ /**
233
+ * Make GET request
234
+ *
235
+ * @param string $uri uri relative to host, ex. "/index.php"
236
+ */
237
+ public function get($uri)
238
+ {
239
+ $this->makeRequest("GET", $uri);
240
+ }
241
+
242
+ /**
243
+ * Make POST request
244
+ * @see lib/Mage/HTTP/Mage_HTTP_Client#post($uri, $params)
245
+ */
246
+ public function post($uri, $params)
247
+ {
248
+ $this->makeRequest("POST", $uri, $params);
249
+ }
250
+
251
+
252
+ /**
253
+ * Get response headers
254
+ *
255
+ * @return array
256
+ */
257
+ public function getHeaders()
258
+ {
259
+ return $this->_responseHeaders;
260
+ }
261
+
262
+
263
+ /**
264
+ * Get response body
265
+ *
266
+ * @return string
267
+ */
268
+ public function getBody()
269
+ {
270
+ return $this->_responseBody;
271
+ }
272
+
273
+ /**
274
+ * Get cookies response hash
275
+ *
276
+ * @return array
277
+ */
278
+ public function getCookies()
279
+ {
280
+ if(empty($this->_responseHeaders['Set-Cookie'])) {
281
+ return array();
282
+ }
283
+ $out = array();
284
+ foreach( $this->_responseHeaders['Set-Cookie'] as $row) {
285
+ $values = explode("; ", $row);
286
+ $c = count($values);
287
+ if(!$c) {
288
+ continue;
289
+ }
290
+ list($key, $val) = explode("=", $values[0]);
291
+ if(is_null($val)) {
292
+ continue;
293
+ }
294
+ $out[trim($key)] = trim($val);
295
+ }
296
+ return $out;
297
+ }
298
+
299
+
300
+ /**
301
+ * Get cookies array with details
302
+ * (domain, expire time etc)
303
+ * @return array
304
+ */
305
+ public function getCookiesFull()
306
+ {
307
+ if(empty($this->_responseHeaders['Set-Cookie'])) {
308
+ return array();
309
+ }
310
+ $out = array();
311
+ foreach( $this->_responseHeaders['Set-Cookie'] as $row) {
312
+ $values = explode("; ", $row);
313
+ $c = count($values);
314
+ if(!$c) {
315
+ continue;
316
+ }
317
+ list($key, $val) = explode("=", $values[0]);
318
+ if(is_null($val)) {
319
+ continue;
320
+ }
321
+ $out[trim($key)] = array('value'=>trim($val));
322
+ array_shift($values);
323
+ $c--;
324
+ if(!$c) {
325
+ continue;
326
+ }
327
+ for($i = 0; $i<$c; $i++) {
328
+ list($subkey, $val) = explode("=", $values[$i]);
329
+ $out[trim($key)][trim($subkey)] = trim($val);
330
+ }
331
+ }
332
+ return $out;
333
+ }
334
+
335
+ /**
336
+ * Get response status code
337
+ * @see lib/Mage/HTTP/Mage_HTTP_Client#getStatus()
338
+ */
339
+ public function getStatus()
340
+ {
341
+ return $this->_responseStatus;
342
+ }
343
+
344
+ /**
345
+ * Make request
346
+ * @param string $method
347
+ * @param string $uri
348
+ * @param array $params
349
+ * @return null
350
+ */
351
+ protected function makeRequest($method, $uri, $params = array())
352
+ {
353
+ $this->_ch = curl_init();
354
+ $this->curlOption(CURLOPT_URL, $uri);
355
+ if($method == 'POST') {
356
+ $this->curlOption(CURLOPT_POST, 1);
357
+ $this->curlOption(CURLOPT_POSTFIELDS, http_build_query($params));
358
+ } elseif($method == "GET") {
359
+ $this->curlOption(CURLOPT_HTTPGET, 1);
360
+ } else {
361
+ $this->curlOption(CURLOPT_CUSTOMREQUEST, $method);
362
+ }
363
+
364
+ if(count($this->_headers)) {
365
+ $heads = array();
366
+ foreach($this->_headers as $k=>$v) {
367
+ $heads[] = $k.': '.$v;
368
+ }
369
+ $this->curlOption(CURLOPT_HTTPHEADER, $heads);
370
+ }
371
+
372
+ if(count($this->_cookies)) {
373
+ $cookies = array();
374
+ foreach($this->_cookies as $k=>$v) {
375
+ $cookies[] = "$k=$v";
376
+ }
377
+ $this->curlOption(CURLOPT_COOKIE, implode(";", $cookies));
378
+ }
379
+
380
+ if($this->_timeout) {
381
+ $this->curlOption(CURLOPT_TIMEOUT, $this->_timeout);
382
+ }
383
+
384
+ if($this->_port != 80) {
385
+ $this->curlOption(CURLOPT_PORT, $this->_port);
386
+ }
387
+
388
+ //$this->curlOption(CURLOPT_HEADER, 1);
389
+ $this->curlOption(CURLOPT_RETURNTRANSFER, 1);
390
+ $this->curlOption(CURLOPT_HEADERFUNCTION, array($this,'parseHeaders'));
391
+
392
+
393
+ if(count($this->_curlUserOptions)) {
394
+ foreach($this->_curlUserOptions as $k=>$v) {
395
+ $this->curlOption($k, $v);
396
+ }
397
+ }
398
+
399
+ $this->_headerCount = 0;
400
+ $this->_responseHeaders = array();
401
+ $this->_responseBody = curl_exec($this->_ch);
402
+ $err = curl_errno($this->_ch);
403
+ if($err) {
404
+ $this->doError(curl_error($this->_ch));
405
+ }
406
+ curl_close($this->_ch);
407
+ }
408
+
409
+ /**
410
+ * Throw error excpetion
411
+ * @param $string
412
+ * @throws Exception
413
+ */
414
+ public function doError($string)
415
+ {
416
+ throw new Exception($string);
417
+ }
418
+
419
+
420
+ /**
421
+ * Parse headers - CURL callback functin
422
+ *
423
+ * @param resource $ch curl handle, not needed
424
+ * @param string $data
425
+ * @return int
426
+ */
427
+ protected function parseHeaders($ch, $data)
428
+ {
429
+ if($this->_headerCount == 0) {
430
+
431
+ $line = explode(" ", trim($data), 3);
432
+ if(count($line) != 3) {
433
+ return $this->doError("Invalid response line returned from server: ".$data);
434
+ }
435
+ $this->_responseStatus = intval($line[1]);
436
+ } else {
437
+ //var_dump($data);
438
+ $name = $value = '';
439
+ $out = explode(": ", trim($data), 2);
440
+ if(count($out) == 2) {
441
+ $name = $out[0];
442
+ $value = $out[1];
443
+ }
444
+
445
+ if(strlen($name)) {
446
+ if("Set-Cookie" == $name) {
447
+ if(!isset($this->_responseHeaders[$name])) {
448
+ $this->_responseHeaders[$name] = array();
449
+ }
450
+ $this->_responseHeaders[$name][] = $value;
451
+ } else {
452
+ $this->_responseHeaders[$name] = $value;
453
+ }
454
+ }
455
+
456
+ }
457
+ $this->_headerCount++;
458
+
459
+
460
+ return strlen($data);
461
+ }
462
+
463
+ /**
464
+ * Set curl option directly
465
+ *
466
+ * @param string $name
467
+ * @param string $value
468
+ */
469
+ protected function curlOption($name, $value)
470
+ {
471
+ curl_setopt($this->_ch, $name, $value);
472
+ }
473
+
474
+ /**
475
+ * Set curl options array directly
476
+ * @param array $array
477
+ */
478
+ protected function curlOptions($array)
479
+ {
480
+ curl_setopt_array($this->_ch, $arr);
481
+ }
482
+
483
+ /**
484
+ * Set CURL options ovverides array *
485
+ */
486
+ public function setOptions($arr)
487
+ {
488
+ $this->_curlUserOptions = $arr;
489
+ }
490
+
491
+ /**
492
+ * Set curl option
493
+ */
494
+ public function setOption($name, $value)
495
+ {
496
+ $this->_curlUserOptions[$name] = $value;
497
+ }
498
+
499
+ }
lib/Mage/HTTP/Client/Socket.php ADDED
@@ -0,0 +1,537 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class to work with HTTP protocol using sockets
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_HTTP_Client_Socket
35
+ implements Mage_HTTP_IClient
36
+ {
37
+ /**
38
+ * Hostname
39
+ * @var string
40
+ */
41
+ private $_host = 'localhost';
42
+
43
+ /**
44
+ * Port
45
+ * @var int
46
+ */
47
+ private $_port = 80;
48
+
49
+ /**
50
+ * Stream resource
51
+ * @var object
52
+ */
53
+ private $_sock = null;
54
+
55
+ /**
56
+ * Request headers
57
+ * @var array
58
+ */
59
+ private $_headers = array();
60
+
61
+
62
+ /**
63
+ * Fields for POST method - hash
64
+ * @var array
65
+ */
66
+ private $_postFields = array();
67
+
68
+ /**
69
+ * Request cookies
70
+ * @var array
71
+ */
72
+ private $_cookies = array();
73
+
74
+ /**
75
+ * Response headers
76
+ * @var array
77
+ */
78
+ private $_responseHeaders = array();
79
+
80
+ /**
81
+ * Response body
82
+ * @var string
83
+ */
84
+ private $_responseBody = '';
85
+
86
+ /**
87
+ * Response status
88
+ * @var int
89
+ */
90
+ private $_responseStatus = 0;
91
+
92
+
93
+ /**
94
+ * Request timeout
95
+ * @var int
96
+ */
97
+ private $_timeout = 300;
98
+
99
+ /**
100
+ * TODO
101
+ * @var int
102
+ */
103
+ private $_redirectCount = 0;
104
+
105
+
106
+ /**
107
+ * Set request timeout, msec
108
+ *
109
+ * @param int $value
110
+ */
111
+ public function setTimeout($value)
112
+ {
113
+ $this->_timeout = (int) $value;
114
+ }
115
+
116
+ /**
117
+ * Constructor
118
+ * @param string $host
119
+ * @param int $port
120
+ */
121
+ public function __construct($host = null, $port = 80)
122
+ {
123
+ if($host) {
124
+ $this->connect($host, (int) $port);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Set connection params
130
+ *
131
+ * @param string $host
132
+ * @param int $port
133
+ */
134
+ public function connect($host, $port = 80)
135
+ {
136
+ $this->_host = $host;
137
+ $this->_port = (int) $port;
138
+
139
+ }
140
+
141
+ /**
142
+ * Disconnect
143
+ */
144
+ public function disconnect()
145
+ {
146
+ @fclose($this->_sock);
147
+ }
148
+
149
+ /**
150
+ * Set headers from hash
151
+
152
+ * @param array $headers
153
+ */
154
+ public function setHeaders($headers)
155
+ {
156
+ $this->_headers = $headers;
157
+
158
+ }
159
+
160
+ /**
161
+ * Add header
162
+ *
163
+ * @param $name name, ex. "Location"
164
+ * @param $value value ex. "http://google.com"
165
+ */
166
+ public function addHeader($name, $value)
167
+ {
168
+ $this->_headers[$name] = $value;
169
+
170
+ }
171
+
172
+ /**
173
+ * Remove specified header
174
+ *
175
+ * @param string $name
176
+ */
177
+ public function removeHeader($name)
178
+ {
179
+ unset($this->_headers[$name]);
180
+
181
+ }
182
+
183
+ /**
184
+ * Authorization: Basic header
185
+ * Login credentials support
186
+ *
187
+ * @param string $login username
188
+ * @param string $pass password
189
+ */
190
+ public function setCredentials($login, $pass)
191
+ {
192
+ $val= base64_encode( "$login:$pass" );
193
+ $this->addHeader( "Authorization", "Basic $val" );
194
+ }
195
+
196
+ /**
197
+ * Add cookie
198
+ *
199
+ * @param string $name
200
+ * @param string $value
201
+ */
202
+ public function addCookie($name, $value)
203
+ {
204
+ $this->_cookies[$name] = $value;
205
+ }
206
+
207
+ /**
208
+ * Remove cookie
209
+ *
210
+ * @param string $name
211
+ */
212
+ public function removeCookie($name)
213
+ {
214
+ unset($this->_cookies[$name]);
215
+ }
216
+
217
+ /**
218
+ * Set cookies array
219
+ *
220
+ * @param array $cookies
221
+ */
222
+ public function setCookies($cookies)
223
+ {
224
+ $this->_cookies = $cookies;
225
+ }
226
+
227
+ /**
228
+ * Clear cookies
229
+ */
230
+ public function removeCookies()
231
+ {
232
+ $this->setCookies(array());
233
+ }
234
+
235
+
236
+ /**
237
+ * Make GET request
238
+ *
239
+ * @param string $uri full uri path
240
+ */
241
+ public function get($uri)
242
+ {
243
+ $this->makeRequest("GET",$this->parseUrl($uri));
244
+ }
245
+
246
+ /**
247
+ * Set host, port from full url
248
+ * and return relative url
249
+ *
250
+ * @param string $uri ex. http://google.com/index.php?a=b
251
+ * @return string ex. /index.php?a=b
252
+ */
253
+ protected function parseUrl($uri)
254
+ {
255
+ $parts = parse_url($uri);
256
+ if(!empty($parts['user']) && !empty($parts['pass'])) {
257
+ $this->setCredentials($parts['user'], $parts['pass']);
258
+ }
259
+ if(!empty($parts['port'])) {
260
+ $this->_port = (int) $parts['port'];
261
+ }
262
+
263
+ if(!empty($parts['host'])) {
264
+ $this->_host = $parts['host'];
265
+ } else {
266
+ throw new InvalidArgumentException("Uri doesn't contain host part");
267
+ }
268
+
269
+
270
+ if(!empty($parts['path'])) {
271
+ $requestUri = $parts['path'];
272
+ } else {
273
+ throw new InvalidArgumentException("Uri doesn't contain path part");
274
+ }
275
+ if(!empty($parts['query'])) {
276
+ $requestUri .= "?".$parts['query'];
277
+ }
278
+ return $requestUri;
279
+ }
280
+
281
+ /**
282
+ * Make POST request
283
+ */
284
+ public function post($uri, $params)
285
+ {
286
+ $this->makeRequest("POST", $this->parseUrl($uri), $params);
287
+ }
288
+
289
+
290
+ /**
291
+ * Get response headers
292
+ *
293
+ * @return array
294
+ */
295
+ public function getHeaders()
296
+ {
297
+ return $this->_responseHeaders;
298
+ }
299
+
300
+
301
+ /**
302
+ * Get response body
303
+ *
304
+ * @return string
305
+ */
306
+ public function getBody()
307
+ {
308
+ return $this->_responseBody;
309
+ }
310
+
311
+ /**
312
+ * Get cookies response hash
313
+ *
314
+ * @return array
315
+ */
316
+ public function getCookies()
317
+ {
318
+ if(empty($this->_responseHeaders['Set-Cookie'])) {
319
+ return array();
320
+ }
321
+ $out = array();
322
+ foreach( $this->_responseHeaders['Set-Cookie'] as $row) {
323
+ $values = explode("; ", $row);
324
+ $c = count($values);
325
+ if(!$c) {
326
+ continue;
327
+ }
328
+ list($key, $val) = explode("=", $values[0]);
329
+ if(is_null($val)) {
330
+ continue;
331
+ }
332
+ $out[trim($key)] = trim($val);
333
+ }
334
+ return $out;
335
+ }
336
+
337
+
338
+ /**
339
+ * Get cookies array with details
340
+ * (domain, expire time etc)
341
+ * @return array
342
+ */
343
+ public function getCookiesFull()
344
+ {
345
+ if(empty($this->_responseHeaders['Set-Cookie'])) {
346
+ return array();
347
+ }
348
+ $out = array();
349
+ foreach( $this->_responseHeaders['Set-Cookie'] as $row) {
350
+ $values = explode("; ", $row);
351
+ $c = count($values);
352
+ if(!$c) {
353
+ continue;
354
+ }
355
+ list($key, $val) = explode("=", $values[0]);
356
+ if(is_null($val)) {
357
+ continue;
358
+ }
359
+ $out[trim($key)] = array('value'=>trim($val));
360
+ array_shift($values);
361
+ $c--;
362
+ if(!$c) {
363
+ continue;
364
+ }
365
+ for($i = 0; $i<$c; $i++) {
366
+ list($subkey, $val) = explode("=", $values[$i]);
367
+ $out[trim($key)][trim($subkey)] = trim($val);
368
+ }
369
+ }
370
+ return $out;
371
+ }
372
+
373
+ /**
374
+ * Process response headers
375
+ */
376
+ protected function processResponseHeaders()
377
+ {
378
+ $crlf = "\r\n";
379
+ $this->_responseHeaders = array();
380
+ while (!feof($this->_sock)) {
381
+ $line = fgets($this->_sock, 1024);
382
+ if($line === $crlf) {
383
+ return;
384
+ }
385
+ $name = $value = '';
386
+ $out = explode(": ", trim($line), 2);
387
+ if(count($out) == 2) {
388
+ $name = $out[0];
389
+ $value = $out[1];
390
+ }
391
+ if(!empty($value)) {
392
+ if($name == "Set-Cookie") {
393
+ if(!isset($this->_responseHeaders[$name])) {
394
+ $this->_responseHeaders[$name] = array();
395
+ }
396
+ $this->_responseHeaders[$name][] = $value;
397
+ } else {
398
+ $this->_responseHeaders[$name] = $value;
399
+ }
400
+ }
401
+ }
402
+ }
403
+
404
+ /**
405
+ * Process response body
406
+ */
407
+ protected function processResponseBody()
408
+ {
409
+ $this->_responseBody = '';
410
+
411
+ while (!feof($this->_sock)) {
412
+ $this->_responseBody .= @fread($this->_sock, 1024);
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Process response
418
+ */
419
+ protected function processResponse()
420
+ {
421
+ $response = '';
422
+ $responseLine = trim(fgets($this->_sock, 1024));
423
+
424
+ $line = explode(" ", $responseLine, 3);
425
+ if(count($line) != 3) {
426
+ return $this->doError("Invalid response line returned from server: ".$responseLine);
427
+ }
428
+ $this->_responseStatus = intval($line[1]);
429
+ $this->processResponseHeaders();
430
+
431
+ $this->processRedirect();
432
+
433
+ $this->processResponseBody();
434
+ }
435
+
436
+
437
+ /**
438
+ * Process redirect
439
+ */
440
+ protected function processRedirect()
441
+ {
442
+ // TODO: implement redircets support
443
+ }
444
+
445
+
446
+ /**
447
+ * Get response status code
448
+ * @see lib/Mage/HTTP/Mage_HTTP_Client#getStatus()
449
+ */
450
+ public function getStatus()
451
+ {
452
+ return $this->_responseStatus;
453
+ }
454
+
455
+ /**
456
+ * Make request
457
+ * @param string $method
458
+ * @param string $uri
459
+ * @param array $params
460
+ * @return null
461
+ */
462
+ protected function makeRequest($method, $uri, $params = array())
463
+ {
464
+ $errno = $errstr = '';
465
+ $this->_sock = @fsockopen($this->_host, $this->_port, $errno, $errstr, $this->_timeout);
466
+ if(!$this->_sock) {
467
+ return $this->doError(sprintf("[errno: %d] %s", $errno, $errstr));
468
+ }
469
+
470
+ $crlf = "\r\n";
471
+ $isPost = $method == "POST";
472
+
473
+ $appendHeaders = array();
474
+ $paramsStr = false;
475
+ if($isPost && count($params)) {
476
+ $paramsStr = http_build_query($params);
477
+ $appendHeaders['Content-type'] = 'application/x-www-form-urlencoded';
478
+ $appendHeaders['Content-length'] = strlen($paramsStr);
479
+ }
480
+
481
+ $out = "{$method} {$uri} HTTP/1.1{$crlf}";
482
+ $out .= $this->headersToString($appendHeaders);
483
+ $out .= $crlf;
484
+ if($paramsStr) {
485
+ $out .= $paramsStr.$crlf;
486
+ }
487
+
488
+ fwrite($this->_sock, $out);
489
+ $this->processResponse();
490
+ }
491
+
492
+ /**
493
+ * Throw error excpetion
494
+ * @param $string
495
+ * @throws Exception
496
+ */
497
+ public function doError($string)
498
+ {
499
+ throw new Exception($string);
500
+ }
501
+
502
+ /**
503
+ * Convert headers hash to string
504
+ * @param $delimiter
505
+ * @param $append
506
+ * @return string
507
+ */
508
+ protected function headersToString($append = array())
509
+ {
510
+ $headers = array();
511
+ $headers["Host"] = $this->_host;
512
+ $headers['Connection'] = "close";
513
+ $headers = array_merge($headers, $this->_headers, $append);
514
+ $str = array();
515
+ foreach ($headers as $k=>$v) {
516
+ $str []= "$k: $v\r\n";
517
+ }
518
+ return implode($str);
519
+ }
520
+
521
+ /**
522
+ * TODO
523
+ */
524
+ public function setOptions($arr)
525
+ {
526
+ // Stub
527
+ }
528
+
529
+ /**
530
+ * TODO
531
+ */
532
+ public function setOption($name, $value)
533
+ {
534
+ // Stub
535
+ }
536
+
537
+ }
lib/Mage/HTTP/IClient.php ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Interface for different HTTP clients
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Connect
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ interface Mage_HTTP_IClient
35
+ {
36
+ /**
37
+ * Set request timeout
38
+ * @param int $value
39
+ */
40
+ function setTimeout($value);
41
+
42
+
43
+ /**
44
+ * Set request headers from hash
45
+ * @param array $headers
46
+ */
47
+ function setHeaders($headers);
48
+
49
+ /**
50
+ * Add header to request
51
+ * @param string $name
52
+ * @param string $value
53
+ */
54
+ function addHeader($name, $value);
55
+
56
+
57
+ /**
58
+ * Remove header from request
59
+ * @param string $name
60
+ */
61
+ function removeHeader($name);
62
+
63
+
64
+ /**
65
+ * Set login credentials
66
+ * for basic auth.
67
+ * @param string $login
68
+ * @param string $pass
69
+ */
70
+ function setCredentials($login, $pass);
71
+
72
+ /**
73
+ * Add cookie to request
74
+ * @param string $name
75
+ * @param string $value
76
+ */
77
+ function addCookie($name, $value);
78
+
79
+ /**
80
+ * Remove cookie from request
81
+ * @param string $name
82
+ */
83
+ function removeCookie($name);
84
+
85
+ /**
86
+ * Set request cookies from hash
87
+ * @param array $cookies
88
+ */
89
+ function setCookies($cookies);
90
+
91
+ /**
92
+ * Remove cookies from request
93
+ */
94
+ function removeCookies();
95
+
96
+ /**
97
+ * Make GET request
98
+ * @param string full uri
99
+ */
100
+ function get($uri);
101
+
102
+ /**
103
+ * Make POST request
104
+ * @param string $uri full uri
105
+ * @param array $params POST fields array
106
+ */
107
+ function post($uri, $params);
108
+
109
+ /**
110
+ * Get response headers
111
+ * @return array
112
+ */
113
+ function getHeaders();
114
+
115
+ /**
116
+ * Get response body
117
+ * @return string
118
+ */
119
+ function getBody();
120
+
121
+ /**
122
+ * Get response status code
123
+ * @return int
124
+ */
125
+ function getStatus();
126
+
127
+ /**
128
+ * Get response cookies (k=>v)
129
+ * @return array
130
+ */
131
+ function getCookies();
132
+
133
+ /**
134
+ * Set additional option
135
+ * @param string $key
136
+ * @param string $value
137
+ */
138
+ function setOption($key, $value);
139
+
140
+ /**
141
+ * Set additional options
142
+ * @param array $arr
143
+ */
144
+ function setOptions($arr);
145
+ }
lib/Mage/System/Args.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Connect
23
+ * @copyright Copyright (c) 2009 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Command-line options parsing class.
29
+ */
30
+ class Mage_System_Args
31
+ {
32
+ public $flags;
33
+ public $filtered;
34
+
35
+ /**
36
+ * Get flags/named options
37
+ * @return array
38
+ */
39
+ public function getFlags()
40
+ {
41
+ return $this->flags;
42
+ }
43
+
44
+ /**
45
+ * Get filtered args
46
+ * @return array
47
+ */
48
+ public function getFiltered()
49
+ {
50
+ return $this->filtered;
51
+ }
52
+
53
+ /**
54
+ * Constructor
55
+ * @param array $argv, if false $GLOBALS['argv'] is taken
56
+ * @return void
57
+ */
58
+ public function __construct($source = false)
59
+ {
60
+ $this->flags = array();
61
+ $this->filtered = array();
62
+
63
+ if(false === $source) {
64
+ $argv = $GLOBALS['argv'];
65
+ array_shift($argv);
66
+ }
67
+
68
+ for($i = 0, $iCount = count($argv); $i < $iCount; $i++)
69
+ {
70
+ $str = $argv[$i];
71
+
72
+ // --foo
73
+ if(strlen($str) > 2 && substr($str, 0, 2) == '--')
74
+ {
75
+ $str = substr($str, 2);
76
+ $parts = explode('=', $str);
77
+ $this->flags[$parts[0]] = true;
78
+
79
+ // Does not have an =, so choose the next arg as its value
80
+ if(count($parts) == 1 && isset($argv[$i + 1]) && preg_match('/^--?.+/', $argv[$i + 1]) == 0)
81
+ {
82
+ $this->flags[$parts[0]] = $argv[$i + 1];
83
+ $argv[$i + 1] = null;
84
+ }
85
+ elseif(count($parts) == 2) // Has a =, so pick the second piece
86
+ {
87
+ $this->flags[$parts[0]] = $parts[1];
88
+ }
89
+ }
90
+ elseif(strlen($str) == 2 && $str[0] == '-') // -a
91
+ {
92
+ $this->flags[$str[1]] = true;
93
+ if(isset($argv[$i + 1]) && preg_match('/^--?.+/', $argv[$i + 1]) == 0) {
94
+ $this->flags[$str[1]] = $argv[$i + 1];
95
+ $argv[$i + 1] = null;
96
+ }
97
+ } else if(!is_null($str)) {
98
+ $this->filtered[] = $str;
99
+ }
100
+ }
101
+ }
102
+ }
lib/Mage/System/Dirs.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class Mage_System_Dirs
5
+ {
6
+
7
+ public static function rm($dirname)
8
+ {
9
+ if(is_array($dirname)) {
10
+ $dirname = $dirname[1];
11
+ }
12
+ // Sanity check
13
+ if (!@file_exists($dirname)) {
14
+ return false;
15
+ }
16
+
17
+ // Simple delete for a file
18
+ if (@is_file($dirname) || @is_link($dirname)) {
19
+ return unlink($dirname);
20
+ }
21
+
22
+ // Create and iterate stack
23
+ $stack = array($dirname);
24
+ while ($entry = array_pop($stack)) {
25
+ // Watch for symlinks
26
+ if (@is_link($entry)) {
27
+ @unlink($entry);
28
+ continue;
29
+ }
30
+
31
+ // Attempt to remove the directory
32
+ if (@rmdir($entry)) {
33
+ continue;
34
+ }
35
+
36
+ // Otherwise add it to the stack
37
+ $stack[] = $entry;
38
+ $dh = opendir($entry);
39
+ while (false !== $child = readdir($dh)) {
40
+ // Ignore pointers
41
+ if ($child === '.' || $child === '..') {
42
+ continue;
43
+ }
44
+ // Unlink files and add directories to stack
45
+ $child = $entry . DIRECTORY_SEPARATOR . $child;
46
+ if (is_dir($child) && !is_link($child)) {
47
+ $stack[] = $child;
48
+ } else {
49
+ @unlink($child);
50
+ }
51
+ }
52
+ @closedir($dh);
53
+ }
54
+ return true;
55
+ }
56
+
57
+
58
+ public static function mkdirStrict($path, $recursive = true, $mode = 0777)
59
+ {
60
+ $exists = file_exists($path);
61
+ if($exists && is_dir($path)) {
62
+ return true;
63
+ }
64
+ if($exists && !is_dir($path)) {
65
+ throw new Exception("'{$path}' already exists, should be a dir, not a file!");
66
+ }
67
+ $out = @mkdir($path, $mode, $recursive);
68
+ if(false === $out) {
69
+ throw new Exception("Can't create dir: '{$path}'");
70
+ }
71
+ return true;
72
+ }
73
+
74
+ public static function copyFileStrict($source, $dest)
75
+ {
76
+ $exists = file_exists($source);
77
+ if(!$exists) {
78
+ throw new Exception('No file exists: '.$exists);
79
+ }
80
+
81
+ }
82
+ }
lib/Mage/Xml/Generator.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Mage_Xml_Generator
3
+ {
4
+ protected $_dom = null;
5
+ protected $_currentDom;
6
+
7
+ public function __construct()
8
+ {
9
+ $this->_dom = new DOMDocument('1.0');
10
+ $this->_dom->formatOutput=true;
11
+ $this->_currentDom = $this->_dom;
12
+ return $this;
13
+ }
14
+
15
+ public function getDom()
16
+ {
17
+ return $this->_dom;
18
+ }
19
+
20
+ protected function _getCurrentDom()
21
+ {
22
+ return $this->_currentDom;
23
+ }
24
+
25
+ protected function _setCurrentDom($node)
26
+ {
27
+ $this->_currentDom = $node;
28
+ return $this;
29
+ }
30
+
31
+ /**
32
+ * @param array $content
33
+ */
34
+ public function arrayToXml($content)
35
+ {
36
+ $parentNode = $this->_getCurrentDom();
37
+ if(!$content || !count($content)) {
38
+ return $this;
39
+ }
40
+ foreach ($content as $_key=>$_item) {
41
+ try{
42
+ $node = $this->getDom()->createElement($_key);
43
+ } catch (DOMException $e) {
44
+ // echo $e->getMessage();
45
+ var_dump($_item);
46
+ die;
47
+ }
48
+ $parentNode->appendChild($node);
49
+ if (is_array($_item) && isset($_item['_attribute'])) {
50
+ if (is_array($_item['_value'])) {
51
+ if (isset($_item['_value'][0])) {
52
+ foreach($_item['_value'] as $_k=>$_v) {
53
+ $this->_setCurrentDom($node)->arrayToXml($_v);
54
+ }
55
+ } else {
56
+ $this->_setCurrentDom($node)->arrayToXml($_item['_value']);
57
+ }
58
+ } else {
59
+ $child = $this->getDom()->createTextNode($_item['_value']);
60
+ $node->appendChild($child);
61
+ }
62
+ foreach($_item['_attribute'] as $_attributeKey=>$_attributeValue) {
63
+ $node->setAttribute($_attributeKey, $_attributeValue);
64
+ }
65
+ } elseif (is_string($_item)) {
66
+ $text = $this->getDom()->createTextNode($_item);
67
+ $node->appendChild($text);
68
+ } elseif (is_array($_item) && !isset($_item[0])) {
69
+ $this->_setCurrentDom($node)->arrayToXml($_item);
70
+ } elseif (is_array($_item) && isset($_item[0])) {
71
+ foreach($_item as $k=>$v) {
72
+ $this->_setCurrentDom($node)->arrayToXml($v);
73
+ }
74
+ }
75
+ }
76
+ return $this;
77
+ }
78
+
79
+ public function __toString()
80
+ {
81
+ return $this->getDom()->saveXML();
82
+ }
83
+
84
+ public function save($file)
85
+ {
86
+ $this->getDom()->save($file);
87
+ return $this;
88
+ }
89
+
90
+ }
lib/Mage/Xml/Parser.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Mage_Xml_Parser
3
+ {
4
+ protected $_dom = null;
5
+ protected $_currentDom;
6
+ protected $_content = array();
7
+
8
+ public function __construct()
9
+ {
10
+ $this->_dom = new DOMDocument;
11
+ $this->_currentDom = $this->_dom;
12
+ return $this;
13
+ }
14
+
15
+ public function getDom()
16
+ {
17
+ return $this->_dom;
18
+ }
19
+
20
+ protected function _getCurrentDom()
21
+ {
22
+ return $this->_currentDom;
23
+ }
24
+
25
+ protected function _setCurrentDom($node)
26
+ {
27
+ $this->_currentDom = $node;
28
+ return $this;
29
+ }
30
+
31
+ public function xmlToArray()
32
+ {
33
+ $this->_content = $this->_xmlToArray();
34
+ return $this->_content;
35
+ }
36
+
37
+ protected function _xmlToArray($currentNode=false)
38
+ {
39
+ if (!$currentNode) {
40
+ $currentNode = $this->getDom();
41
+ }
42
+ $content = array();
43
+ foreach ($currentNode->childNodes as $node) {
44
+ switch ($node->nodeType) {
45
+ case XML_ELEMENT_NODE:
46
+
47
+ $value = null;
48
+ if ($node->hasChildNodes()) {
49
+ $value = $this->_xmlToArray($node);
50
+ }
51
+ $attributes = array();
52
+ if ($node->hasAttributes()) {
53
+ foreach($node->attributes as $attribute) {
54
+ $attributes += array($attribute->name=>$attribute->value);
55
+ }
56
+ $value = array('_value'=>$value, '_attribute'=>$attributes);
57
+ }
58
+ if (isset($content[$node->nodeName])) {
59
+ if (!isset($content[$node->nodeName][0]) || !is_array($content[$node->nodeName][0])) {
60
+ $oldValue = $content[$node->nodeName];
61
+ $content[$node->nodeName] = array();
62
+ $content[$node->nodeName][] = $oldValue;
63
+ }
64
+ $content[$node->nodeName][] = $value;
65
+ } else {
66
+ $content[$node->nodeName] = $value;
67
+ }
68
+ break;
69
+ case XML_TEXT_NODE:
70
+ if (trim($node->nodeValue)) {
71
+ $content = $node->nodeValue;
72
+ }
73
+ break;
74
+ }
75
+ }
76
+ return $content;
77
+ }
78
+
79
+ public function load($file)
80
+ {
81
+ $this->getDom()->load($file);
82
+ return $this;
83
+ }
84
+
85
+ public function loadXML($string)
86
+ {
87
+ $this->getDom()->loadXML($string);
88
+ return $this;
89
+ }
90
+
91
+ }
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Lib_Mage</name>
4
+ <version>1.6.0.0</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/osl-3.0.php">OSL v3.0</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Mage Library</summary>
10
+ <description>Mage Library</description>
11
+ <notes>1.6.0.0</notes>
12
+ <authors><author><name>Magento Core Team</name><user>core</user><email>core@magentocommerce.com</email></author></authors>
13
+ <date>2011-08-18</date>
14
+ <time>08:29:36</time>
15
+ <contents><target name="magelib"><dir name="Mage"><dir name="Archive"><file name="Abstract.php" hash="facb355053858f94439409d8657757ff"/><file name="Bz.php" hash="bce512d3062f936f1a8c12bf5c408b64"/><file name="Gz.php" hash="0dca1839014c71886a8a93186bb993d0"/><file name="Interface.php" hash="ef441008981545b886dcab6f386a6882"/><file name="Tar.php" hash="9f09ed124af89b683897246503ea9832"/></dir><file name="Archive.php" hash="f7745fb5794efd8e0a2adaef4a3f6f12"/><dir name="Autoload"><file name="Simple.php" hash="0112e880807596fbe8822be12314f14f"/></dir><dir name="Connect"><dir name="Channel"><file name="Generator.php" hash="a1f115e909680bca8c1b1f0940244b13"/><file name="Parser.php" hash="ec2e7380ec4bca81de2795f96975e778"/><file name="VO.php" hash="870a1b9b28d85dd72b2b1e2529dca354"/></dir><dir name="Command"><file name="Channels.php" hash="12f730285615329cba0b570385009837"/><file name="Channels_Header.php" hash="934366a4daa1ebe87b25cb9dfa061798"/><file name="Config.php" hash="5091776f35732d8b0ff12c8ff3561c69"/><file name="Config_Header.php" hash="119a103ed71b326993756961b6c7c2a0"/><file name="Install.php" hash="2f7227a1f4003d6ce6edec4a0ce02e0f"/><file name="Install_Header.php" hash="71f0b6bad2f745235352d181bcc66645"/><file name="Package.php" hash="b36bdc6a3c0f6bc9d7766a37c046a481"/><file name="Package_Header.php" hash="dc3840a95f89ef844df40c585582815d"/><file name="Registry.php" hash="f9fde28940ff38dbc336335d575a295d"/><file name="Registry_Header.php" hash="559edbac0f3d267c8ca289e526b940d1"/><file name="Remote.php" hash="6f959617993d58aec974d578921b6212"/><file name="Remote_Header.php" hash="4b56cd683710de6c81241f902570ba25"/></dir><file name="Command.php" hash="0d474da0956a292aa83e88da4975c527"/><file name="Config.php" hash="511f752123ccc6c148a6a308fc3f81b2"/><file name="Converter.php" hash="1cde84613c6079050bb3824aba3b380d"/><dir name="Frontend"><file name="CLI.php" hash="86e6631d9345d5bf9d15148cfdace3bf"/></dir><file name="Frontend.php" hash="0a97c590fdc9844b225638d631315d35"/><file name="Ftp.php" hash="095b9ad01ea60418b1667732cb374d95"/><dir name="Loader"><file name="Ftp.php" hash="0ca46737d25bf0eb321414f2d8b7ced0"/></dir><file name="Loader.php" hash="009d6f986d5ff406f7e94d2b395f6e25"/><dir name="Package"><file name="Extension.php" hash="68b329da9893e34099c7d8ad5cb9c940"/><file name="Hotfix.php" hash="850bd23783a77dbf6f50b4f169c2c7b3"/><file name="Maintainer.php" hash="d41d8cd98f00b204e9800998ecf8427e"/><file name="Reader.php" hash="ca384e066ac21168e0a7aebc5da869c1"/><file name="Target.php" hash="351af5965f7870d3f4e960d402805b17"/><file name="VO.php" hash="9ffbdff9210474b68db7caddf9a5fe5a"/><file name="Writer.php" hash="431f107ab2ffd1977f74a94ae16481d1"/></dir><file name="Package.php" hash="2df5f5a222c9e45bb9e4d8c0e5e6d170"/><file name="Packager.php" hash="37df01e250a103684b37a6f0b71b3128"/><dir name="Repository"><file name="Abstract.php" hash="d41d8cd98f00b204e9800998ecf8427e"/><dir name="Channel"><file name="Abstract.php" hash="d41d8cd98f00b204e9800998ecf8427e"/><file name="Commercial.php" hash="d41d8cd98f00b204e9800998ecf8427e"/><file name="Community.php" hash="d41d8cd98f00b204e9800998ecf8427e"/><file name="Core.php" hash="d41d8cd98f00b204e9800998ecf8427e"/></dir><file name="Channel.php" hash="d41d8cd98f00b204e9800998ecf8427e"/><file name="Local.php" hash="d41d8cd98f00b204e9800998ecf8427e"/></dir><file name="Repository.php" hash="d41d8cd98f00b204e9800998ecf8427e"/><file name="Rest.php" hash="ba2d2365fc989274940e1fdff6476875"/><file name="Singleconfig.php" hash="975c04d57cbce24327e84a93d9f5443e"/><dir name="Structures"><file name="Graph.php" hash="b85283fd2af6fd54fb748cb6ea933842"/><file name="Node.php" hash="26a7e4041c3d2fd93bfdc3c692c85ede"/></dir><file name="Validator.php" hash="3ac1b21ec703073d5dd9b5dda3471fe8"/></dir><dir name="DB"><file name="Exception.php" hash="e0b55d77af8e215135ef223f593832ad"/><file name="Mysqli.php" hash="5057ea65f441bbb00c7e55298e9252a4"/></dir><file name="Exception.php" hash="9bf1aefdb7fd166d25889f36707997dc"/><dir name="HTTP"><dir name="Client"><file name="Curl.php" hash="f537a9879781ec3fe2748677bee43737"/><file name="Socket.php" hash="7a889fed729d92eba849d6fdfa32d7d6"/></dir><file name="Client.php" hash="7072f9366680bc5ebc1c447700193cb4"/><file name="IClient.php" hash="88a4a88a31a05a2154d430d7b4308ff6"/></dir><dir name="System"><file name="Args.php" hash="8080a06707f4393b9155441ed4160f4a"/><file name="Dirs.php" hash="adc832887e04f359b2ec050b0b2e6d17"/></dir><dir name="Xml"><file name="Generator.php" hash="0ddc93b0c75885f67d049d0a38c630fa"/><file name="Parser.php" hash="b2bdc0880cd5bc2d82468ee075e783a2"/></dir></dir></target></contents>
16
+ <compatible/>
17
+ <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
18
+ </package>