SCP.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Pure-PHP implementation of SCP.
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
  9. *
  10. * Here's a short example of how to use this library:
  11. * <code>
  12. * <?php
  13. * include('Net/SCP.php');
  14. * include('Net/SSH2.php');
  15. *
  16. * $ssh = new Net_SSH2('www.domain.tld');
  17. * if (!$ssh->login('username', 'password')) {
  18. * exit('bad login');
  19. * }
  20. * $scp = new Net_SCP($ssh);
  21. * $scp->put('abcd', str_repeat('x', 1024*1024));
  22. * ?>
  23. * </code>
  24. *
  25. * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  26. * of this software and associated documentation files (the "Software"), to deal
  27. * in the Software without restriction, including without limitation the rights
  28. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  29. * copies of the Software, and to permit persons to whom the Software is
  30. * furnished to do so, subject to the following conditions:
  31. *
  32. * The above copyright notice and this permission notice shall be included in
  33. * all copies or substantial portions of the Software.
  34. *
  35. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  36. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  37. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  38. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  39. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  40. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  41. * THE SOFTWARE.
  42. *
  43. * @category Net
  44. * @package Net_SCP
  45. * @author Jim Wigginton <terrafrost@php.net>
  46. * @copyright MMX Jim Wigginton
  47. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  48. * @link http://phpseclib.sourceforge.net
  49. */
  50. /**#@+
  51. * @access public
  52. * @see Net_SCP::put()
  53. */
  54. /**
  55. * Reads data from a local file.
  56. */
  57. define('NET_SCP_LOCAL_FILE', 1);
  58. /**
  59. * Reads data from a string.
  60. */
  61. define('NET_SCP_STRING', 2);
  62. /**#@-*/
  63. /**#@+
  64. * @access private
  65. * @see Net_SCP::_send()
  66. * @see Net_SCP::_receive()
  67. */
  68. /**
  69. * SSH1 is being used.
  70. */
  71. define('NET_SCP_SSH1', 1);
  72. /**
  73. * SSH2 is being used.
  74. */
  75. define('NET_SCP_SSH2', 2);
  76. /**#@-*/
  77. /**
  78. * Pure-PHP implementations of SCP.
  79. *
  80. * @author Jim Wigginton <terrafrost@php.net>
  81. * @version 0.1.0
  82. * @access public
  83. * @package Net_SCP
  84. */
  85. class Net_SCP {
  86. /**
  87. * SSH Object
  88. *
  89. * @var Object
  90. * @access private
  91. */
  92. var $ssh;
  93. /**
  94. * Packet Size
  95. *
  96. * @var Integer
  97. * @access private
  98. */
  99. var $packet_size;
  100. /**
  101. * Mode
  102. *
  103. * @var Integer
  104. * @access private
  105. */
  106. var $mode;
  107. /**
  108. * Default Constructor.
  109. *
  110. * Connects to an SSH server
  111. *
  112. * @param String $host
  113. * @param optional Integer $port
  114. * @param optional Integer $timeout
  115. * @return Net_SCP
  116. * @access public
  117. */
  118. function Net_SCP($ssh)
  119. {
  120. if (!is_object($ssh)) {
  121. return;
  122. }
  123. switch (strtolower(get_class($ssh))) {
  124. case'net_ssh2':
  125. $this->mode = NET_SCP_SSH2;
  126. break;
  127. case 'net_ssh1':
  128. $this->packet_size = 50000;
  129. $this->mode = NET_SCP_SSH1;
  130. break;
  131. default:
  132. return;
  133. }
  134. $this->ssh = $ssh;
  135. }
  136. /**
  137. * Uploads a file to the SCP server.
  138. *
  139. * By default, Net_SCP::put() does not read from the local filesystem. $data is dumped directly into $remote_file.
  140. * So, for example, if you set $data to 'filename.ext' and then do Net_SCP::get(), you will get a file, twelve bytes
  141. * long, containing 'filename.ext' as its contents.
  142. *
  143. * Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior. With NET_SFTP_LOCAL_FILE, $remote_file will
  144. * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
  145. * large $remote_file will be, as well.
  146. *
  147. * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take
  148. * care of that, yourself.
  149. *
  150. * @param String $remote_file
  151. * @param String $data
  152. * @param optional Integer $mode
  153. * @return Boolean
  154. * @access public
  155. */
  156. function put($remote_file, $data, $mode = NET_SCP_STRING)
  157. {
  158. if (!isset($this->ssh)) {
  159. return false;
  160. }
  161. $this->ssh->exec('scp -t ' . $remote_file, false); // -t = to
  162. $temp = $this->_receive();
  163. if ($temp !== chr(0)) {
  164. return false;
  165. }
  166. if ($this->mode == NET_SCP_SSH2) {
  167. $this->packet_size = $this->ssh->packet_size_client_to_server[NET_SSH2_CHANNEL_EXEC];
  168. }
  169. $remote_file = basename($remote_file);
  170. $this->_send('C0644 ' . strlen($data) . ' ' . $remote_file . "\n");
  171. $temp = $this->_receive();
  172. if ($temp !== chr(0)) {
  173. return false;
  174. }
  175. if ($mode == NET_SCP_STRING) {
  176. $this->_send($data);
  177. } else {
  178. if (!is_file($data)) {
  179. user_error("$data is not a valid file", E_USER_NOTICE);
  180. return false;
  181. }
  182. $fp = @fopen($data, 'rb');
  183. if (!$fp) {
  184. return false;
  185. }
  186. $size = filesize($data);
  187. for ($i = 0; $i < $size; $i += $this->packet_size) {
  188. $this->_send(fgets($fp, $this->packet_size));
  189. }
  190. fclose($fp);
  191. }
  192. $this->_close();
  193. }
  194. /**
  195. * Downloads a file from the SCP server.
  196. *
  197. * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
  198. * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the
  199. * operation
  200. *
  201. * @param String $remote_file
  202. * @param optional String $local_file
  203. * @return Mixed
  204. * @access public
  205. */
  206. function get($remote_file, $local_file = false)
  207. {
  208. if (!isset($this->ssh)) {
  209. return false;
  210. }
  211. $this->ssh->exec('scp -f ' . $remote_file, false); // -f = from
  212. $this->_send("\0");
  213. if (!preg_match('#(?<perms>[^ ]+) (?<size>\d+) (?<name>.+)#', rtrim($this->_receive()), $info)) {
  214. return false;
  215. }
  216. $this->_send("\0");
  217. $size = 0;
  218. if ($local_file !== false) {
  219. $fp = @fopen($local_file, 'wb');
  220. if (!$fp) {
  221. return false;
  222. }
  223. }
  224. $content = '';
  225. while ($size < $info['size']) {
  226. $data = $this->_receive();
  227. // SCP usually seems to split stuff out into 16k chunks
  228. $size+= strlen($data);
  229. if ($local_file === false) {
  230. $content.= $data;
  231. } else {
  232. fputs($fp, $data);
  233. }
  234. }
  235. $this->_close();
  236. if ($local_file !== false) {
  237. fclose($fp);
  238. return true;
  239. }
  240. return $content;
  241. }
  242. /**
  243. * Sends a packet to an SSH server
  244. *
  245. * @param String $data
  246. * @access private
  247. */
  248. function _send($data)
  249. {
  250. switch ($this->mode) {
  251. case NET_SCP_SSH2:
  252. $this->ssh->_send_channel_packet(NET_SSH2_CHANNEL_EXEC, $data);
  253. break;
  254. case NET_SCP_SSH1:
  255. $data = pack('CNa*', NET_SSH1_CMSG_STDIN_DATA, strlen($data), $data);
  256. $this->ssh->_send_binary_packet($data);
  257. }
  258. }
  259. /**
  260. * Receives a packet from an SSH server
  261. *
  262. * @return String
  263. * @access private
  264. */
  265. function _receive()
  266. {
  267. switch ($this->mode) {
  268. case NET_SCP_SSH2:
  269. return $this->ssh->_get_channel_packet(NET_SSH2_CHANNEL_EXEC, true);
  270. case NET_SCP_SSH1:
  271. if (!$this->ssh->bitmap) {
  272. return false;
  273. }
  274. while (true) {
  275. $response = $this->ssh->_get_binary_packet();
  276. switch ($response[NET_SSH1_RESPONSE_TYPE]) {
  277. case NET_SSH1_SMSG_STDOUT_DATA:
  278. extract(unpack('Nlength', $response[NET_SSH1_RESPONSE_DATA]));
  279. return $this->ssh->_string_shift($response[NET_SSH1_RESPONSE_DATA], $length);
  280. case NET_SSH1_SMSG_STDERR_DATA:
  281. break;
  282. case NET_SSH1_SMSG_EXITSTATUS:
  283. $this->ssh->_send_binary_packet(chr(NET_SSH1_CMSG_EXIT_CONFIRMATION));
  284. fclose($this->ssh->fsock);
  285. $this->ssh->bitmap = 0;
  286. return false;
  287. default:
  288. user_error('Unknown packet received', E_USER_NOTICE);
  289. return false;
  290. }
  291. }
  292. }
  293. }
  294. /**
  295. * Closes the connection to an SSH server
  296. *
  297. * @access private
  298. */
  299. function _close()
  300. {
  301. switch ($this->mode) {
  302. case NET_SCP_SSH2:
  303. $this->ssh->_close_channel(NET_SSH2_CHANNEL_EXEC);
  304. break;
  305. case NET_SCP_SSH1:
  306. $this->ssh->disconnect();
  307. }
  308. }
  309. }