SSH SFTP Updater Support - Version 0.8.5

Version Description

  • 2022/Dec/08 =

  • TWEAK: Update URL reference to current location

Download this release

Release Info

Developer DavidAnderson
Plugin Icon wp plugin SSH SFTP Updater Support
Version 0.8.5
Comparing to
See all releases

Code changes from version 0.8.4 to 0.8.5

phpseclib/Crypt/Base.php CHANGED
@@ -821,12 +821,13 @@ class Crypt_Base
821
  }
822
 
823
  if ($this->engine === CRYPT_ENGINE_MCRYPT) {
 
824
  if ($this->changed) {
825
  $this->_setupMcrypt();
826
  $this->changed = false;
827
  }
828
  if ($this->enchanged) {
829
- @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
830
  $this->enchanged = false;
831
  }
832
 
@@ -859,15 +860,15 @@ class Crypt_Base
859
  if ($len >= $block_size) {
860
  if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) {
861
  if ($this->enbuffer['enmcrypt_init'] === true) {
862
- @mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
863
  $this->enbuffer['enmcrypt_init'] = false;
864
  }
865
- $ciphertext.= @mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size));
866
  $iv = substr($ciphertext, -$block_size);
867
  $len%= $block_size;
868
  } else {
869
  while ($len >= $block_size) {
870
- $iv = @mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size);
871
  $ciphertext.= $iv;
872
  $len-= $block_size;
873
  $i+= $block_size;
@@ -876,22 +877,26 @@ class Crypt_Base
876
  }
877
 
878
  if ($len) {
879
- $iv = @mcrypt_generic($this->ecb, $iv);
880
  $block = $iv ^ substr($plaintext, -$len);
881
  $iv = substr_replace($iv, $block, 0, $len);
882
  $ciphertext.= $block;
883
  $pos = $len;
884
  }
885
 
 
 
886
  return $ciphertext;
887
  }
888
 
889
- $ciphertext = @mcrypt_generic($this->enmcrypt, $plaintext);
890
 
891
  if (!$this->continuousBuffer) {
892
- @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
893
  }
894
 
 
 
895
  return $ciphertext;
896
  }
897
 
@@ -1132,13 +1137,14 @@ class Crypt_Base
1132
  }
1133
 
1134
  if ($this->engine === CRYPT_ENGINE_MCRYPT) {
 
1135
  $block_size = $this->block_size;
1136
  if ($this->changed) {
1137
  $this->_setupMcrypt();
1138
  $this->changed = false;
1139
  }
1140
  if ($this->dechanged) {
1141
- @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
1142
  $this->dechanged = false;
1143
  }
1144
 
@@ -1166,26 +1172,30 @@ class Crypt_Base
1166
  }
1167
  if ($len >= $block_size) {
1168
  $cb = substr($ciphertext, $i, $len - $len % $block_size);
1169
- $plaintext.= @mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
1170
  $iv = substr($cb, -$block_size);
1171
  $len%= $block_size;
1172
  }
1173
  if ($len) {
1174
- $iv = @mcrypt_generic($this->ecb, $iv);
1175
  $plaintext.= $iv ^ substr($ciphertext, -$len);
1176
  $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
1177
  $pos = $len;
1178
  }
1179
 
 
 
1180
  return $plaintext;
1181
  }
1182
 
1183
- $plaintext = @mdecrypt_generic($this->demcrypt, $ciphertext);
1184
 
1185
  if (!$this->continuousBuffer) {
1186
- @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
1187
  }
1188
 
 
 
1189
  return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
1190
  }
1191
 
@@ -1643,9 +1653,12 @@ class Crypt_Base
1643
  }
1644
  return false;
1645
  case CRYPT_ENGINE_MCRYPT:
1646
- return $this->cipher_name_mcrypt &&
 
1647
  extension_loaded('mcrypt') &&
1648
- in_array($this->cipher_name_mcrypt, @mcrypt_list_algorithms());
 
 
1649
  case CRYPT_ENGINE_INTERNAL:
1650
  return true;
1651
  }
@@ -1722,17 +1735,19 @@ class Crypt_Base
1722
  }
1723
 
1724
  if ($this->engine != CRYPT_ENGINE_MCRYPT && $this->enmcrypt) {
 
1725
  // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed,
1726
  // (re)open them with the module named in $this->cipher_name_mcrypt
1727
- @mcrypt_module_close($this->enmcrypt);
1728
- @mcrypt_module_close($this->demcrypt);
1729
  $this->enmcrypt = null;
1730
  $this->demcrypt = null;
1731
 
1732
  if ($this->ecb) {
1733
- @mcrypt_module_close($this->ecb);
1734
  $this->ecb = null;
1735
  }
 
1736
  }
1737
 
1738
  $this->changed = true;
@@ -1850,19 +1865,19 @@ class Crypt_Base
1850
  CRYPT_MODE_STREAM => MCRYPT_MODE_STREAM,
1851
  );
1852
 
1853
- $this->demcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
1854
- $this->enmcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
1855
 
1856
  // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer()
1857
  // to workaround mcrypt's broken ncfb implementation in buffered mode
1858
  // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
1859
  if ($this->mode == CRYPT_MODE_CFB) {
1860
- $this->ecb = @mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, '');
1861
  }
1862
  } // else should mcrypt_generic_deinit be called?
1863
 
1864
  if ($this->mode == CRYPT_MODE_CFB) {
1865
- @mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size));
1866
  }
1867
  }
1868
 
@@ -2584,7 +2599,7 @@ class Crypt_Base
2584
  *
2585
  * @see self::_setupInlineCrypt()
2586
  * @access private
2587
- * @param $bytes
2588
  * @return string
2589
  */
2590
  function _hashInlineCryptFunction($bytes)
@@ -2657,4 +2672,13 @@ class Crypt_Base
2657
  return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
2658
  }
2659
  }
 
 
 
 
 
 
 
 
 
2660
  }
821
  }
822
 
823
  if ($this->engine === CRYPT_ENGINE_MCRYPT) {
824
+ set_error_handler(array($this, 'do_nothing'));
825
  if ($this->changed) {
826
  $this->_setupMcrypt();
827
  $this->changed = false;
828
  }
829
  if ($this->enchanged) {
830
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
831
  $this->enchanged = false;
832
  }
833
 
860
  if ($len >= $block_size) {
861
  if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) {
862
  if ($this->enbuffer['enmcrypt_init'] === true) {
863
+ mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
864
  $this->enbuffer['enmcrypt_init'] = false;
865
  }
866
+ $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size));
867
  $iv = substr($ciphertext, -$block_size);
868
  $len%= $block_size;
869
  } else {
870
  while ($len >= $block_size) {
871
+ $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size);
872
  $ciphertext.= $iv;
873
  $len-= $block_size;
874
  $i+= $block_size;
877
  }
878
 
879
  if ($len) {
880
+ $iv = mcrypt_generic($this->ecb, $iv);
881
  $block = $iv ^ substr($plaintext, -$len);
882
  $iv = substr_replace($iv, $block, 0, $len);
883
  $ciphertext.= $block;
884
  $pos = $len;
885
  }
886
 
887
+ restore_error_handler();
888
+
889
  return $ciphertext;
890
  }
891
 
892
+ $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
893
 
894
  if (!$this->continuousBuffer) {
895
+ mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
896
  }
897
 
898
+ restore_error_handler();
899
+
900
  return $ciphertext;
901
  }
902
 
1137
  }
1138
 
1139
  if ($this->engine === CRYPT_ENGINE_MCRYPT) {
1140
+ set_error_handler(array($this, 'do_nothing'));
1141
  $block_size = $this->block_size;
1142
  if ($this->changed) {
1143
  $this->_setupMcrypt();
1144
  $this->changed = false;
1145
  }
1146
  if ($this->dechanged) {
1147
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
1148
  $this->dechanged = false;
1149
  }
1150
 
1172
  }
1173
  if ($len >= $block_size) {
1174
  $cb = substr($ciphertext, $i, $len - $len % $block_size);
1175
+ $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
1176
  $iv = substr($cb, -$block_size);
1177
  $len%= $block_size;
1178
  }
1179
  if ($len) {
1180
+ $iv = mcrypt_generic($this->ecb, $iv);
1181
  $plaintext.= $iv ^ substr($ciphertext, -$len);
1182
  $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
1183
  $pos = $len;
1184
  }
1185
 
1186
+ restore_error_handler();
1187
+
1188
  return $plaintext;
1189
  }
1190
 
1191
+ $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
1192
 
1193
  if (!$this->continuousBuffer) {
1194
+ mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
1195
  }
1196
 
1197
+ restore_error_handler();
1198
+
1199
  return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
1200
  }
1201
 
1653
  }
1654
  return false;
1655
  case CRYPT_ENGINE_MCRYPT:
1656
+ set_error_handler(array($this, 'do_nothing'));
1657
+ $result = $this->cipher_name_mcrypt &&
1658
  extension_loaded('mcrypt') &&
1659
+ in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms());
1660
+ restore_error_handler();
1661
+ return $result;
1662
  case CRYPT_ENGINE_INTERNAL:
1663
  return true;
1664
  }
1735
  }
1736
 
1737
  if ($this->engine != CRYPT_ENGINE_MCRYPT && $this->enmcrypt) {
1738
+ set_error_handler(array($this, 'do_nothing'));
1739
  // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed,
1740
  // (re)open them with the module named in $this->cipher_name_mcrypt
1741
+ mcrypt_module_close($this->enmcrypt);
1742
+ mcrypt_module_close($this->demcrypt);
1743
  $this->enmcrypt = null;
1744
  $this->demcrypt = null;
1745
 
1746
  if ($this->ecb) {
1747
+ mcrypt_module_close($this->ecb);
1748
  $this->ecb = null;
1749
  }
1750
+ restore_error_handler();
1751
  }
1752
 
1753
  $this->changed = true;
1865
  CRYPT_MODE_STREAM => MCRYPT_MODE_STREAM,
1866
  );
1867
 
1868
+ $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
1869
+ $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
1870
 
1871
  // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer()
1872
  // to workaround mcrypt's broken ncfb implementation in buffered mode
1873
  // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
1874
  if ($this->mode == CRYPT_MODE_CFB) {
1875
+ $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, '');
1876
  }
1877
  } // else should mcrypt_generic_deinit be called?
1878
 
1879
  if ($this->mode == CRYPT_MODE_CFB) {
1880
+ mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size));
1881
  }
1882
  }
1883
 
2599
  *
2600
  * @see self::_setupInlineCrypt()
2601
  * @access private
2602
+ * @param string $bytes
2603
  * @return string
2604
  */
2605
  function _hashInlineCryptFunction($bytes)
2672
  return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
2673
  }
2674
  }
2675
+
2676
+ /**
2677
+ * Dummy error handler to suppress mcrypt errors
2678
+ *
2679
+ * @access private
2680
+ */
2681
+ function do_nothing()
2682
+ {
2683
+ }
2684
  }
phpseclib/Crypt/Hash.php CHANGED
@@ -191,7 +191,7 @@ class Crypt_Hash
191
  * PHP4 compatible Default Constructor.
192
  *
193
  * @see self::__construct()
194
- * @param int $mode
195
  * @access public
196
  */
197
  function Crypt_Hash($hash = 'sha1')
@@ -879,7 +879,6 @@ class Crypt_Hash
879
  * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the
880
  * possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster.
881
  *
882
- * @param int $...
883
  * @return int
884
  * @see self::_sha256()
885
  * @access private
191
  * PHP4 compatible Default Constructor.
192
  *
193
  * @see self::__construct()
194
+ * @param string $hash
195
  * @access public
196
  */
197
  function Crypt_Hash($hash = 'sha1')
879
  * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the
880
  * possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster.
881
  *
 
882
  * @return int
883
  * @see self::_sha256()
884
  * @access private
phpseclib/Crypt/RSA.php CHANGED
@@ -515,7 +515,7 @@ class Crypt_RSA
515
  case !function_exists('openssl_pkey_get_details'):
516
  define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
517
  break;
518
- case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile):
519
  // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
520
  ob_start();
521
  @phpinfo();
@@ -589,7 +589,7 @@ class Crypt_RSA
589
  * @access public
590
  * @param int $bits
591
  * @param int $timeout
592
- * @param Math_BigInteger $p
593
  */
594
  function createKey($bits = 1024, $timeout = false, $partial = array())
595
  {
@@ -768,7 +768,12 @@ class Crypt_RSA
768
  *
769
  * @access private
770
  * @see self::setPrivateKeyFormat()
771
- * @param string $RSAPrivateKey
 
 
 
 
 
772
  * @return string
773
  */
774
  function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
@@ -931,9 +936,9 @@ class Crypt_RSA
931
  );
932
  $key = "openssh-key-v1\0$key";
933
 
934
- return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" .
935
- chunk_split(base64_encode($key), 70) .
936
- "-----END OPENSSH PRIVATE KEY-----";
937
  default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
938
  $components = array();
939
  foreach ($raw as $name => $value) {
@@ -1061,8 +1066,9 @@ class Crypt_RSA
1061
  *
1062
  * @access private
1063
  * @see self::setPublicKeyFormat()
1064
- * @param string $RSAPrivateKey
1065
- * @return string
 
1066
  */
1067
  function _convertPublicKey($n, $e)
1068
  {
@@ -1292,6 +1298,7 @@ class Crypt_RSA
1292
  $length = $this->_decodeLength($temp);
1293
  switch ($this->_string_shift($temp, $length)) {
1294
  case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
 
1295
  break;
1296
  case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
1297
  /*
@@ -1624,6 +1631,8 @@ class Crypt_RSA
1624
 
1625
  return $components;
1626
  }
 
 
1627
  }
1628
 
1629
  /**
@@ -1962,7 +1971,6 @@ class Crypt_RSA
1962
  *
1963
  * @see self::getPublicKey()
1964
  * @access public
1965
- * @param string $key
1966
  * @param int $type optional
1967
  */
1968
  function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
@@ -2020,7 +2028,6 @@ class Crypt_RSA
2020
  *
2021
  * @see self::getPublicKey()
2022
  * @access public
2023
- * @param string $key
2024
  * @param int $type optional
2025
  * @return mixed
2026
  */
@@ -2045,8 +2052,7 @@ class Crypt_RSA
2045
  *
2046
  * @see self::getPrivateKey()
2047
  * @access private
2048
- * @param string $key
2049
- * @param int $type optional
2050
  */
2051
  function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
2052
  {
@@ -2263,7 +2269,7 @@ class Crypt_RSA
2263
  * of the hash function Hash) and 0.
2264
  *
2265
  * @access public
2266
- * @param int $format
2267
  */
2268
  function setSaltLength($sLen)
2269
  {
@@ -2296,7 +2302,7 @@ class Crypt_RSA
2296
  * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
2297
  *
2298
  * @access private
2299
- * @param string $x
2300
  * @return Math_BigInteger
2301
  */
2302
  function _os2ip($x)
@@ -2523,7 +2529,7 @@ class Crypt_RSA
2523
  *
2524
  * @access private
2525
  * @param string $mgfSeed
2526
- * @param int $mgfLen
2527
  * @return string
2528
  */
2529
  function _mgf1($mgfSeed, $maskLen)
@@ -2658,9 +2664,9 @@ class Crypt_RSA
2658
  $offset+= $patternMatch ? 0 : 1;
2659
  }
2660
 
2661
- // we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
2662
  // to protect against timing attacks
2663
- if (!$hashesMatch & !$patternMatch) {
2664
  user_error('Decryption error');
2665
  return false;
2666
  }
@@ -2995,6 +3001,59 @@ class Crypt_RSA
2995
  return $em;
2996
  }
2997
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2998
  /**
2999
  * RSASSA-PKCS1-V1_5-SIGN
3000
  *
@@ -3032,6 +3091,7 @@ class Crypt_RSA
3032
  *
3033
  * @access private
3034
  * @param string $m
 
3035
  * @return string
3036
  */
3037
  function _rsassa_pkcs1_v1_5_verify($m, $s)
@@ -3060,13 +3120,17 @@ class Crypt_RSA
3060
  // EMSA-PKCS1-v1_5 encoding
3061
 
3062
  $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
3063
- if ($em2 === false) {
 
 
3064
  user_error('RSA modulus too short');
3065
  return false;
3066
  }
3067
 
3068
  // Compare
3069
- return $this->_equals($em, $em2);
 
 
3070
  }
3071
 
3072
  /**
@@ -3172,7 +3236,7 @@ class Crypt_RSA
3172
  *
3173
  * @see self::encrypt()
3174
  * @access public
3175
- * @param string $plaintext
3176
  * @return string
3177
  */
3178
  function decrypt($ciphertext)
515
  case !function_exists('openssl_pkey_get_details'):
516
  define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
517
  break;
518
+ case function_exists('phpinfo') && extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile):
519
  // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
520
  ob_start();
521
  @phpinfo();
589
  * @access public
590
  * @param int $bits
591
  * @param int $timeout
592
+ * @param array $partial
593
  */
594
  function createKey($bits = 1024, $timeout = false, $partial = array())
595
  {
768
  *
769
  * @access private
770
  * @see self::setPrivateKeyFormat()
771
+ * @param Math_BigInteger $n
772
+ * @param Math_BigInteger $e
773
+ * @param Math_BigInteger $d
774
+ * @param array<int,Math_BigInteger> $primes
775
+ * @param array<int,Math_BigInteger> $exponents
776
+ * @param array<int,Math_BigInteger> $coefficients
777
  * @return string
778
  */
779
  function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
936
  );
937
  $key = "openssh-key-v1\0$key";
938
 
939
+ return "-----BEGIN OPENSSH PRIVATE KEY-----\n" .
940
+ chunk_split(base64_encode($key), 70, "\n") .
941
+ "-----END OPENSSH PRIVATE KEY-----\n";
942
  default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
943
  $components = array();
944
  foreach ($raw as $name => $value) {
1066
  *
1067
  * @access private
1068
  * @see self::setPublicKeyFormat()
1069
+ * @param Math_BigInteger $n
1070
+ * @param Math_BigInteger $e
1071
+ * @return string|array<string,Math_BigInteger>
1072
  */
1073
  function _convertPublicKey($n, $e)
1074
  {
1298
  $length = $this->_decodeLength($temp);
1299
  switch ($this->_string_shift($temp, $length)) {
1300
  case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption
1301
+ case "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": // rsaPSS
1302
  break;
1303
  case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC
1304
  /*
1631
 
1632
  return $components;
1633
  }
1634
+
1635
+ return false;
1636
  }
1637
 
1638
  /**
1971
  *
1972
  * @see self::getPublicKey()
1973
  * @access public
 
1974
  * @param int $type optional
1975
  */
1976
  function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
2028
  *
2029
  * @see self::getPublicKey()
2030
  * @access public
 
2031
  * @param int $type optional
2032
  * @return mixed
2033
  */
2052
  *
2053
  * @see self::getPrivateKey()
2054
  * @access private
2055
+ * @param int $mode optional
 
2056
  */
2057
  function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS8)
2058
  {
2269
  * of the hash function Hash) and 0.
2270
  *
2271
  * @access public
2272
+ * @param int $sLen
2273
  */
2274
  function setSaltLength($sLen)
2275
  {
2302
  * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
2303
  *
2304
  * @access private
2305
+ * @param int|string|resource $x
2306
  * @return Math_BigInteger
2307
  */
2308
  function _os2ip($x)
2529
  *
2530
  * @access private
2531
  * @param string $mgfSeed
2532
+ * @param int $maskLen
2533
  * @return string
2534
  */
2535
  function _mgf1($mgfSeed, $maskLen)
2664
  $offset+= $patternMatch ? 0 : 1;
2665
  }
2666
 
2667
+ // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation
2668
  // to protect against timing attacks
2669
+ if (!$hashesMatch | !$patternMatch) {
2670
  user_error('Decryption error');
2671
  return false;
2672
  }
3001
  return $em;
3002
  }
3003
 
3004
+ /**
3005
+ * EMSA-PKCS1-V1_5-ENCODE (without NULL)
3006
+ *
3007
+ * Quoting https://tools.ietf.org/html/rfc8017#page-65,
3008
+ *
3009
+ * "The parameters field associated with id-sha1, id-sha224, id-sha256,
3010
+ * id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should
3011
+ * generally be omitted, but if present, it shall have a value of type
3012
+ * NULL"
3013
+ *
3014
+ * @access private
3015
+ * @param string $m
3016
+ * @param int $emLen
3017
+ * @return string
3018
+ */
3019
+ function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen)
3020
+ {
3021
+ $h = $this->hash->hash($m);
3022
+ if ($h === false) {
3023
+ return false;
3024
+ }
3025
+
3026
+ switch ($this->hashName) {
3027
+ case 'sha1':
3028
+ $t = pack('H*', '301f300706052b0e03021a0414');
3029
+ break;
3030
+ case 'sha256':
3031
+ $t = pack('H*', '302f300b06096086480165030402010420');
3032
+ break;
3033
+ case 'sha384':
3034
+ $t = pack('H*', '303f300b06096086480165030402020430');
3035
+ break;
3036
+ case 'sha512':
3037
+ $t = pack('H*', '304f300b06096086480165030402030440');
3038
+ break;
3039
+ default:
3040
+ return false;
3041
+ }
3042
+ $t.= $h;
3043
+ $tLen = strlen($t);
3044
+
3045
+ if ($emLen < $tLen + 11) {
3046
+ user_error('Intended encoded message length too short');
3047
+ return false;
3048
+ }
3049
+
3050
+ $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
3051
+
3052
+ $em = "\0\1$ps\0$t";
3053
+
3054
+ return $em;
3055
+ }
3056
+
3057
  /**
3058
  * RSASSA-PKCS1-V1_5-SIGN
3059
  *
3091
  *
3092
  * @access private
3093
  * @param string $m
3094
+ * @param string $s
3095
  * @return string
3096
  */
3097
  function _rsassa_pkcs1_v1_5_verify($m, $s)
3120
  // EMSA-PKCS1-v1_5 encoding
3121
 
3122
  $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
3123
+ $em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m, $this->k);
3124
+
3125
+ if ($em2 === false && $em3 === false) {
3126
  user_error('RSA modulus too short');
3127
  return false;
3128
  }
3129
 
3130
  // Compare
3131
+
3132
+ return ($em2 !== false && $this->_equals($em, $em2)) ||
3133
+ ($em3 !== false && $this->_equals($em, $em3));
3134
  }
3135
 
3136
  /**
3236
  *
3237
  * @see self::encrypt()
3238
  * @access public
3239
+ * @param string $ciphertext
3240
  * @return string
3241
  */
3242
  function decrypt($ciphertext)
phpseclib/File/ANSI.php CHANGED
@@ -230,8 +230,7 @@ class File_ANSI
230
  /**
231
  * Set the number of lines that should be logged past the terminal height
232
  *
233
- * @param int $x
234
- * @param int $y
235
  * @access public
236
  */
237
  function setHistory($history)
@@ -343,19 +342,20 @@ class File_ANSI
343
  $mods = explode(';', $match[1]);
344
  foreach ($mods as $mod) {
345
  switch ($mod) {
346
- case 0: // Turn off character attributes
 
347
  $attr_cell = clone($this->base_attr_cell);
348
  break;
349
- case 1: // Turn bold mode on
350
  $attr_cell->bold = true;
351
  break;
352
- case 4: // Turn underline mode on
353
  $attr_cell->underline = true;
354
  break;
355
- case 5: // Turn blinking mode on
356
  $attr_cell->blink = true;
357
  break;
358
- case 7: // Turn reverse video on
359
  $attr_cell->reverse = !$attr_cell->reverse;
360
  $temp = $attr_cell->background;
361
  $attr_cell->background = $attr_cell->foreground;
@@ -368,23 +368,23 @@ class File_ANSI
368
  $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' };
369
  switch ($mod) {
370
  // @codingStandardsIgnoreStart
371
- case 30: $front = 'black'; break;
372
- case 31: $front = 'red'; break;
373
- case 32: $front = 'green'; break;
374
- case 33: $front = 'yellow'; break;
375
- case 34: $front = 'blue'; break;
376
- case 35: $front = 'magenta'; break;
377
- case 36: $front = 'cyan'; break;
378
- case 37: $front = 'white'; break;
379
-
380
- case 40: $back = 'black'; break;
381
- case 41: $back = 'red'; break;
382
- case 42: $back = 'green'; break;
383
- case 43: $back = 'yellow'; break;
384
- case 44: $back = 'blue'; break;
385
- case 45: $back = 'magenta'; break;
386
- case 46: $back = 'cyan'; break;
387
- case 47: $back = 'white'; break;
388
  // @codingStandardsIgnoreEnd
389
 
390
  default:
230
  /**
231
  * Set the number of lines that should be logged past the terminal height
232
  *
233
+ * @param int $history
 
234
  * @access public
235
  */
236
  function setHistory($history)
342
  $mods = explode(';', $match[1]);
343
  foreach ($mods as $mod) {
344
  switch ($mod) {
345
+ case '':
346
+ case '0': // Turn off character attributes
347
  $attr_cell = clone($this->base_attr_cell);
348
  break;
349
+ case '1': // Turn bold mode on
350
  $attr_cell->bold = true;
351
  break;
352
+ case '4': // Turn underline mode on
353
  $attr_cell->underline = true;
354
  break;
355
+ case '5': // Turn blinking mode on
356
  $attr_cell->blink = true;
357
  break;
358
+ case '7': // Turn reverse video on
359
  $attr_cell->reverse = !$attr_cell->reverse;
360
  $temp = $attr_cell->background;
361
  $attr_cell->background = $attr_cell->foreground;
368
  $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' };
369
  switch ($mod) {
370
  // @codingStandardsIgnoreStart
371
+ case '30': $front = 'black'; break;
372
+ case '31': $front = 'red'; break;
373
+ case '32': $front = 'green'; break;
374
+ case '33': $front = 'yellow'; break;
375
+ case '34': $front = 'blue'; break;
376
+ case '35': $front = 'magenta'; break;
377
+ case '36': $front = 'cyan'; break;
378
+ case '37': $front = 'white'; break;
379
+
380
+ case '40': $back = 'black'; break;
381
+ case '41': $back = 'red'; break;
382
+ case '42': $back = 'green'; break;
383
+ case '43': $back = 'yellow'; break;
384
+ case '44': $back = 'blue'; break;
385
+ case '45': $back = 'magenta'; break;
386
+ case '46': $back = 'cyan'; break;
387
+ case '47': $back = 'white'; break;
388
  // @codingStandardsIgnoreEnd
389
 
390
  default:
phpseclib/File/ASN1.php CHANGED
@@ -140,7 +140,7 @@ class File_ASN1_Element
140
  * PHP4 compatible Default Constructor.
141
  *
142
  * @see self::__construct()
143
- * @param int $mode
144
  * @access public
145
  */
146
  function File_ASN1_Element($encoded)
@@ -316,8 +316,11 @@ class File_ASN1
316
  {
317
  $current = array('start' => $start);
318
 
 
 
 
319
  $type = ord($encoded[$encoded_pos++]);
320
- $start++;
321
 
322
  $constructed = ($type >> 5) & 1;
323
 
@@ -326,15 +329,28 @@ class File_ASN1
326
  $tag = 0;
327
  // process septets (since the eighth bit is ignored, it's not an octet)
328
  do {
 
 
 
329
  $temp = ord($encoded[$encoded_pos++]);
 
330
  $loop = $temp >> 7;
331
  $tag <<= 7;
332
- $tag |= $temp & 0x7F;
333
- $start++;
 
 
 
 
334
  } while ($loop);
335
  }
336
 
 
 
337
  // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
 
 
 
338
  $length = ord($encoded[$encoded_pos++]);
339
  $start++;
340
  if ($length == 0x80) { // indefinite length
@@ -426,13 +442,16 @@ class File_ASN1
426
  switch ($tag) {
427
  case FILE_ASN1_TYPE_BOOLEAN:
428
  // "The contents octets shall consist of a single octet." -- paragraph 8.2.1
429
- //if (strlen($content) != 1) {
430
- // return false;
431
- //}
432
  $current['content'] = (bool) ord($content[$content_pos]);
433
  break;
434
  case FILE_ASN1_TYPE_INTEGER:
435
  case FILE_ASN1_TYPE_ENUMERATED:
 
 
 
436
  $current['content'] = new Math_BigInteger(substr($content, $content_pos), -256);
437
  break;
438
  case FILE_ASN1_TYPE_REAL: // not currently supported
@@ -452,15 +471,15 @@ class File_ASN1
452
  $last = count($temp) - 1;
453
  for ($i = 0; $i < $last; $i++) {
454
  // all subtags should be bit strings
455
- //if ($temp[$i]['type'] != FILE_ASN1_TYPE_BIT_STRING) {
456
- // return false;
457
- //}
458
  $current['content'].= substr($temp[$i]['content'], 1);
459
  }
460
  // all subtags should be bit strings
461
- //if ($temp[$last]['type'] != FILE_ASN1_TYPE_BIT_STRING) {
462
- // return false;
463
- //}
464
  $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1);
465
  }
466
  break;
@@ -477,9 +496,9 @@ class File_ASN1
477
  }
478
  $content_pos += $temp['length'];
479
  // all subtags should be octet strings
480
- //if ($temp['type'] != FILE_ASN1_TYPE_OCTET_STRING) {
481
- // return false;
482
- //}
483
  $current['content'].= $temp['content'];
484
  $length+= $temp['length'];
485
  }
@@ -490,12 +509,15 @@ class File_ASN1
490
  break;
491
  case FILE_ASN1_TYPE_NULL:
492
  // "The contents octets shall not contain any octets." -- paragraph 8.8.2
493
- //if (strlen($content)) {
494
- // return false;
495
- //}
496
  break;
497
  case FILE_ASN1_TYPE_SEQUENCE:
498
  case FILE_ASN1_TYPE_SET:
 
 
 
499
  $offset = 0;
500
  $current['content'] = array();
501
  $content_len = strlen($content);
@@ -516,7 +538,13 @@ class File_ASN1
516
  }
517
  break;
518
  case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
 
 
 
519
  $current['content'] = $this->_decodeOID(substr($content, $content_pos));
 
 
 
520
  break;
521
  /* Each character string type shall be encoded as if it had been declared:
522
  [UNIVERSAL x] IMPLICIT OCTET STRING
@@ -546,14 +574,22 @@ class File_ASN1
546
  case FILE_ASN1_TYPE_UTF8_STRING:
547
  // ????
548
  case FILE_ASN1_TYPE_BMP_STRING:
 
 
 
549
  $current['content'] = substr($content, $content_pos);
550
  break;
551
  case FILE_ASN1_TYPE_UTC_TIME:
552
  case FILE_ASN1_TYPE_GENERALIZED_TIME:
 
 
 
553
  $current['content'] = class_exists('DateTime') ?
554
  $this->_decodeDateTime(substr($content, $content_pos), $tag) :
555
  $this->_decodeUnixTime(substr($content, $content_pos), $tag);
 
556
  default:
 
557
  }
558
 
559
  $start+= $length;
@@ -887,7 +923,7 @@ class File_ASN1
887
  *
888
  * @param string $source
889
  * @param string $mapping
890
- * @param int $idx
891
  * @return string
892
  * @access public
893
  */
@@ -903,6 +939,7 @@ class File_ASN1
903
  * @param string $source
904
  * @param string $mapping
905
  * @param int $idx
 
906
  * @return string
907
  * @access private
908
  */
@@ -1065,7 +1102,10 @@ class File_ASN1
1065
  if (!class_exists('DateTime')) {
1066
  $value = @gmdate($format, strtotime($source)) . 'Z';
1067
  } else {
 
1068
  $date = new DateTime($source, new DateTimeZone('GMT'));
 
 
1069
  $value = $date->format($format) . 'Z';
1070
  }
1071
  break;
@@ -1227,6 +1267,11 @@ class File_ASN1
1227
  $oid = array();
1228
  $pos = 0;
1229
  $len = strlen($content);
 
 
 
 
 
1230
  $n = new Math_BigInteger();
1231
  while ($pos < $len) {
1232
  $temp = ord($content[$pos++]);
@@ -1262,7 +1307,7 @@ class File_ASN1
1262
  * Called by _encode_der()
1263
  *
1264
  * @access private
1265
- * @param string $content
1266
  * @return string
1267
  */
1268
  function _encodeOID($source)
140
  * PHP4 compatible Default Constructor.
141
  *
142
  * @see self::__construct()
143
+ * @param string $encoded
144
  * @access public
145
  */
146
  function File_ASN1_Element($encoded)
316
  {
317
  $current = array('start' => $start);
318
 
319
+ if (!isset($encoded[$encoded_pos])) {
320
+ return false;
321
+ }
322
  $type = ord($encoded[$encoded_pos++]);
323
+ $startOffset = 1;
324
 
325
  $constructed = ($type >> 5) & 1;
326
 
329
  $tag = 0;
330
  // process septets (since the eighth bit is ignored, it's not an octet)
331
  do {
332
+ if (!isset($encoded[$encoded_pos])) {
333
+ return false;
334
+ }
335
  $temp = ord($encoded[$encoded_pos++]);
336
+ $startOffset++;
337
  $loop = $temp >> 7;
338
  $tag <<= 7;
339
+ $temp &= 0x7F;
340
+ // "bits 7 to 1 of the first subsequent octet shall not all be zero"
341
+ if ($startOffset == 2 && $temp == 0) {
342
+ return false;
343
+ }
344
+ $tag |= $temp;
345
  } while ($loop);
346
  }
347
 
348
+ $start+= $startOffset;
349
+
350
  // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13
351
+ if (!isset($encoded[$encoded_pos])) {
352
+ return false;
353
+ }
354
  $length = ord($encoded[$encoded_pos++]);
355
  $start++;
356
  if ($length == 0x80) { // indefinite length
442
  switch ($tag) {
443
  case FILE_ASN1_TYPE_BOOLEAN:
444
  // "The contents octets shall consist of a single octet." -- paragraph 8.2.1
445
+ if ($constructed || strlen($content) != 1) {
446
+ return false;
447
+ }
448
  $current['content'] = (bool) ord($content[$content_pos]);
449
  break;
450
  case FILE_ASN1_TYPE_INTEGER:
451
  case FILE_ASN1_TYPE_ENUMERATED:
452
+ if ($constructed) {
453
+ return false;
454
+ }
455
  $current['content'] = new Math_BigInteger(substr($content, $content_pos), -256);
456
  break;
457
  case FILE_ASN1_TYPE_REAL: // not currently supported
471
  $last = count($temp) - 1;
472
  for ($i = 0; $i < $last; $i++) {
473
  // all subtags should be bit strings
474
+ if ($temp[$i]['type'] != FILE_ASN1_TYPE_BIT_STRING) {
475
+ return false;
476
+ }
477
  $current['content'].= substr($temp[$i]['content'], 1);
478
  }
479
  // all subtags should be bit strings
480
+ if ($temp[$last]['type'] != FILE_ASN1_TYPE_BIT_STRING) {
481
+ return false;
482
+ }
483
  $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1);
484
  }
485
  break;
496
  }
497
  $content_pos += $temp['length'];
498
  // all subtags should be octet strings
499
+ if ($temp['type'] != FILE_ASN1_TYPE_OCTET_STRING) {
500
+ return false;
501
+ }
502
  $current['content'].= $temp['content'];
503
  $length+= $temp['length'];
504
  }
509
  break;
510
  case FILE_ASN1_TYPE_NULL:
511
  // "The contents octets shall not contain any octets." -- paragraph 8.8.2
512
+ if ($constructed || strlen($content)) {
513
+ return false;
514
+ }
515
  break;
516
  case FILE_ASN1_TYPE_SEQUENCE:
517
  case FILE_ASN1_TYPE_SET:
518
+ if (!$constructed) {
519
+ return false;
520
+ }
521
  $offset = 0;
522
  $current['content'] = array();
523
  $content_len = strlen($content);
538
  }
539
  break;
540
  case FILE_ASN1_TYPE_OBJECT_IDENTIFIER:
541
+ if ($constructed) {
542
+ return false;
543
+ }
544
  $current['content'] = $this->_decodeOID(substr($content, $content_pos));
545
+ if ($current['content'] === false) {
546
+ return false;
547
+ }
548
  break;
549
  /* Each character string type shall be encoded as if it had been declared:
550
  [UNIVERSAL x] IMPLICIT OCTET STRING
574
  case FILE_ASN1_TYPE_UTF8_STRING:
575
  // ????
576
  case FILE_ASN1_TYPE_BMP_STRING:
577
+ if ($constructed) {
578
+ return false;
579
+ }
580
  $current['content'] = substr($content, $content_pos);
581
  break;
582
  case FILE_ASN1_TYPE_UTC_TIME:
583
  case FILE_ASN1_TYPE_GENERALIZED_TIME:
584
+ if ($constructed) {
585
+ return false;
586
+ }
587
  $current['content'] = class_exists('DateTime') ?
588
  $this->_decodeDateTime(substr($content, $content_pos), $tag) :
589
  $this->_decodeUnixTime(substr($content, $content_pos), $tag);
590
+ break;
591
  default:
592
+ return false;
593
  }
594
 
595
  $start+= $length;
923
  *
924
  * @param string $source
925
  * @param string $mapping
926
+ * @param array $special
927
  * @return string
928
  * @access public
929
  */
939
  * @param string $source
940
  * @param string $mapping
941
  * @param int $idx
942
+ * @param array $special
943
  * @return string
944
  * @access private
945
  */
1102
  if (!class_exists('DateTime')) {
1103
  $value = @gmdate($format, strtotime($source)) . 'Z';
1104
  } else {
1105
+ // if $source does _not_ include timezone information within it then assume that the timezone is GMT
1106
  $date = new DateTime($source, new DateTimeZone('GMT'));
1107
+ // if $source _does_ include timezone information within it then convert the time to GMT
1108
+ $date->setTimezone(new DateTimeZone('GMT'));
1109
  $value = $date->format($format) . 'Z';
1110
  }
1111
  break;
1267
  $oid = array();
1268
  $pos = 0;
1269
  $len = strlen($content);
1270
+
1271
+ if (ord($content[$len - 1]) & 0x80) {
1272
+ return false;
1273
+ }
1274
+
1275
  $n = new Math_BigInteger();
1276
  while ($pos < $len) {
1277
  $temp = ord($content[$pos++]);
1307
  * Called by _encode_der()
1308
  *
1309
  * @access private
1310
+ * @param string $source
1311
  * @return string
1312
  */
1313
  function _encodeOID($source)
phpseclib/File/X509.php CHANGED
@@ -1638,7 +1638,7 @@ class File_X509
1638
  * Map extension values from octet string to extension-specific internal
1639
  * format.
1640
  *
1641
- * @param array ref $root
1642
  * @param string $path
1643
  * @param object $asn1
1644
  * @access private
@@ -1652,7 +1652,6 @@ class File_X509
1652
  $id = $extensions[$i]['extnId'];
1653
  $value = &$extensions[$i]['extnValue'];
1654
  $value = base64_decode($value);
1655
- $decoded = $asn1->decodeBER($value);
1656
  /* [extnValue] contains the DER encoding of an ASN.1 value
1657
  corresponding to the extension type identified by extnID */
1658
  $map = $this->_getMapping($id);
@@ -1660,6 +1659,7 @@ class File_X509
1660
  $decoder = $id == 'id-ce-nameConstraints' ?
1661
  array($this, '_decodeNameConstraintIP') :
1662
  array($this, '_decodeIP');
 
1663
  $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder));
1664
  $value = $mapped === false ? $decoded[0] : $mapped;
1665
 
@@ -1691,7 +1691,7 @@ class File_X509
1691
  * Map extension values from extension-specific internal format to
1692
  * octet string.
1693
  *
1694
- * @param array ref $root
1695
  * @param string $path
1696
  * @param object $asn1
1697
  * @access private
@@ -1757,7 +1757,7 @@ class File_X509
1757
  * Map attribute values from ANY type to attribute-specific internal
1758
  * format.
1759
  *
1760
- * @param array ref $root
1761
  * @param string $path
1762
  * @param object $asn1
1763
  * @access private
@@ -1798,7 +1798,7 @@ class File_X509
1798
  * Map attribute values from attribute-specific internal format to
1799
  * ANY type.
1800
  *
1801
- * @param array ref $root
1802
  * @param string $path
1803
  * @param object $asn1
1804
  * @access private
@@ -1841,7 +1841,7 @@ class File_X509
1841
  * Map DN values from ANY type to DN-specific internal
1842
  * format.
1843
  *
1844
- * @param array ref $root
1845
  * @param string $path
1846
  * @param object $asn1
1847
  * @access private
@@ -1871,7 +1871,7 @@ class File_X509
1871
  * Map DN values from DN-specific internal format to
1872
  * ANY type.
1873
  *
1874
- * @param array ref $root
1875
  * @param string $path
1876
  * @param object $asn1
1877
  * @access private
@@ -3243,7 +3243,8 @@ class File_X509
3243
  /**
3244
  * Load a Certificate Signing Request
3245
  *
3246
- * @param string $csr
 
3247
  * @access public
3248
  * @return mixed
3249
  */
@@ -3383,7 +3384,7 @@ class File_X509
3383
  *
3384
  * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
3385
  *
3386
- * @param string $csr
3387
  * @access public
3388
  * @return mixed
3389
  */
@@ -3457,7 +3458,7 @@ class File_X509
3457
  /**
3458
  * Save a SPKAC CSR request
3459
  *
3460
- * @param array $csr
3461
  * @param int $format optional
3462
  * @access public
3463
  * @return string
@@ -3501,6 +3502,7 @@ class File_X509
3501
  * Load a Certificate Revocation List
3502
  *
3503
  * @param string $crl
 
3504
  * @access public
3505
  * @return mixed
3506
  */
@@ -4114,7 +4116,6 @@ class File_X509
4114
  * X.509 certificate signing helper function.
4115
  *
4116
  * @param object $key
4117
- * @param File_X509 $subject
4118
  * @param string $signatureAlgorithm
4119
  * @access public
4120
  * @return mixed
@@ -4192,7 +4193,7 @@ class File_X509
4192
  * Set Serial Number
4193
  *
4194
  * @param string $serial
4195
- * @param $base optional
4196
  * @access public
4197
  */
4198
  function setSerialNumber($serial, $base = -256)
@@ -4866,7 +4867,6 @@ class File_X509
4866
  * Set the IP Addresses's which the cert is to be valid for
4867
  *
4868
  * @access public
4869
- * @param string $ipAddress optional
4870
  */
4871
  function setIPAddress()
4872
  {
@@ -5144,11 +5144,16 @@ class File_X509
5144
  * subject=/O=organization/OU=org unit/CN=common name
5145
  * issuer=/O=organization/CN=common name
5146
  */
5147
- $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
5148
- // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
5149
- $temp = preg_replace('#-+[^-]+-+#', '', $temp);
 
 
 
5150
  // remove new lines
5151
  $temp = str_replace(array("\r", "\n", ' '), '', $temp);
 
 
5152
  $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
5153
  return $temp != false ? $temp : $str;
5154
  }
1638
  * Map extension values from octet string to extension-specific internal
1639
  * format.
1640
  *
1641
+ * @param array $root (by reference)
1642
  * @param string $path
1643
  * @param object $asn1
1644
  * @access private
1652
  $id = $extensions[$i]['extnId'];
1653
  $value = &$extensions[$i]['extnValue'];
1654
  $value = base64_decode($value);
 
1655
  /* [extnValue] contains the DER encoding of an ASN.1 value
1656
  corresponding to the extension type identified by extnID */
1657
  $map = $this->_getMapping($id);
1659
  $decoder = $id == 'id-ce-nameConstraints' ?
1660
  array($this, '_decodeNameConstraintIP') :
1661
  array($this, '_decodeIP');
1662
+ $decoded = $asn1->decodeBER($value);
1663
  $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder));
1664
  $value = $mapped === false ? $decoded[0] : $mapped;
1665
 
1691
  * Map extension values from extension-specific internal format to
1692
  * octet string.
1693
  *
1694
+ * @param array $root (by reference)
1695
  * @param string $path
1696
  * @param object $asn1
1697
  * @access private
1757
  * Map attribute values from ANY type to attribute-specific internal
1758
  * format.
1759
  *
1760
+ * @param array $root (by reference)
1761
  * @param string $path
1762
  * @param object $asn1
1763
  * @access private
1798
  * Map attribute values from attribute-specific internal format to
1799
  * ANY type.
1800
  *
1801
+ * @param array $root (by reference)
1802
  * @param string $path
1803
  * @param object $asn1
1804
  * @access private
1841
  * Map DN values from ANY type to DN-specific internal
1842
  * format.
1843
  *
1844
+ * @param array $root (by reference)
1845
  * @param string $path
1846
  * @param object $asn1
1847
  * @access private
1871
  * Map DN values from DN-specific internal format to
1872
  * ANY type.
1873
  *
1874
+ * @param array $root (by reference)
1875
  * @param string $path
1876
  * @param object $asn1
1877
  * @access private
3243
  /**
3244
  * Load a Certificate Signing Request
3245
  *
3246
+ * @param string|array $csr
3247
+ * @param int $mode
3248
  * @access public
3249
  * @return mixed
3250
  */
3384
  *
3385
  * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen
3386
  *
3387
+ * @param string|array $spkac
3388
  * @access public
3389
  * @return mixed
3390
  */
3458
  /**
3459
  * Save a SPKAC CSR request
3460
  *
3461
+ * @param string|array $spkac
3462
  * @param int $format optional
3463
  * @access public
3464
  * @return string
3502
  * Load a Certificate Revocation List
3503
  *
3504
  * @param string $crl
3505
+ * @param int $mode
3506
  * @access public
3507
  * @return mixed
3508
  */
4116
  * X.509 certificate signing helper function.
4117
  *
4118
  * @param object $key
 
4119
  * @param string $signatureAlgorithm
4120
  * @access public
4121
  * @return mixed
4193
  * Set Serial Number
4194
  *
4195
  * @param string $serial
4196
+ * @param int $base optional
4197
  * @access public
4198
  */
4199
  function setSerialNumber($serial, $base = -256)
4867
  * Set the IP Addresses's which the cert is to be valid for
4868
  *
4869
  * @access public
 
4870
  */
4871
  function setIPAddress()
4872
  {
5144
  * subject=/O=organization/OU=org unit/CN=common name
5145
  * issuer=/O=organization/CN=common name
5146
  */
5147
+ if (strlen($str) > ini_get('pcre.backtrack_limit')) {
5148
+ $temp = $str;
5149
+ } else {
5150
+ $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1);
5151
+ $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1);
5152
+ }
5153
  // remove new lines
5154
  $temp = str_replace(array("\r", "\n", ' '), '', $temp);
5155
+ // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff
5156
+ $temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp);
5157
  $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false;
5158
  return $temp != false ? $temp : $str;
5159
  }
phpseclib/Math/BigInteger.php CHANGED
@@ -237,7 +237,7 @@ class Math_BigInteger
237
  * ?>
238
  * </code>
239
  *
240
- * @param $x base-10 number or base-$base number if $base set.
241
  * @param int $base
242
  * @return Math_BigInteger
243
  * @access public
@@ -257,7 +257,7 @@ class Math_BigInteger
257
  }
258
  }
259
 
260
- if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
261
  // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
262
  ob_start();
263
  @phpinfo();
@@ -673,11 +673,11 @@ class Math_BigInteger
673
  {
674
  $hex = $this->toHex($twos_compliment);
675
  $bits = '';
676
- for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) {
677
- $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits;
678
  }
679
  if ($start) { // hexdec('') == 0
680
- $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits;
681
  }
682
  $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
683
 
@@ -2021,7 +2021,7 @@ class Math_BigInteger
2021
  *
2022
  * @see self::_slidingWindow()
2023
  * @access private
2024
- * @param Math_BigInteger
2025
  * @return Math_BigInteger
2026
  */
2027
  function _mod2($n)
@@ -3136,7 +3136,7 @@ class Math_BigInteger
3136
  *
3137
  * Byte length is equal to $length. Uses crypt_random if it's loaded and mt_rand if it's not.
3138
  *
3139
- * @param int $length
3140
  * @return Math_BigInteger
3141
  * @access private
3142
  */
@@ -3603,7 +3603,7 @@ class Math_BigInteger
3603
  *
3604
  * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
3605
  *
3606
- * @param Math_BigInteger
3607
  * @return Math_BigInteger
3608
  * @see self::_trim()
3609
  * @access private
@@ -3680,8 +3680,8 @@ class Math_BigInteger
3680
  /**
3681
  * Array Repeat
3682
  *
3683
- * @param $input Array
3684
- * @param $multiplier mixed
3685
  * @return array
3686
  * @access private
3687
  */
@@ -3695,8 +3695,8 @@ class Math_BigInteger
3695
  *
3696
  * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
3697
  *
3698
- * @param $x String
3699
- * @param $shift Integer
3700
  * @return string
3701
  * @access private
3702
  */
@@ -3724,8 +3724,8 @@ class Math_BigInteger
3724
  *
3725
  * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
3726
  *
3727
- * @param $x String
3728
- * @param $shift Integer
3729
  * @return string
3730
  * @access private
3731
  */
237
  * ?>
238
  * </code>
239
  *
240
+ * @param int|string|resource $x base-10 number or base-$base number if $base set.
241
  * @param int $base
242
  * @return Math_BigInteger
243
  * @access public
257
  }
258
  }
259
 
260
+ if (function_exists('phpinfo') && extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) {
261
  // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work
262
  ob_start();
263
  @phpinfo();
673
  {
674
  $hex = $this->toHex($twos_compliment);
675
  $bits = '';
676
+ for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >= $start; $i-=6) {
677
+ $bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24, '0', STR_PAD_LEFT) . $bits;
678
  }
679
  if ($start) { // hexdec('') == 0
680
+ $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8 * $start, '0', STR_PAD_LEFT) . $bits;
681
  }
682
  $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
683
 
2021
  *
2022
  * @see self::_slidingWindow()
2023
  * @access private
2024
+ * @param Math_BigInteger $n
2025
  * @return Math_BigInteger
2026
  */
2027
  function _mod2($n)
3136
  *
3137
  * Byte length is equal to $length. Uses crypt_random if it's loaded and mt_rand if it's not.
3138
  *
3139
+ * @param int $size
3140
  * @return Math_BigInteger
3141
  * @access private
3142
  */
3603
  *
3604
  * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
3605
  *
3606
+ * @param Math_BigInteger $result
3607
  * @return Math_BigInteger
3608
  * @see self::_trim()
3609
  * @access private
3680
  /**
3681
  * Array Repeat
3682
  *
3683
+ * @param array $input
3684
+ * @param mixed $multiplier
3685
  * @return array
3686
  * @access private
3687
  */
3695
  *
3696
  * Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
3697
  *
3698
+ * @param string $x (by reference)
3699
+ * @param int $shift
3700
  * @return string
3701
  * @access private
3702
  */
3724
  *
3725
  * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
3726
  *
3727
+ * @param string $x (by referenc)
3728
+ * @param int $shift
3729
  * @return string
3730
  * @access private
3731
  */
phpseclib/Net/SFTP.php CHANGED
@@ -5,9 +5,7 @@
5
  *
6
  * PHP versions 4 and 5
7
  *
8
- * Currently only supports SFTPv2 and v3, which, according to wikipedia.org, "is the most widely used version,
9
- * implemented by the popular OpenSSH SFTP server". If you want SFTPv4/5/6 support, provide me with access
10
- * to an SFTPv4/5/6 server.
11
  *
12
  * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
13
  *
@@ -195,6 +193,24 @@ class Net_SFTP extends Net_SSH2
195
  */
196
  var $version;
197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  /**
199
  * Current working directory
200
  *
@@ -300,6 +316,49 @@ class Net_SFTP extends Net_SSH2
300
  */
301
  var $requestBuffer = array();
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  /**
304
  * Default Constructor.
305
  *
@@ -320,15 +379,13 @@ class Net_SFTP extends Net_SSH2
320
  $this->packet_types = array(
321
  1 => 'NET_SFTP_INIT',
322
  2 => 'NET_SFTP_VERSION',
323
- /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+:
324
- SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
325
- pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
326
  3 => 'NET_SFTP_OPEN',
327
  4 => 'NET_SFTP_CLOSE',
328
  5 => 'NET_SFTP_READ',
329
  6 => 'NET_SFTP_WRITE',
330
  7 => 'NET_SFTP_LSTAT',
331
  9 => 'NET_SFTP_SETSTAT',
 
332
  11 => 'NET_SFTP_OPENDIR',
333
  12 => 'NET_SFTP_READDIR',
334
  13 => 'NET_SFTP_REMOVE',
@@ -336,18 +393,13 @@ class Net_SFTP extends Net_SSH2
336
  15 => 'NET_SFTP_RMDIR',
337
  16 => 'NET_SFTP_REALPATH',
338
  17 => 'NET_SFTP_STAT',
339
- /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+:
340
- SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
341
- pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
342
  18 => 'NET_SFTP_RENAME',
343
  19 => 'NET_SFTP_READLINK',
344
  20 => 'NET_SFTP_SYMLINK',
 
345
 
346
  101=> 'NET_SFTP_STATUS',
347
  102=> 'NET_SFTP_HANDLE',
348
- /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+:
349
- SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
350
- pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
351
  103=> 'NET_SFTP_DATA',
352
  104=> 'NET_SFTP_NAME',
353
  105=> 'NET_SFTP_ATTRS',
@@ -392,25 +444,59 @@ class Net_SFTP extends Net_SSH2
392
  // the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why
393
  $this->attributes = array(
394
  0x00000001 => 'NET_SFTP_ATTR_SIZE',
395
- 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
 
396
  0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
397
  0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
 
 
 
 
 
 
 
 
 
 
 
398
  // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
399
  // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
400
  // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
401
  // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
402
  (-1 << 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED'
403
  );
404
- // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
405
- // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name
406
- // the array for that $this->open5_flags and similarly alter the constant names.
407
  $this->open_flags = array(
408
  0x00000001 => 'NET_SFTP_OPEN_READ',
409
  0x00000002 => 'NET_SFTP_OPEN_WRITE',
410
  0x00000004 => 'NET_SFTP_OPEN_APPEND',
411
  0x00000008 => 'NET_SFTP_OPEN_CREATE',
412
  0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
413
- 0x00000020 => 'NET_SFTP_OPEN_EXCL'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  );
415
  // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
416
  // see Net_SFTP::_parseLongname() for an explanation
@@ -432,6 +518,7 @@ class Net_SFTP extends Net_SSH2
432
  $this->status_codes,
433
  $this->attributes,
434
  $this->open_flags,
 
435
  $this->file_types
436
  );
437
 
@@ -458,23 +545,32 @@ class Net_SFTP extends Net_SSH2
458
  }
459
 
460
  /**
461
- * Login
462
  *
463
- * @param string $username
464
- * @param string $password
465
  * @return bool
466
  * @access public
467
  */
468
- function login($username)
469
  {
470
- $args = func_get_args();
471
- $callback = version_compare(PHP_VERSION, '5.3.0') < 0 ?
472
- array(&$this, 'parent::login') :
473
- 'parent::login';
474
- if (!call_user_func_array($callback, $args)) {
475
  return false;
476
  }
477
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
  $this->window_size_server_to_client[NET_SFTP_CHANNEL] = $this->window_size;
479
 
480
  $packet = pack(
@@ -496,6 +592,8 @@ class Net_SFTP extends Net_SSH2
496
  $response = $this->_get_channel_packet(NET_SFTP_CHANNEL, true);
497
  if ($response === false) {
498
  return false;
 
 
499
  }
500
 
501
  $packet = pack(
@@ -542,6 +640,8 @@ class Net_SFTP extends Net_SSH2
542
  if ($response === false) {
543
  return false;
544
  }
 
 
545
  }
546
 
547
  $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
@@ -556,11 +656,13 @@ class Net_SFTP extends Net_SSH2
556
  return false;
557
  }
558
 
 
 
559
  if (strlen($response) < 4) {
560
  return false;
561
  }
562
  extract(unpack('Nversion', $this->_string_shift($response, 4)));
563
- $this->version = $version;
564
  while (!empty($response)) {
565
  if (strlen($response) < 4) {
566
  return false;
@@ -575,21 +677,22 @@ class Net_SFTP extends Net_SSH2
575
  $this->extensions[$key] = $value;
576
  }
577
 
578
- /*
579
- SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
580
- however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
581
- not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
582
- one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
583
- 'newline@vandyke.com' would.
584
- */
585
- /*
586
- if (isset($this->extensions['newline@vandyke.com'])) {
587
- $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
588
- unset($this->extensions['newline@vandyke.com']);
589
- }
590
- */
591
 
592
- $this->use_request_id = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
593
 
594
  /*
595
  A Note on SFTPv4/5/6 support:
@@ -614,12 +717,60 @@ class Net_SFTP extends Net_SSH2
614
  in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what Net_SFTP would do is close the
615
  channel and reopen it with a new and updated SSH_FXP_INIT packet.
616
  */
617
- switch ($this->version) {
618
- case 2:
619
- case 3:
620
- break;
621
- default:
622
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
623
  }
624
 
625
  $this->pwd = $this->_realpath('.');
@@ -679,6 +830,26 @@ class Net_SFTP extends Net_SSH2
679
  $this->canonicalize_paths = false;
680
  }
681
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
682
  /**
683
  * Returns the current directory name
684
  *
@@ -687,6 +858,10 @@ class Net_SFTP extends Net_SSH2
687
  */
688
  function pwd()
689
  {
 
 
 
 
690
  return $this->pwd;
691
  }
692
 
@@ -728,6 +903,10 @@ class Net_SFTP extends Net_SSH2
728
  */
729
  function realpath($path)
730
  {
 
 
 
 
731
  return $this->_realpath($path);
732
  }
733
 
@@ -810,7 +989,7 @@ class Net_SFTP extends Net_SSH2
810
  */
811
  function chdir($dir)
812
  {
813
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
814
  return false;
815
  }
816
 
@@ -967,7 +1146,7 @@ class Net_SFTP extends Net_SSH2
967
  */
968
  function _list($dir, $raw = true)
969
  {
970
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
971
  return false;
972
  }
973
 
@@ -1022,13 +1201,17 @@ class Net_SFTP extends Net_SSH2
1022
  }
1023
  extract(unpack('Nlength', $this->_string_shift($response, 4)));
1024
  $shortname = $this->_string_shift($response, $length);
1025
- if (strlen($response) < 4) {
1026
- return false;
 
 
 
 
 
 
1027
  }
1028
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
1029
- $longname = $this->_string_shift($response, $length);
1030
  $attributes = $this->_parseAttributes($response);
1031
- if (!isset($attributes['type'])) {
1032
  $fileType = $this->_parseLongname($longname);
1033
  if ($fileType) {
1034
  $attributes['type'] = $fileType;
@@ -1074,7 +1257,7 @@ class Net_SFTP extends Net_SSH2
1074
  uasort($contents, array(&$this, '_comparator'));
1075
  }
1076
 
1077
- return $raw ? $contents : array_keys($contents);
1078
  }
1079
 
1080
  /**
@@ -1188,10 +1371,6 @@ class Net_SFTP extends Net_SSH2
1188
  */
1189
  function size($filename)
1190
  {
1191
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1192
- return false;
1193
- }
1194
-
1195
  $result = $this->stat($filename);
1196
  if ($result === false) {
1197
  return false;
@@ -1276,7 +1455,7 @@ class Net_SFTP extends Net_SSH2
1276
  *
1277
  * Mainly used by file_exists
1278
  *
1279
- * @param string $dir
1280
  * @return mixed
1281
  * @access private
1282
  */
@@ -1308,7 +1487,7 @@ class Net_SFTP extends Net_SSH2
1308
  */
1309
  function stat($filename)
1310
  {
1311
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1312
  return false;
1313
  }
1314
 
@@ -1365,7 +1544,7 @@ class Net_SFTP extends Net_SSH2
1365
  */
1366
  function lstat($filename)
1367
  {
1368
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1369
  return false;
1370
  }
1371
 
@@ -1479,7 +1658,7 @@ class Net_SFTP extends Net_SSH2
1479
  */
1480
  function touch($filename, $time = null, $atime = null)
1481
  {
1482
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1483
  return false;
1484
  }
1485
 
@@ -1495,9 +1674,25 @@ class Net_SFTP extends Net_SSH2
1495
  $atime = $time;
1496
  }
1497
 
1498
- $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL;
1499
- $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime);
1500
- $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1501
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
1502
  return false;
1503
  }
@@ -1520,19 +1715,47 @@ class Net_SFTP extends Net_SSH2
1520
  /**
1521
  * Changes file or directory owner
1522
  *
 
 
 
 
 
1523
  * Returns true on success or false on error.
1524
  *
1525
  * @param string $filename
1526
- * @param int $uid
1527
  * @param bool $recursive
1528
  * @return bool
1529
  * @access public
1530
  */
1531
  function chown($filename, $uid, $recursive = false)
1532
  {
1533
- // quoting from <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
1534
- // "if the owner or group is specified as -1, then that ID is not changed"
1535
- $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1536
 
1537
  return $this->_setstat($filename, $attr, $recursive);
1538
  }
@@ -1540,17 +1763,24 @@ class Net_SFTP extends Net_SSH2
1540
  /**
1541
  * Changes file or directory group
1542
  *
 
 
 
 
 
1543
  * Returns true on success or false on error.
1544
  *
1545
  * @param string $filename
1546
- * @param int $gid
1547
  * @param bool $recursive
1548
  * @return bool
1549
  * @access public
1550
  */
1551
  function chgrp($filename, $gid, $recursive = false)
1552
  {
1553
- $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid);
 
 
1554
 
1555
  return $this->_setstat($filename, $attr, $recursive);
1556
  }
@@ -1617,7 +1847,7 @@ class Net_SFTP extends Net_SSH2
1617
  */
1618
  function _setstat($filename, $attr, $recursive)
1619
  {
1620
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1621
  return false;
1622
  }
1623
 
@@ -1635,9 +1865,10 @@ class Net_SFTP extends Net_SSH2
1635
  return $result;
1636
  }
1637
 
1638
- // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
1639
- // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
1640
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) {
 
1641
  return false;
1642
  }
1643
 
@@ -1707,7 +1938,10 @@ class Net_SFTP extends Net_SSH2
1707
  return false;
1708
  }
1709
  } else {
1710
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) {
 
 
 
1711
  return false;
1712
  }
1713
 
@@ -1722,7 +1956,10 @@ class Net_SFTP extends Net_SSH2
1722
  }
1723
  }
1724
 
1725
- if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) {
 
 
 
1726
  return false;
1727
  }
1728
 
@@ -1747,7 +1984,7 @@ class Net_SFTP extends Net_SSH2
1747
  */
1748
  function readlink($link)
1749
  {
1750
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1751
  return false;
1752
  }
1753
 
@@ -1797,15 +2034,44 @@ class Net_SFTP extends Net_SSH2
1797
  */
1798
  function symlink($target, $link)
1799
  {
1800
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1801
  return false;
1802
  }
1803
 
1804
  //$target = $this->_realpath($target);
1805
  $link = $this->_realpath($link);
1806
 
1807
- $packet = pack('Na*Na*', strlen($target), $target, strlen($link), $link);
1808
- if (!$this->_send_sftp_packet(NET_SFTP_SYMLINK, $packet)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1809
  return false;
1810
  }
1811
 
@@ -1831,12 +2097,14 @@ class Net_SFTP extends Net_SSH2
1831
  * Creates a directory.
1832
  *
1833
  * @param string $dir
 
 
1834
  * @return bool
1835
  * @access public
1836
  */
1837
  function mkdir($dir, $mode = -1, $recursive = false)
1838
  {
1839
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1840
  return false;
1841
  }
1842
 
@@ -1863,6 +2131,7 @@ class Net_SFTP extends Net_SSH2
1863
  * Helper function for directory creation
1864
  *
1865
  * @param string $dir
 
1866
  * @return bool
1867
  * @access private
1868
  */
@@ -1904,7 +2173,7 @@ class Net_SFTP extends Net_SSH2
1904
  */
1905
  function rmdir($dir)
1906
  {
1907
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1908
  return false;
1909
  }
1910
 
@@ -1955,7 +2224,6 @@ class Net_SFTP extends Net_SSH2
1955
  *
1956
  * If $data is a resource then it'll be used as a resource instead.
1957
  *
1958
- *
1959
  * Setting $mode to NET_SFTP_CALLBACK will use $data as callback function, which gets only one parameter -- number
1960
  * of bytes to return, and returns a string if there is some data or null if there is no more data
1961
  *
@@ -1991,7 +2259,7 @@ class Net_SFTP extends Net_SSH2
1991
  */
1992
  function put($remote_file, $data, $mode = NET_SFTP_STRING, $start = -1, $local_start = -1, $progressCallback = null)
1993
  {
1994
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
1995
  return false;
1996
  }
1997
 
@@ -2002,10 +2270,14 @@ class Net_SFTP extends Net_SSH2
2002
 
2003
  $this->_remove_from_stat_cache($remote_file);
2004
 
2005
- $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
2006
- // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
2007
- // in practice, it doesn't seem to do that.
2008
- //$flags|= ($mode & NET_SFTP_RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
 
 
 
 
2009
 
2010
  if ($start >= 0) {
2011
  $offset = $start;
@@ -2015,10 +2287,17 @@ class Net_SFTP extends Net_SSH2
2015
  $offset = $size !== false ? $size : 0;
2016
  } else {
2017
  $offset = 0;
2018
- $flags|= NET_SFTP_OPEN_TRUNCATE;
 
 
 
 
2019
  }
2020
 
2021
- $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0);
 
 
 
2022
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
2023
  return false;
2024
  }
@@ -2085,8 +2364,8 @@ class Net_SFTP extends Net_SSH2
2085
  $sent = 0;
2086
  $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
2087
 
2088
- $sftp_packet_size = 4096; // PuTTY uses 4096
2089
- // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header"
2090
  $sftp_packet_size-= strlen($handle) + 25;
2091
  $i = $j = 0;
2092
  while ($dataCallback || ($size === 0 || $sent < $size)) {
@@ -2127,6 +2406,8 @@ class Net_SFTP extends Net_SSH2
2127
  }
2128
  }
2129
 
 
 
2130
  if (!$this->_read_put_responses($i)) {
2131
  if ($mode & NET_SFTP_LOCAL_FILE) {
2132
  fclose($fp);
@@ -2136,10 +2417,32 @@ class Net_SFTP extends Net_SSH2
2136
  }
2137
 
2138
  if ($mode & NET_SFTP_LOCAL_FILE) {
2139
- fclose($fp);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2140
  }
2141
 
2142
- return $this->_close_handle($handle);
2143
  }
2144
 
2145
  /**
@@ -2226,7 +2529,7 @@ class Net_SFTP extends Net_SSH2
2226
  */
2227
  function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)
2228
  {
2229
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
2230
  return false;
2231
  }
2232
 
@@ -2235,7 +2538,10 @@ class Net_SFTP extends Net_SSH2
2235
  return false;
2236
  }
2237
 
2238
- $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
 
 
 
2239
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
2240
  return false;
2241
  }
@@ -2259,7 +2565,7 @@ class Net_SFTP extends Net_SSH2
2259
  $res_offset = $stat['size'];
2260
  } else {
2261
  $res_offset = 0;
2262
- if ($local_file !== false) {
2263
  $fp = fopen($local_file, 'wb');
2264
  if (!$fp) {
2265
  return false;
@@ -2269,7 +2575,7 @@ class Net_SFTP extends Net_SSH2
2269
  }
2270
  }
2271
 
2272
- $fclose_check = $local_file !== false && !is_resource($local_file);
2273
 
2274
  $start = $offset;
2275
  $read = 0;
@@ -2290,9 +2596,6 @@ class Net_SFTP extends Net_SSH2
2290
  }
2291
  $packet = null;
2292
  $read+= $packet_size;
2293
- if (is_callable($progressCallback)) {
2294
- call_user_func($progressCallback, $read);
2295
- }
2296
  $i++;
2297
  }
2298
 
@@ -2319,9 +2622,14 @@ class Net_SFTP extends Net_SSH2
2319
  $offset+= strlen($temp);
2320
  if ($local_file === false) {
2321
  $content.= $temp;
 
 
2322
  } else {
2323
  fputs($fp, $temp);
2324
  }
 
 
 
2325
  $temp = null;
2326
  break;
2327
  case NET_SFTP_STATUS:
@@ -2333,7 +2641,14 @@ class Net_SFTP extends Net_SSH2
2333
  if ($fclose_check) {
2334
  fclose($fp);
2335
  }
2336
- user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS');
 
 
 
 
 
 
 
2337
  }
2338
  $response = null;
2339
  }
@@ -2353,6 +2668,11 @@ class Net_SFTP extends Net_SSH2
2353
 
2354
  if ($fclose_check) {
2355
  fclose($fp);
 
 
 
 
 
2356
  }
2357
 
2358
  if (!$this->_close_handle($handle)) {
@@ -2373,7 +2693,7 @@ class Net_SFTP extends Net_SSH2
2373
  */
2374
  function delete($path, $recursive = true)
2375
  {
2376
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
2377
  return false;
2378
  }
2379
 
@@ -2502,6 +2822,10 @@ class Net_SFTP extends Net_SSH2
2502
  function file_exists($path)
2503
  {
2504
  if ($this->use_stat_cache) {
 
 
 
 
2505
  $path = $this->_realpath($path);
2506
 
2507
  $result = $this->_query_stat_cache($path);
@@ -2572,6 +2896,10 @@ class Net_SFTP extends Net_SSH2
2572
  */
2573
  function is_readable($path)
2574
  {
 
 
 
 
2575
  $path = $this->_realpath($path);
2576
 
2577
  $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0);
@@ -2600,6 +2928,10 @@ class Net_SFTP extends Net_SSH2
2600
  */
2601
  function is_writable($path)
2602
  {
 
 
 
 
2603
  $path = $this->_realpath($path);
2604
 
2605
  $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0);
@@ -2774,11 +3106,16 @@ class Net_SFTP extends Net_SSH2
2774
  *
2775
  * @param string $path
2776
  * @param string $prop
 
2777
  * @return mixed
2778
  * @access private
2779
  */
2780
  function _get_xstat_cache_prop($path, $prop, $type)
2781
  {
 
 
 
 
2782
  if ($this->use_stat_cache) {
2783
  $path = $this->_realpath($path);
2784
 
@@ -2799,7 +3136,9 @@ class Net_SFTP extends Net_SSH2
2799
  }
2800
 
2801
  /**
2802
- * Renames a file or a directory on the SFTP server
 
 
2803
  *
2804
  * @param string $oldname
2805
  * @param string $newname
@@ -2808,7 +3147,7 @@ class Net_SFTP extends Net_SSH2
2808
  */
2809
  function rename($oldname, $newname)
2810
  {
2811
- if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
2812
  return false;
2813
  }
2814
 
@@ -2820,6 +3159,18 @@ class Net_SFTP extends Net_SSH2
2820
 
2821
  // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
2822
  $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
 
 
 
 
 
 
 
 
 
 
 
 
2823
  if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
2824
  return false;
2825
  }
@@ -2849,6 +3200,31 @@ class Net_SFTP extends Net_SSH2
2849
  return true;
2850
  }
2851
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2852
  /**
2853
  * Parse Attributes
2854
  *
@@ -2860,16 +3236,56 @@ class Net_SFTP extends Net_SSH2
2860
  */
2861
  function _parseAttributes(&$response)
2862
  {
 
 
 
 
 
 
 
 
2863
  $attr = array();
2864
- if (strlen($response) < 4) {
2865
  user_error('Malformed file attributes');
2866
  return array();
2867
  }
2868
- extract(unpack('Nflags', $this->_string_shift($response, 4)));
2869
- // SFTPv4+ have a type field (a byte) that follows the above flag field
 
 
2870
  foreach ($this->attributes as $key => $value) {
2871
  switch ($flags & $key) {
2872
- case NET_SFTP_ATTR_SIZE: // 0x00000001
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2873
  // The size attribute is defined as an unsigned 64-bit integer.
2874
  // The following will use floats on 32-bit platforms, if necessary.
2875
  // As can be seen in the BigInteger class, floats are generally
@@ -2878,14 +3294,14 @@ class Net_SFTP extends Net_SSH2
2878
  // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
2879
  $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
2880
  break;
2881
- case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
2882
  if (strlen($response) < 8) {
2883
  user_error('Malformed file attributes');
2884
  return $attr;
2885
  }
2886
  $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
2887
  break;
2888
- case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
2889
  if (strlen($response) < 4) {
2890
  user_error('Malformed file attributes');
2891
  return $attr;
@@ -2899,14 +3315,134 @@ class Net_SFTP extends Net_SSH2
2899
  $attr+= array('type' => $fileType);
2900
  }
2901
  break;
2902
- case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
 
 
 
 
2903
  if (strlen($response) < 8) {
2904
  user_error('Malformed file attributes');
2905
  return $attr;
2906
  }
2907
  $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
2908
  break;
2909
- case NET_SFTP_ATTR_EXTENDED: // 0x80000000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2910
  if (strlen($response) < 4) {
2911
  user_error('Malformed file attributes');
2912
  return $attr;
@@ -3014,6 +3550,7 @@ class Net_SFTP extends Net_SSH2
3014
  *
3015
  * @param int $type
3016
  * @param string $data
 
3017
  * @see self::_get_sftp_packet()
3018
  * @see Net_SSH2::_send_channel_packet()
3019
  * @return bool
@@ -3021,6 +3558,10 @@ class Net_SFTP extends Net_SSH2
3021
  */
3022
  function _send_sftp_packet($type, $data, $request_id = 1)
3023
  {
 
 
 
 
3024
  $packet = $this->use_request_id ?
3025
  pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) :
3026
  pack('NCa*', strlen($data) + 1, $type, $data);
@@ -3033,9 +3574,17 @@ class Net_SFTP extends Net_SSH2
3033
  $packet_type = '-> ' . $this->packet_types[$type] .
3034
  ' (' . round($stop - $start, 4) . 's)';
3035
  if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
3036
- echo "<pre>\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n</pre>\r\n";
3037
- flush();
3038
- ob_flush();
 
 
 
 
 
 
 
 
3039
  } else {
3040
  $this->packet_type_log[] = $packet_type;
3041
  if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
@@ -3076,6 +3625,8 @@ class Net_SFTP extends Net_SSH2
3076
  */
3077
  function _get_sftp_packet($request_id = null)
3078
  {
 
 
3079
  if (isset($request_id) && isset($this->requestBuffer[$request_id])) {
3080
  $this->packet_type = $this->requestBuffer[$request_id]['packet_type'];
3081
  $temp = $this->requestBuffer[$request_id]['packet'];
@@ -3092,11 +3643,17 @@ class Net_SFTP extends Net_SSH2
3092
  // SFTP packet length
3093
  while (strlen($this->packet_buffer) < 4) {
3094
  $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL, true);
3095
- if (is_bool($temp)) {
 
 
 
3096
  $this->packet_type = false;
3097
  $this->packet_buffer = '';
3098
  return false;
3099
  }
 
 
 
3100
  $this->packet_buffer.= $temp;
3101
  }
3102
  if (strlen($this->packet_buffer) < 4) {
@@ -3106,9 +3663,8 @@ class Net_SFTP extends Net_SSH2
3106
  $tempLength = $length;
3107
  $tempLength-= strlen($this->packet_buffer);
3108
 
3109
-
3110
  // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h
3111
- if ($tempLength > 256 * 1024) {
3112
  user_error('Invalid SFTP packet size');
3113
  return false;
3114
  }
@@ -3142,9 +3698,17 @@ class Net_SFTP extends Net_SSH2
3142
  $packet_type = '<- ' . $this->packet_types[$this->packet_type] .
3143
  ' (' . round($stop - $start, 4) . 's)';
3144
  if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
3145
- echo "<pre>\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n</pre>\r\n";
3146
- flush();
3147
- ob_flush();
 
 
 
 
 
 
 
 
3148
  } else {
3149
  $this->packet_type_log[] = $packet_type;
3150
  if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
@@ -3218,13 +3782,51 @@ class Net_SFTP extends Net_SSH2
3218
  */
3219
  function getSupportedVersions()
3220
  {
3221
- $temp = array('version' => $this->version);
 
 
 
 
 
 
 
 
3222
  if (isset($this->extensions['versions'])) {
3223
  $temp['extensions'] = $this->extensions['versions'];
3224
  }
3225
  return $temp;
3226
  }
3227
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3228
  /**
3229
  * Disconnect
3230
  *
@@ -3237,4 +3839,24 @@ class Net_SFTP extends Net_SSH2
3237
  $this->pwd = false;
3238
  parent::_disconnect($reason);
3239
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3240
  }
5
  *
6
  * PHP versions 4 and 5
7
  *
8
+ * Supports SFTPv2/3/4/5/6. Defaults to v3.
 
 
9
  *
10
  * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
11
  *
193
  */
194
  var $version;
195
 
196
+ /**
197
+ * Default Server SFTP version
198
+ *
199
+ * @var int
200
+ * @see self::_initChannel()
201
+ * @access private
202
+ */
203
+ var $defaultVersion;
204
+
205
+ /**
206
+ * Preferred SFTP version
207
+ *
208
+ * @var int
209
+ * @see self::_initChannel()
210
+ * @access private
211
+ */
212
+ var $preferredVersion = 3;
213
+
214
  /**
215
  * Current working directory
216
  *
316
  */
317
  var $requestBuffer = array();
318
 
319
+ /**
320
+ * Preserve timestamps on file downloads / uploads
321
+ *
322
+ * @see self::get()
323
+ * @see self::put()
324
+ * @var bool
325
+ * @access private
326
+ */
327
+ var $preserveTime = false;
328
+
329
+ /**
330
+ * Arbitrary Length Packets Flag
331
+ *
332
+ * Determines whether or not packets of any length should be allowed,
333
+ * in cases where the server chooses the packet length (such as
334
+ * directory listings). By default, packets are only allowed to be
335
+ * 256 * 1024 bytes (SFTP_MAX_MSG_LENGTH from OpenSSH's sftp-common.h)
336
+ *
337
+ * @see self::enableArbitraryLengthPackets()
338
+ * @see self::_get_sftp_packet()
339
+ * @var bool
340
+ * @access private
341
+ */
342
+ var $allow_arbitrary_length_packets = false;
343
+
344
+ /**
345
+ * Was the last packet due to the channels being closed or not?
346
+ *
347
+ * @see self::get()
348
+ * @see self::get_sftp_packet()
349
+ * @var bool
350
+ * @access private
351
+ */
352
+ var $channel_close = false;
353
+
354
+ /**
355
+ * Has the SFTP channel been partially negotiated?
356
+ *
357
+ * @var bool
358
+ * @access private
359
+ */
360
+ var $partial_init = false;
361
+
362
  /**
363
  * Default Constructor.
364
  *
379
  $this->packet_types = array(
380
  1 => 'NET_SFTP_INIT',
381
  2 => 'NET_SFTP_VERSION',
 
 
 
382
  3 => 'NET_SFTP_OPEN',
383
  4 => 'NET_SFTP_CLOSE',
384
  5 => 'NET_SFTP_READ',
385
  6 => 'NET_SFTP_WRITE',
386
  7 => 'NET_SFTP_LSTAT',
387
  9 => 'NET_SFTP_SETSTAT',
388
+ 10 => 'NET_SFTP_FSETSTAT',
389
  11 => 'NET_SFTP_OPENDIR',
390
  12 => 'NET_SFTP_READDIR',
391
  13 => 'NET_SFTP_REMOVE',
393
  15 => 'NET_SFTP_RMDIR',
394
  16 => 'NET_SFTP_REALPATH',
395
  17 => 'NET_SFTP_STAT',
 
 
 
396
  18 => 'NET_SFTP_RENAME',
397
  19 => 'NET_SFTP_READLINK',
398
  20 => 'NET_SFTP_SYMLINK',
399
+ 21 => 'NET_SFTP_LINK',
400
 
401
  101=> 'NET_SFTP_STATUS',
402
  102=> 'NET_SFTP_HANDLE',
 
 
 
403
  103=> 'NET_SFTP_DATA',
404
  104=> 'NET_SFTP_NAME',
405
  105=> 'NET_SFTP_ATTRS',
444
  // the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why
445
  $this->attributes = array(
446
  0x00000001 => 'NET_SFTP_ATTR_SIZE',
447
+ 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
448
+ 0x00000080 => 'NET_SFTP_ATTR_OWNERGROUP', // defined in SFTPv4+
449
  0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
450
  0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
451
+ 0x00000010 => 'NET_SFTP_ATTR_CREATETIME', // SFTPv4+
452
+ 0x00000020 => 'NET_SFTP_ATTR_MODIFYTIME',
453
+ 0x00000040 => 'NET_SFTP_ATTR_ACL',
454
+ 0x00000100 => 'NET_SFTP_ATTR_SUBSECOND_TIMES',
455
+ 0x00000200 => 'NET_SFTP_ATTR_BITS', // SFTPv5+
456
+ 0x00000400 => 'NET_SFTP_ATTR_ALLOCATION_SIZE', // SFTPv6+
457
+ 0x00000800 => 'NET_SFTP_ATTR_TEXT_HINT',
458
+ 0x00001000 => 'NET_SFTP_ATTR_MIME_TYPE',
459
+ 0x00002000 => 'NET_SFTP_ATTR_LINK_COUNT',
460
+ 0x00004000 => 'NET_SFTP_ATTR_UNTRANSLATED_NAME',
461
+ 0x00008000 => 'NET_SFTP_ATTR_CTIME',
462
  // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
463
  // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
464
  // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
465
  // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
466
  (-1 << 31) & 0xFFFFFFFF => 'NET_SFTP_ATTR_EXTENDED'
467
  );
 
 
 
468
  $this->open_flags = array(
469
  0x00000001 => 'NET_SFTP_OPEN_READ',
470
  0x00000002 => 'NET_SFTP_OPEN_WRITE',
471
  0x00000004 => 'NET_SFTP_OPEN_APPEND',
472
  0x00000008 => 'NET_SFTP_OPEN_CREATE',
473
  0x00000010 => 'NET_SFTP_OPEN_TRUNCATE',
474
+ 0x00000020 => 'NET_SFTP_OPEN_EXCL',
475
+ 0x00000040 => 'NET_SFTP_OPEN_TEXT' // defined in SFTPv4
476
+ );
477
+ // SFTPv5+ changed the flags up:
478
+ // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-8.1.1.3
479
+ $this->open_flags5 = array(
480
+ // when SSH_FXF_ACCESS_DISPOSITION is a 3 bit field that controls how the file is opened
481
+ 0x00000000 => 'NET_SFTP_OPEN_CREATE_NEW',
482
+ 0x00000001 => 'NET_SFTP_OPEN_CREATE_TRUNCATE',
483
+ 0x00000002 => 'NET_SFTP_OPEN_OPEN_EXISTING',
484
+ 0x00000003 => 'NET_SFTP_OPEN_OPEN_OR_CREATE',
485
+ 0x00000004 => 'NET_SFTP_OPEN_TRUNCATE_EXISTING',
486
+ // the rest of the flags are not supported
487
+ 0x00000008 => 'NET_SFTP_OPEN_APPEND_DATA', // "the offset field of SS_FXP_WRITE requests is ignored"
488
+ 0x00000010 => 'NET_SFTP_OPEN_APPEND_DATA_ATOMIC',
489
+ 0x00000020 => 'NET_SFTP_OPEN_TEXT_MODE',
490
+ 0x00000040 => 'NET_SFTP_OPEN_BLOCK_READ',
491
+ 0x00000080 => 'NET_SFTP_OPEN_BLOCK_WRITE',
492
+ 0x00000100 => 'NET_SFTP_OPEN_BLOCK_DELETE',
493
+ 0x00000200 => 'NET_SFTP_OPEN_BLOCK_ADVISORY',
494
+ 0x00000400 => 'NET_SFTP_OPEN_NOFOLLOW',
495
+ 0x00000800 => 'NET_SFTP_OPEN_DELETE_ON_CLOSE',
496
+ 0x00001000 => 'NET_SFTP_OPEN_ACCESS_AUDIT_ALARM_INFO',
497
+ 0x00002000 => 'NET_SFTP_OPEN_ACCESS_BACKUP',
498
+ 0x00004000 => 'NET_SFTP_OPEN_BACKUP_STREAM',
499
+ 0x00008000 => 'NET_SFTP_OPEN_OVERRIDE_OWNER',
500
  );
501
  // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
502
  // see Net_SFTP::_parseLongname() for an explanation
518
  $this->status_codes,
519
  $this->attributes,
520
  $this->open_flags,
521
+ $this->open_flags5,
522
  $this->file_types
523
  );
524
 
545
  }
546
 
547
  /**
548
+ * Check a few things before SFTP functions are called
549
  *
 
 
550
  * @return bool
551
  * @access public
552
  */
553
+ function _precheck()
554
  {
555
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
 
 
 
 
556
  return false;
557
  }
558
 
559
+ if ($this->pwd === false) {
560
+ return $this->_init_sftp_connection();
561
+ }
562
+
563
+ return true;
564
+ }
565
+
566
+ /**
567
+ * Partially initialize an SFTP connection
568
+ *
569
+ * @return bool
570
+ * @access public
571
+ */
572
+ function _partial_init_sftp_connection()
573
+ {
574
  $this->window_size_server_to_client[NET_SFTP_CHANNEL] = $this->window_size;
575
 
576
  $packet = pack(
592
  $response = $this->_get_channel_packet(NET_SFTP_CHANNEL, true);
593
  if ($response === false) {
594
  return false;
595
+ } elseif ($response === true && $this->isTimeout()) {
596
+ return false;
597
  }
598
 
599
  $packet = pack(
640
  if ($response === false) {
641
  return false;
642
  }
643
+ } elseif ($response === true && $this->isTimeout()) {
644
+ return false;
645
  }
646
 
647
  $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
656
  return false;
657
  }
658
 
659
+ $this->use_request_id = true;
660
+
661
  if (strlen($response) < 4) {
662
  return false;
663
  }
664
  extract(unpack('Nversion', $this->_string_shift($response, 4)));
665
+ $this->defaultVersion = $version;
666
  while (!empty($response)) {
667
  if (strlen($response) < 4) {
668
  return false;
677
  $this->extensions[$key] = $value;
678
  }
679
 
680
+ $this->partial_init = true;
 
 
 
 
 
 
 
 
 
 
 
 
681
 
682
+ return true;
683
+ }
684
+
685
+ /**
686
+ * (Re)initializes the SFTP channel
687
+ *
688
+ * @return bool
689
+ * @access private
690
+ */
691
+ function _init_sftp_connection()
692
+ {
693
+ if (!$this->partial_init && !$this->_partial_init_sftp_connection()) {
694
+ return false;
695
+ }
696
 
697
  /*
698
  A Note on SFTPv4/5/6 support:
717
  in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what Net_SFTP would do is close the
718
  channel and reopen it with a new and updated SSH_FXP_INIT packet.
719
  */
720
+ $this->version = $this->defaultVersion;
721
+ if (isset($this->extensions['versions']) && (!$this->preferredVersion || $this->preferredVersion != $this->version)) {
722
+ $versions = explode(',', $this->extensions['versions']);
723
+ $supported = array(6, 5, 4);
724
+ if ($this->preferredVersion) {
725
+ $supported = array_diff($supported, array($this->preferredVersion));
726
+ array_unshift($supported, $this->preferredVersion);
727
+ }
728
+ foreach ($supported as $ver) {
729
+ if (in_array($ver, $versions)) {
730
+ if ($ver === $this->version) {
731
+ break;
732
+ }
733
+ $this->version = (int) $ver;
734
+ $packet = pack('Na*Na*', strlen('version-select'), 'version-select', strlen($ver), $ver);
735
+ if (!$this->_send_sftp_packet(NET_SFTP_EXTENDED, $packet)) {
736
+ return false;
737
+ }
738
+ $response = $this->_get_sftp_packet();
739
+ if ($this->packet_type != NET_SFTP_STATUS) {
740
+ user_error('Expected SSH_FXP_STATUS');
741
+ return false;
742
+ }
743
+
744
+ if (strlen($response) < 4) {
745
+ return false;
746
+ }
747
+ extract(unpack('Nstatus', $this->_string_shift($response, 4)));
748
+ if ($status != NET_SFTP_STATUS_OK) {
749
+ $this->_logError($response, $status);
750
+ return false;
751
+ }
752
+
753
+ break;
754
+ }
755
+ }
756
+ }
757
+
758
+ /*
759
+ SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
760
+ however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
761
+ not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
762
+ one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
763
+ 'newline@vandyke.com' would.
764
+ */
765
+ /*
766
+ if (isset($this->extensions['newline@vandyke.com'])) {
767
+ $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
768
+ unset($this->extensions['newline@vandyke.com']);
769
+ }
770
+ */
771
+
772
+ if ($this->version < 2 || $this->version > 6) {
773
+ return false;
774
  }
775
 
776
  $this->pwd = $this->_realpath('.');
830
  $this->canonicalize_paths = false;
831
  }
832
 
833
+ /**
834
+ * Enable arbitrary length packets
835
+ *
836
+ * @access public
837
+ */
838
+ function enableArbitraryLengthPackets()
839
+ {
840
+ $this->allow_arbitrary_length_packets = true;
841
+ }
842
+
843
+ /**
844
+ * Disable arbitrary length packets
845
+ *
846
+ * @access public
847
+ */
848
+ function disableArbitraryLengthPackets()
849
+ {
850
+ $this->allow_arbitrary_length_packets = false;
851
+ }
852
+
853
  /**
854
  * Returns the current directory name
855
  *
858
  */
859
  function pwd()
860
  {
861
+ if (!$this->_precheck()) {
862
+ return false;
863
+ }
864
+
865
  return $this->pwd;
866
  }
867
 
903
  */
904
  function realpath($path)
905
  {
906
+ if (!$this->_precheck()) {
907
+ return false;
908
+ }
909
+
910
  return $this->_realpath($path);
911
  }
912
 
989
  */
990
  function chdir($dir)
991
  {
992
+ if (!$this->_precheck()) {
993
  return false;
994
  }
995
 
1146
  */
1147
  function _list($dir, $raw = true)
1148
  {
1149
+ if (!$this->_precheck()) {
1150
  return false;
1151
  }
1152
 
1201
  }
1202
  extract(unpack('Nlength', $this->_string_shift($response, 4)));
1203
  $shortname = $this->_string_shift($response, $length);
1204
+ // SFTPv4 "removed the long filename from the names structure-- it can now be
1205
+ // built from information available in the attrs structure."
1206
+ if ($this->version < 4) {
1207
+ if (strlen($response) < 4) {
1208
+ return false;
1209
+ }
1210
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
1211
+ $longname = $this->_string_shift($response, $length);
1212
  }
 
 
1213
  $attributes = $this->_parseAttributes($response);
1214
+ if (!isset($attributes['type']) && $this->version < 4) {
1215
  $fileType = $this->_parseLongname($longname);
1216
  if ($fileType) {
1217
  $attributes['type'] = $fileType;
1257
  uasort($contents, array(&$this, '_comparator'));
1258
  }
1259
 
1260
+ return $raw ? $contents : array_map('strval', array_keys($contents));
1261
  }
1262
 
1263
  /**
1371
  */
1372
  function size($filename)
1373
  {
 
 
 
 
1374
  $result = $this->stat($filename);
1375
  if ($result === false) {
1376
  return false;
1455
  *
1456
  * Mainly used by file_exists
1457
  *
1458
+ * @param string $path
1459
  * @return mixed
1460
  * @access private
1461
  */
1487
  */
1488
  function stat($filename)
1489
  {
1490
+ if (!$this->_precheck()) {
1491
  return false;
1492
  }
1493
 
1544
  */
1545
  function lstat($filename)
1546
  {
1547
+ if (!$this->_precheck()) {
1548
  return false;
1549
  }
1550
 
1658
  */
1659
  function touch($filename, $time = null, $atime = null)
1660
  {
1661
+ if (!$this->_precheck()) {
1662
  return false;
1663
  }
1664
 
1674
  $atime = $time;
1675
  }
1676
 
1677
+ if ($this->version < 4) {
1678
+ $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $atime, $time);
1679
+ } else {
1680
+ $attr = pack(
1681
+ 'N5',
1682
+ NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME,
1683
+ $atime / 4294967296,
1684
+ $atime,
1685
+ $time / 4294967296,
1686
+ $time
1687
+ );
1688
+ }
1689
+
1690
+ $packet = pack('Na*', strlen($filename), $filename);
1691
+ $packet.= $this->version >= 5 ?
1692
+ pack('N2', 0, NET_SFTP_OPEN_OPEN_EXISTING) :
1693
+ pack('N', NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL);
1694
+ $packet.= $attr;
1695
+
1696
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
1697
  return false;
1698
  }
1715
  /**
1716
  * Changes file or directory owner
1717
  *
1718
+ * $uid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string
1719
+ * would be of the form "user@dns_domain" but it does not need to be.
1720
+ * `$sftp->getSupportedVersions()['version']` will return the specific version
1721
+ * that's being used.
1722
+ *
1723
  * Returns true on success or false on error.
1724
  *
1725
  * @param string $filename
1726
+ * @param int|string $uid
1727
  * @param bool $recursive
1728
  * @return bool
1729
  * @access public
1730
  */
1731
  function chown($filename, $uid, $recursive = false)
1732
  {
1733
+ /*
1734
+ quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
1735
+
1736
+ "To avoid a representation that is tied to a particular underlying
1737
+ implementation at the client or server, the use of UTF-8 strings has
1738
+ been chosen. The string should be of the form "user@dns_domain".
1739
+ This will allow for a client and server that do not use the same
1740
+ local representation the ability to translate to a common syntax that
1741
+ can be interpreted by both. In the case where there is no
1742
+ translation available to the client or server, the attribute value
1743
+ must be constructed without the "@"."
1744
+
1745
+ phpseclib _could_ auto append the dns_domain to $uid BUT what if it shouldn't
1746
+ have one? phpseclib would have no way of knowing so rather than guess phpseclib
1747
+ will just use whatever value the user provided
1748
+ */
1749
+
1750
+ $attr = $this->version < 4 ?
1751
+ // quoting <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
1752
+ // "if the owner or group is specified as -1, then that ID is not changed"
1753
+ pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1) :
1754
+ // quoting <https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.5>,
1755
+ // "If either the owner or group field is zero length, the field should be
1756
+ // considered absent, and no change should be made to that specific field
1757
+ // during a modification operation"
1758
+ pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, strlen($uid), $uid, 0, '');
1759
 
1760
  return $this->_setstat($filename, $attr, $recursive);
1761
  }
1763
  /**
1764
  * Changes file or directory group
1765
  *
1766
+ * $gid should be an int for SFTPv3 and a string for SFTPv4+. Ideally the string
1767
+ * would be of the form "user@dns_domain" but it does not need to be.
1768
+ * `$sftp->getSupportedVersions()['version']` will return the specific version
1769
+ * that's being used.
1770
+ *
1771
  * Returns true on success or false on error.
1772
  *
1773
  * @param string $filename
1774
+ * @param int|string $gid
1775
  * @param bool $recursive
1776
  * @return bool
1777
  * @access public
1778
  */
1779
  function chgrp($filename, $gid, $recursive = false)
1780
  {
1781
+ $attr = $this->version < 4 ?
1782
+ pack('N3', NET_SFTP_ATTR_UIDGID, $gid, -1) :
1783
+ pack('NNa*Na*', NET_SFTP_ATTR_OWNERGROUP, 0, '', strlen($gid), $gid);
1784
 
1785
  return $this->_setstat($filename, $attr, $recursive);
1786
  }
1847
  */
1848
  function _setstat($filename, $attr, $recursive)
1849
  {
1850
+ if (!$this->_precheck()) {
1851
  return false;
1852
  }
1853
 
1865
  return $result;
1866
  }
1867
 
1868
+ $packet = $this->version >= 4 ?
1869
+ pack('Na*a*Ca*', strlen($filename), $filename, substr($attr, 0, 4), NET_SFTP_TYPE_UNKNOWN, substr($attr, 4)) :
1870
+ pack('Na*a*', strlen($filename), $filename, $attr);
1871
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
1872
  return false;
1873
  }
1874
 
1938
  return false;
1939
  }
1940
  } else {
1941
+ $packet = $this->version >= 4 ?
1942
+ pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) :
1943
+ pack('Na*a*', strlen($temp), $temp, $attr);
1944
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
1945
  return false;
1946
  }
1947
 
1956
  }
1957
  }
1958
 
1959
+ $packet = $this->version >= 4 ?
1960
+ pack('Na*Ca*', strlen($temp), $temp, NET_SFTP_TYPE_UNKNOWN, $attr) :
1961
+ pack('Na*a*', strlen($temp), $temp, $attr);
1962
+ if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, $packet)) {
1963
  return false;
1964
  }
1965
 
1984
  */
1985
  function readlink($link)
1986
  {
1987
+ if (!$this->_precheck()) {
1988
  return false;
1989
  }
1990
 
2034
  */
2035
  function symlink($target, $link)
2036
  {
2037
+ if (!$this->_precheck()) {
2038
  return false;
2039
  }
2040
 
2041
  //$target = $this->_realpath($target);
2042
  $link = $this->_realpath($link);
2043
 
2044
+ /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-09#section-12.1 :
2045
+
2046
+ Changed the SYMLINK packet to be LINK and give it the ability to
2047
+ create hard links. Also change it's packet number because many
2048
+ implementation implemented SYMLINK with the arguments reversed.
2049
+ Hopefully the new argument names make it clear which way is which.
2050
+ */
2051
+ if ($this->version == 6) {
2052
+ $type = NET_SFTP_LINK;
2053
+ $packet = pack('Na*Na*C', strlen($link), $link, strlen($target), $target, 1);
2054
+ } else {
2055
+ $type = NET_SFTP_SYMLINK;
2056
+ /* quoting http://bxr.su/OpenBSD/usr.bin/ssh/PROTOCOL#347 :
2057
+
2058
+ 3.1. sftp: Reversal of arguments to SSH_FXP_SYMLINK
2059
+
2060
+ When OpenSSH's sftp-server was implemented, the order of the arguments
2061
+ to the SSH_FXP_SYMLINK method was inadvertently reversed. Unfortunately,
2062
+ the reversal was not noticed until the server was widely deployed. Since
2063
+ fixing this to follow the specification would cause incompatibility, the
2064
+ current order was retained. For correct operation, clients should send
2065
+ SSH_FXP_SYMLINK as follows:
2066
+
2067
+ uint32 id
2068
+ string targetpath
2069
+ string linkpath */
2070
+ $packet = substr($this->server_identifier, 0, 15) == 'SSH-2.0-OpenSSH' ?
2071
+ pack('Na*Na*', strlen($target), $target, strlen($link), $link) :
2072
+ pack('Na*Na*', strlen($link), $link, strlen($target), $target);
2073
+ }
2074
+ if (!$this->_send_sftp_packet($type, $packet)) {
2075
  return false;
2076
  }
2077
 
2097
  * Creates a directory.
2098
  *
2099
  * @param string $dir
2100
+ * @param int $mode
2101
+ * @param bool $recursive
2102
  * @return bool
2103
  * @access public
2104
  */
2105
  function mkdir($dir, $mode = -1, $recursive = false)
2106
  {
2107
+ if (!$this->_precheck()) {
2108
  return false;
2109
  }
2110
 
2131
  * Helper function for directory creation
2132
  *
2133
  * @param string $dir
2134
+ * @param int $mode
2135
  * @return bool
2136
  * @access private
2137
  */
2173
  */
2174
  function rmdir($dir)
2175
  {
2176
+ if (!$this->_precheck()) {
2177
  return false;
2178
  }
2179
 
2224
  *
2225
  * If $data is a resource then it'll be used as a resource instead.
2226
  *
 
2227
  * Setting $mode to NET_SFTP_CALLBACK will use $data as callback function, which gets only one parameter -- number
2228
  * of bytes to return, and returns a string if there is some data or null if there is no more data
2229
  *
2259
  */
2260
  function put($remote_file, $data, $mode = NET_SFTP_STRING, $start = -1, $local_start = -1, $progressCallback = null)
2261
  {
2262
+ if (!$this->_precheck()) {
2263
  return false;
2264
  }
2265
 
2270
 
2271
  $this->_remove_from_stat_cache($remote_file);
2272
 
2273
+ if ($this->version >= 5) {
2274
+ $flags = NET_SFTP_OPEN_OPEN_OR_CREATE;
2275
+ } else {
2276
+ $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
2277
+ // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
2278
+ // in practice, it doesn't seem to do that.
2279
+ //$flags|= ($mode & NET_SFTP_RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
2280
+ }
2281
 
2282
  if ($start >= 0) {
2283
  $offset = $start;
2287
  $offset = $size !== false ? $size : 0;
2288
  } else {
2289
  $offset = 0;
2290
+ if ($this->version >= 5) {
2291
+ $flags = NET_SFTP_OPEN_CREATE_TRUNCATE;
2292
+ } else {
2293
+ $flags|= NET_SFTP_OPEN_TRUNCATE;
2294
+ }
2295
  }
2296
 
2297
+ $packet = pack('Na*', strlen($remote_file), $remote_file);
2298
+ $packet.= $this->version >= 5 ?
2299
+ pack('N3', 0, $flags, 0) :
2300
+ pack('N2', $flags, 0);
2301
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
2302
  return false;
2303
  }
2364
  $sent = 0;
2365
  $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
2366
 
2367
+ $sftp_packet_size = $this->max_sftp_packet;
2368
+ // make the SFTP packet be exactly the SFTP packet size by including the bytes in the NET_SFTP_WRITE packets "header"
2369
  $sftp_packet_size-= strlen($handle) + 25;
2370
  $i = $j = 0;
2371
  while ($dataCallback || ($size === 0 || $sent < $size)) {
2406
  }
2407
  }
2408
 
2409
+ $result = $this->_close_handle($handle);
2410
+
2411
  if (!$this->_read_put_responses($i)) {
2412
  if ($mode & NET_SFTP_LOCAL_FILE) {
2413
  fclose($fp);
2417
  }
2418
 
2419
  if ($mode & NET_SFTP_LOCAL_FILE) {
2420
+ if (isset($fp) && is_resource($fp)) {
2421
+ fclose($fp);
2422
+ }
2423
+
2424
+ if ($this->preserveTime) {
2425
+ $stat = stat($data);
2426
+ if ($this->version < 4) {
2427
+ $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $stat['atime'], $stat['mtime']);
2428
+ } else {
2429
+ $attr = pack(
2430
+ 'N5',
2431
+ NET_SFTP_ATTR_ACCESSTIME | NET_SFTP_ATTR_MODIFYTIME,
2432
+ $stat['atime'] / 4294967296,
2433
+ $stat['atime'],
2434
+ $stat['mtime'] / 4294967296,
2435
+ $stat['mtime']
2436
+ );
2437
+ }
2438
+
2439
+ if (!$this->_setstat($remote_file, $attr, false)) {
2440
+ user_error('Error setting file time');
2441
+ }
2442
+ }
2443
  }
2444
 
2445
+ return $result;
2446
  }
2447
 
2448
  /**
2529
  */
2530
  function get($remote_file, $local_file = false, $offset = 0, $length = -1, $progressCallback = null)
2531
  {
2532
+ if (!$this->_precheck()) {
2533
  return false;
2534
  }
2535
 
2538
  return false;
2539
  }
2540
 
2541
+ $packet = pack('Na*', strlen($remote_file), $remote_file);
2542
+ $packet.= $this->version >= 5 ?
2543
+ pack('N3', 0, NET_SFTP_OPEN_OPEN_EXISTING, 0) :
2544
+ pack('N2', NET_SFTP_OPEN_READ, 0);
2545
  if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
2546
  return false;
2547
  }
2565
  $res_offset = $stat['size'];
2566
  } else {
2567
  $res_offset = 0;
2568
+ if ($local_file !== false && !is_callable($local_file)) {
2569
  $fp = fopen($local_file, 'wb');
2570
  if (!$fp) {
2571
  return false;
2575
  }
2576
  }
2577
 
2578
+ $fclose_check = $local_file !== false && !is_callable($local_file) && !is_resource($local_file);
2579
 
2580
  $start = $offset;
2581
  $read = 0;
2596
  }
2597
  $packet = null;
2598
  $read+= $packet_size;
 
 
 
2599
  $i++;
2600
  }
2601
 
2622
  $offset+= strlen($temp);
2623
  if ($local_file === false) {
2624
  $content.= $temp;
2625
+ } elseif (is_callable($local_file)) {
2626
+ $local_file($temp);
2627
  } else {
2628
  fputs($fp, $temp);
2629
  }
2630
+ if (is_callable($progressCallback)) {
2631
+ call_user_func($progressCallback, $offset);
2632
+ }
2633
  $temp = null;
2634
  break;
2635
  case NET_SFTP_STATUS:
2641
  if ($fclose_check) {
2642
  fclose($fp);
2643
  }
2644
+ // maybe the file was successfully transferred, maybe it wasn't
2645
+ if ($this->channel_close) {
2646
+ $this->partial_init = false;
2647
+ $this->_init_sftp_connection();
2648
+ return false;
2649
+ } else {
2650
+ user_error('Expected SSH_FX_DATA or SSH_FXP_STATUS');
2651
+ }
2652
  }
2653
  $response = null;
2654
  }
2668
 
2669
  if ($fclose_check) {
2670
  fclose($fp);
2671
+
2672
+ if ($this->preserveTime) {
2673
+ $stat = $this->stat($remote_file);
2674
+ touch($local_file, $stat['mtime'], $stat['atime']);
2675
+ }
2676
  }
2677
 
2678
  if (!$this->_close_handle($handle)) {
2693
  */
2694
  function delete($path, $recursive = true)
2695
  {
2696
+ if (!$this->_precheck()) {
2697
  return false;
2698
  }
2699
 
2822
  function file_exists($path)
2823
  {
2824
  if ($this->use_stat_cache) {
2825
+ if (!$this->_precheck()) {
2826
+ return false;
2827
+ }
2828
+
2829
  $path = $this->_realpath($path);
2830
 
2831
  $result = $this->_query_stat_cache($path);
2896
  */
2897
  function is_readable($path)
2898
  {
2899
+ if (!$this->_precheck()) {
2900
+ return false;
2901
+ }
2902
+
2903
  $path = $this->_realpath($path);
2904
 
2905
  $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_READ, 0);
2928
  */
2929
  function is_writable($path)
2930
  {
2931
+ if (!$this->_precheck()) {
2932
+ return false;
2933
+ }
2934
+
2935
  $path = $this->_realpath($path);
2936
 
2937
  $packet = pack('Na*N2', strlen($path), $path, NET_SFTP_OPEN_WRITE, 0);
3106
  *
3107
  * @param string $path
3108
  * @param string $prop
3109
+ * @param mixed $type
3110
  * @return mixed
3111
  * @access private
3112
  */
3113
  function _get_xstat_cache_prop($path, $prop, $type)
3114
  {
3115
+ if (!$this->_precheck()) {
3116
+ return false;
3117
+ }
3118
+
3119
  if ($this->use_stat_cache) {
3120
  $path = $this->_realpath($path);
3121
 
3136
  }
3137
 
3138
  /**
3139
+ * Renames a file or a directory on the SFTP server.
3140
+ *
3141
+ * If the file already exists this will return false
3142
  *
3143
  * @param string $oldname
3144
  * @param string $newname
3147
  */
3148
  function rename($oldname, $newname)
3149
  {
3150
+ if (!$this->_precheck()) {
3151
  return false;
3152
  }
3153
 
3159
 
3160
  // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
3161
  $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
3162
+ if ($this->version >= 5) {
3163
+ /* quoting https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-6.5 ,
3164
+
3165
+ 'flags' is 0 or a combination of:
3166
+
3167
+ SSH_FXP_RENAME_OVERWRITE 0x00000001
3168
+ SSH_FXP_RENAME_ATOMIC 0x00000002
3169
+ SSH_FXP_RENAME_NATIVE 0x00000004
3170
+
3171
+ (none of these are currently supported) */
3172
+ $packet.= "\0\0\0\0";
3173
+ }
3174
  if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
3175
  return false;
3176
  }
3200
  return true;
3201
  }
3202
 
3203
+ /**
3204
+ * Parse Time
3205
+ *
3206
+ * See '7.7. Times' of draft-ietf-secsh-filexfer-13 for more info.
3207
+ *
3208
+ * @param string $key
3209
+ * @param int $flags
3210
+ * @param string $response
3211
+ * @return array
3212
+ * @access private
3213
+ */
3214
+ function _parseTime($key, $flags, &$response)
3215
+ {
3216
+ if (strlen($response) < 8) {
3217
+ user_error('Malformed file attributes');
3218
+ return array();
3219
+ }
3220
+ $attr = array();
3221
+ $attr[$key] = hexdec(bin2hex($this->_string_shift($response, 8)));
3222
+ if ($flags & NET_SFTP_ATTR_SUBSECOND_TIMES) {
3223
+ $attr+= extract(unpack('N' . $key . '_nseconds', $this->_string_shift($response, 4)));
3224
+ }
3225
+ return $attr;
3226
+ }
3227
+
3228
  /**
3229
  * Parse Attributes
3230
  *
3236
  */
3237
  function _parseAttributes(&$response)
3238
  {
3239
+ if ($this->version >= 4) {
3240
+ $length = 5;
3241
+ $format = 'Nflags/Ctype';
3242
+ } else {
3243
+ $length = 4;
3244
+ $format = 'Nflags';
3245
+ }
3246
+
3247
  $attr = array();
3248
+ if (strlen($response) < $length) {
3249
  user_error('Malformed file attributes');
3250
  return array();
3251
  }
3252
+ extract(unpack($format, $this->_string_shift($response, $length)));
3253
+ if (isset($type)) {
3254
+ $attr['type'] = $type;
3255
+ }
3256
  foreach ($this->attributes as $key => $value) {
3257
  switch ($flags & $key) {
3258
+ case NET_SFTP_ATTR_UIDGID:
3259
+ if ($this->version > 3) {
3260
+ continue 2;
3261
+ }
3262
+ break;
3263
+ case NET_SFTP_ATTR_CREATETIME:
3264
+ case NET_SFTP_ATTR_MODIFYTIME:
3265
+ case NET_SFTP_ATTR_ACL:
3266
+ case NET_SFTP_ATTR_OWNERGROUP:
3267
+ case NET_SFTP_ATTR_SUBSECOND_TIMES:
3268
+ if ($this->version < 4) {
3269
+ continue 2;
3270
+ }
3271
+ break;
3272
+ case NET_SFTP_ATTR_BITS:
3273
+ if ($this->version < 5) {
3274
+ continue 2;
3275
+ }
3276
+ break;
3277
+ case NET_SFTP_ATTR_ALLOCATION_SIZE:
3278
+ case NET_SFTP_ATTR_TEXT_HINT:
3279
+ case NET_SFTP_ATTR_MIME_TYPE:
3280
+ case NET_SFTP_ATTR_LINK_COUNT:
3281
+ case NET_SFTP_ATTR_UNTRANSLATED_NAME:
3282
+ case NET_SFTP_ATTR_CTIME:
3283
+ if ($this->version < 6) {
3284
+ continue 2;
3285
+ }
3286
+ }
3287
+ switch ($flags & $key) {
3288
+ case NET_SFTP_ATTR_SIZE: // 0x00000001
3289
  // The size attribute is defined as an unsigned 64-bit integer.
3290
  // The following will use floats on 32-bit platforms, if necessary.
3291
  // As can be seen in the BigInteger class, floats are generally
3294
  // of precision. Interpreted in filesize, 2^50 bytes = 1024 TiB.
3295
  $attr['size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
3296
  break;
3297
+ case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 or earlier)
3298
  if (strlen($response) < 8) {
3299
  user_error('Malformed file attributes');
3300
  return $attr;
3301
  }
3302
  $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
3303
  break;
3304
+ case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
3305
  if (strlen($response) < 4) {
3306
  user_error('Malformed file attributes');
3307
  return $attr;
3315
  $attr+= array('type' => $fileType);
3316
  }
3317
  break;
3318
+ case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
3319
+ if ($this->version >= 4) {
3320
+ $attr+= $this->_parseTime('atime', $flags, $response);
3321
+ break;
3322
+ }
3323
  if (strlen($response) < 8) {
3324
  user_error('Malformed file attributes');
3325
  return $attr;
3326
  }
3327
  $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8));
3328
  break;
3329
+ case NET_SFTP_ATTR_CREATETIME: // 0x00000010 (SFTPv4+)
3330
+ $attr+= $this->_parseTime('createtime', $flags, $response);
3331
+ break;
3332
+ case NET_SFTP_ATTR_MODIFYTIME: // 0x00000020
3333
+ $attr+= $this->_parseTime('mtime', $flags, $response);
3334
+ break;
3335
+ case NET_SFTP_ATTR_ACL: // 0x00000040
3336
+ // access control list
3337
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-04#section-5.7
3338
+ // currently unsupported
3339
+ if (strlen($response) < 4) {
3340
+ user_error('Malformed file attributes');
3341
+ return $attr;
3342
+ }
3343
+ extract(unpack('Ncount', $this->_string_shift($response, 4)));
3344
+ for ($i = 0; $i < $count; $i++) {
3345
+ if (strlen($response) < 16) {
3346
+ user_error('Malformed file attributes');
3347
+ return $attr;
3348
+ }
3349
+ extract(unpack('Ntype/Nflag/Nmask/Nlength', $this->_string_shift($response, 16)));
3350
+ if (strlen($response) < $length) {
3351
+ user_error('Malformed file attributes');
3352
+ return $attr;
3353
+ }
3354
+ $this->_string_shift($response, $length); // who
3355
+ }
3356
+ break;
3357
+ case NET_SFTP_ATTR_OWNERGROUP: // 0x00000080
3358
+ if (strlen($response) < 4) {
3359
+ user_error('Malformed file attributes');
3360
+ return $attr;
3361
+ }
3362
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
3363
+ if (strlen($response) < $length) {
3364
+ user_error('Malformed file attributes');
3365
+ return $attr;
3366
+ }
3367
+ $attr['owner'] = $this->_string_shift($response, $length);
3368
+
3369
+ if (strlen($response) < 4) {
3370
+ user_error('Malformed file attributes');
3371
+ return $attr;
3372
+ }
3373
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
3374
+ if (strlen($response) < $length) {
3375
+ user_error('Malformed file attributes');
3376
+ return $attr;
3377
+ }
3378
+ $attr['group'] = $this->_string_shift($response, $length);
3379
+ break;
3380
+ case NET_SFTP_ATTR_SUBSECOND_TIMES: // 0x00000100
3381
+ break;
3382
+ case NET_SFTP_ATTR_BITS: // 0x00000200 (SFTPv5+)
3383
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-05#section-5.8
3384
+ // currently unsupported
3385
+ // tells if you file is:
3386
+ // readonly, system, hidden, case inensitive, archive, encrypted, compressed, sparse
3387
+ // append only, immutable, sync
3388
+ if (strlen($response) < 8) {
3389
+ user_error('Malformed file attributes');
3390
+ return $attr;
3391
+ }
3392
+ extract(unpack('Nattrib-bits/Nattrib-bits-valid', $this->_string_shift($response, 8)));
3393
+ break;
3394
+ case NET_SFTP_ATTR_ALLOCATION_SIZE: // 0x00000400 (SFTPv6+)
3395
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.4
3396
+ // represents the number of bytes htat the file consumes on the disk. will
3397
+ // usually be larger than the 'size' field
3398
+ $attr['allocation-size'] = hexdec(bin2hex($this->_string_shift($response, 8)));
3399
+ break;
3400
+ case NET_SFTP_ATTR_TEXT_HINT: // 0x00000800
3401
+ // https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.10
3402
+ // currently unsupported
3403
+ // tells if file is "known text", "guessed text", "known binary", "guessed binary"
3404
+ extract(unpack('Ctext-hint', $this->_string_shift($response)));
3405
+ break;
3406
+ case NET_SFTP_ATTR_MIME_TYPE: // 0x00001000
3407
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.11
3408
+ if (strlen($response) < 4) {
3409
+ user_error('Malformed file attributes');
3410
+ return $attr;
3411
+ }
3412
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
3413
+ if (strlen($response) < $length) {
3414
+ user_error('Malformed file attributes');
3415
+ return $attr;
3416
+ }
3417
+ $attr['mime-type'] = $this->_string_shift($response, $length);
3418
+ break;
3419
+ case NET_SFTP_ATTR_LINK_COUNT: // 0x00002000
3420
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.12
3421
+ if (strlen($response) < 4) {
3422
+ user_error('Malformed file attributes');
3423
+ return $attr;
3424
+ }
3425
+ $attr+= unpack('Nlink-count', $this->_string_shift($response, 4));
3426
+ break;
3427
+ case NET_SFTP_ATTR_UNTRANSLATED_NAME:// 0x00004000
3428
+ // see https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-13#section-7.13
3429
+ if (strlen($response) < 4) {
3430
+ user_error('Malformed file attributes');
3431
+ return $attr;
3432
+ }
3433
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
3434
+ if (strlen($response) < $length) {
3435
+ user_error('Malformed file attributes');
3436
+ return $attr;
3437
+ }
3438
+ $attr['untranslated-name'] = $this->_string_shift($response, $length);
3439
+ break;
3440
+ case NET_SFTP_ATTR_CTIME: // 0x00008000
3441
+ // 'ctime' contains the last time the file attributes were changed. The
3442
+ // exact meaning of this field depends on the server.
3443
+ $attr+= $this->_parseTime('ctime', $flags, $response);
3444
+ break;
3445
+ case NET_SFTP_ATTR_EXTENDED: // 0x80000000
3446
  if (strlen($response) < 4) {
3447
  user_error('Malformed file attributes');
3448
  return $attr;
3550
  *
3551
  * @param int $type
3552
  * @param string $data
3553
+ * @param int $request_id
3554
  * @see self::_get_sftp_packet()
3555
  * @see Net_SSH2::_send_channel_packet()
3556
  * @return bool
3558
  */
3559
  function _send_sftp_packet($type, $data, $request_id = 1)
3560
  {
3561
+ // in SSH2.php the timeout is cumulative per function call. eg. exec() will
3562
+ // timeout after 10s. but for SFTP.php it's cumulative per packet
3563
+ $this->curTimeout = $this->timeout;
3564
+
3565
  $packet = $this->use_request_id ?
3566
  pack('NCNa*', strlen($data) + 5, $type, $request_id, $data) :
3567
  pack('NCa*', strlen($data) + 1, $type, $data);
3574
  $packet_type = '-> ' . $this->packet_types[$type] .
3575
  ' (' . round($stop - $start, 4) . 's)';
3576
  if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
3577
+ switch (PHP_SAPI) {
3578
+ case 'cli':
3579
+ $start = $stop = "\r\n";
3580
+ break;
3581
+ default:
3582
+ $start = '<pre>';
3583
+ $stop = '</pre>';
3584
+ }
3585
+ echo $start . $this->_format_log(array($data), array($packet_type)) . $stop;
3586
+ @flush();
3587
+ @ob_flush();
3588
  } else {
3589
  $this->packet_type_log[] = $packet_type;
3590
  if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
3625
  */
3626
  function _get_sftp_packet($request_id = null)
3627
  {
3628
+ $this->channel_close = false;
3629
+
3630
  if (isset($request_id) && isset($this->requestBuffer[$request_id])) {
3631
  $this->packet_type = $this->requestBuffer[$request_id]['packet_type'];
3632
  $temp = $this->requestBuffer[$request_id]['packet'];
3643
  // SFTP packet length
3644
  while (strlen($this->packet_buffer) < 4) {
3645
  $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL, true);
3646
+ if ($temp === true) {
3647
+ if ($this->channel_status[NET_SFTP_CHANNEL] === NET_SSH2_MSG_CHANNEL_CLOSE) {
3648
+ $this->channel_close = true;
3649
+ }
3650
  $this->packet_type = false;
3651
  $this->packet_buffer = '';
3652
  return false;
3653
  }
3654
+ if ($temp === false) {
3655
+ return false;
3656
+ }
3657
  $this->packet_buffer.= $temp;
3658
  }
3659
  if (strlen($this->packet_buffer) < 4) {
3663
  $tempLength = $length;
3664
  $tempLength-= strlen($this->packet_buffer);
3665
 
 
3666
  // 256 * 1024 is what SFTP_MAX_MSG_LENGTH is set to in OpenSSH's sftp-common.h
3667
+ if (!$this->allow_arbitrary_length_packets && !$this->use_request_id && $tempLength > 256 * 1024) {
3668
  user_error('Invalid SFTP packet size');
3669
  return false;
3670
  }
3698
  $packet_type = '<- ' . $this->packet_types[$this->packet_type] .
3699
  ' (' . round($stop - $start, 4) . 's)';
3700
  if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) {
3701
+ switch (PHP_SAPI) {
3702
+ case 'cli':
3703
+ $start = $stop = "\r\n";
3704
+ break;
3705
+ default:
3706
+ $start = '<pre>';
3707
+ $stop = '</pre>';
3708
+ }
3709
+ echo $start . $this->_format_log(array($packet), array($packet_type)) . $stop;
3710
+ @flush();
3711
+ @ob_flush();
3712
  } else {
3713
  $this->packet_type_log[] = $packet_type;
3714
  if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) {
3782
  */
3783
  function getSupportedVersions()
3784
  {
3785
+ if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
3786
+ return false;
3787
+ }
3788
+
3789
+ if (!$this->partial_init) {
3790
+ $this->_partial_init_sftp_connection();
3791
+ }
3792
+
3793
+ $temp = array('version' => $this->defaultVersion);
3794
  if (isset($this->extensions['versions'])) {
3795
  $temp['extensions'] = $this->extensions['versions'];
3796
  }
3797
  return $temp;
3798
  }
3799
 
3800
+ /**
3801
+ * Get supported SFTP versions
3802
+ *
3803
+ * @return array
3804
+ * @access public
3805
+ */
3806
+ function getNegotiatedVersion()
3807
+ {
3808
+ if (!$this->_precheck()) {
3809
+ return false;
3810
+ }
3811
+
3812
+ return $this->version;
3813
+ }
3814
+
3815
+ /**
3816
+ * Set preferred version
3817
+ *
3818
+ * If you're preferred version isn't supported then the highest supported
3819
+ * version of SFTP will be utilized. Set to null or false or int(0) to
3820
+ * unset the preferred version
3821
+ *
3822
+ * @param int $version
3823
+ * @access public
3824
+ */
3825
+ function setPreferredVersion($version)
3826
+ {
3827
+ $this->preferredVersion = $version;
3828
+ }
3829
+
3830
  /**
3831
  * Disconnect
3832
  *
3839
  $this->pwd = false;
3840
  parent::_disconnect($reason);
3841
  }
3842
+
3843
+ /**
3844
+ * Enable Date Preservation
3845
+ *
3846
+ * @access public
3847
+ */
3848
+ function enableDatePreservation()
3849
+ {
3850
+ $this->preserveTime = true;
3851
+ }
3852
+
3853
+ /**
3854
+ * Disable Date Preservation
3855
+ *
3856
+ * @access public
3857
+ */
3858
+ function disableDatePreservation()
3859
+ {
3860
+ $this->preserveTime = false;
3861
+ }
3862
  }
phpseclib/Net/SFTP/Stream.php CHANGED
@@ -428,7 +428,7 @@ class Net_SFTP_Stream
428
  {
429
  switch ($whence) {
430
  case SEEK_SET:
431
- if ($offset >= $this->size || $offset < 0) {
432
  return false;
433
  }
434
  break;
@@ -465,7 +465,9 @@ class Net_SFTP_Stream
465
  // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592
466
  switch ($option) {
467
  case 1: // PHP_STREAM_META_TOUCH
468
- return $this->sftp->touch($path, $var[0], $var[1]);
 
 
469
  case 2: // PHP_STREAM_OWNER_NAME
470
  case 3: // PHP_STREAM_GROUP_NAME
471
  return false;
@@ -644,7 +646,6 @@ class Net_SFTP_Stream
644
  * $options. What does 8 correspond to?
645
  *
646
  * @param string $path
647
- * @param int $mode
648
  * @param int $options
649
  * @return bool
650
  * @access public
@@ -786,8 +787,8 @@ class Net_SFTP_Stream
786
  * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not
787
  * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method.
788
  *
789
- * @param string
790
- * @param array
791
  * @return mixed
792
  * @access public
793
  */
428
  {
429
  switch ($whence) {
430
  case SEEK_SET:
431
+ if ($offset < 0) {
432
  return false;
433
  }
434
  break;
465
  // and https://github.com/php/php-src/blob/master/main/php_streams.h#L592
466
  switch ($option) {
467
  case 1: // PHP_STREAM_META_TOUCH
468
+ $time = isset($var[0]) ? $var[0] : null;
469
+ $atime = isset($var[1]) ? $var[1] : null;
470
+ return $this->sftp->touch($path, $time, $atime);
471
  case 2: // PHP_STREAM_OWNER_NAME
472
  case 3: // PHP_STREAM_GROUP_NAME
473
  return false;
646
  * $options. What does 8 correspond to?
647
  *
648
  * @param string $path
 
649
  * @param int $options
650
  * @return bool
651
  * @access public
787
  * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not
788
  * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method.
789
  *
790
+ * @param string $name
791
+ * @param array $arguments
792
  * @return mixed
793
  * @access public
794
  */
phpseclib/Net/SSH1.php CHANGED
@@ -857,6 +857,7 @@ class Net_SSH1
857
  * @see self::interactiveRead()
858
  * @see self::interactiveWrite()
859
  * @param string $cmd
 
860
  * @return mixed
861
  * @access public
862
  */
@@ -1434,7 +1435,6 @@ class Net_SSH1
1434
  * named constants from it, using the value as the name of the constant and the index as the value of the constant.
1435
  * If any of the constants that would be defined already exists, none of the constants will be defined.
1436
  *
1437
- * @param array $array
1438
  * @access private
1439
  */
1440
  function _define_array()
@@ -1633,7 +1633,8 @@ class Net_SSH1
1633
  *
1634
  * Makes sure that only the last 1MB worth of packets will be logged
1635
  *
1636
- * @param string $data
 
1637
  * @access private
1638
  */
1639
  function _append_log($protocol_flags, $message)
857
  * @see self::interactiveRead()
858
  * @see self::interactiveWrite()
859
  * @param string $cmd
860
+ * @param bool $block
861
  * @return mixed
862
  * @access public
863
  */
1435
  * named constants from it, using the value as the name of the constant and the index as the value of the constant.
1436
  * If any of the constants that would be defined already exists, none of the constants will be defined.
1437
  *
 
1438
  * @access private
1439
  */
1440
  function _define_array()
1633
  *
1634
  * Makes sure that only the last 1MB worth of packets will be logged
1635
  *
1636
+ * @param int $protocol_flags
1637
+ * @param string $message
1638
  * @access private
1639
  */
1640
  function _append_log($protocol_flags, $message)
phpseclib/Net/SSH2.php CHANGED
@@ -150,6 +150,23 @@ define('NET_SSH2_READ_REGEX', 2);
150
  define('NET_SSH2_READ_NEXT', 3);
151
  /**#@-*/
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  /**
154
  * Pure-PHP implementation of SSHv2.
155
  *
@@ -692,6 +709,14 @@ class Net_SSH2
692
  */
693
  var $curTimeout;
694
 
 
 
 
 
 
 
 
 
695
  /**
696
  * Real-time log file pointer
697
  *
@@ -967,6 +992,71 @@ class Net_SSH2
967
  */
968
  var $auth = array();
969
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
970
  /**
971
  * Default Constructor.
972
  *
@@ -1352,6 +1442,7 @@ class Net_SSH2
1352
  function _key_exchange($kexinit_payload_server = false)
1353
  {
1354
  $preferred = $this->preferred;
 
1355
 
1356
  $kex_algorithms = isset($preferred['kex']) ?
1357
  $preferred['kex'] :
@@ -1435,7 +1526,7 @@ class Net_SSH2
1435
  0
1436
  );
1437
 
1438
- if ($this->send_kex_first) {
1439
  if (!$this->_send_binary_packet($kexinit_payload_client)) {
1440
  return false;
1441
  }
@@ -1451,6 +1542,8 @@ class Net_SSH2
1451
  user_error('Expected SSH_MSG_KEXINIT');
1452
  return false;
1453
  }
 
 
1454
  }
1455
 
1456
  $response = $kexinit_payload_server;
@@ -1523,7 +1616,7 @@ class Net_SSH2
1523
  extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
1524
  $first_kex_packet_follows = $first_kex_packet_follows != 0;
1525
 
1526
- if (!$this->send_kex_first && !$this->_send_binary_packet($kexinit_payload_client)) {
1527
  return false;
1528
  }
1529
 
@@ -1552,6 +1645,45 @@ class Net_SSH2
1552
  user_error('No compatible key exchange algorithms found');
1553
  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1554
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1555
  if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
1556
  $dh_group_sizes_packed = pack(
1557
  'NNN',
@@ -1759,12 +1891,6 @@ class Net_SSH2
1759
  $this->session_id = $this->exchange_hash;
1760
  }
1761
 
1762
- $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
1763
- if ($server_host_key_algorithm === false) {
1764
- user_error('No compatible server host key algorithms found');
1765
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1766
- }
1767
-
1768
  switch ($server_host_key_algorithm) {
1769
  case 'ssh-dss':
1770
  $expected_key_format = 'ssh-dss';
@@ -1884,14 +2010,8 @@ class Net_SSH2
1884
  $this->decrypt->decrypt(str_repeat("\0", 1536));
1885
  }
1886
 
1887
- $mac_algorithm = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
1888
- if ($mac_algorithm === false) {
1889
- user_error('No compatible client to server message authentication algorithms found');
1890
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1891
- }
1892
-
1893
  $createKeyLength = 0; // ie. $mac_algorithm == 'none'
1894
- switch ($mac_algorithm) {
1895
  case 'hmac-sha2-256':
1896
  $this->hmac_create = new Crypt_Hash('sha256');
1897
  $createKeyLength = 32;
@@ -1912,17 +2032,11 @@ class Net_SSH2
1912
  $this->hmac_create = new Crypt_Hash('md5-96');
1913
  $createKeyLength = 16;
1914
  }
1915
- $this->hmac_create->name = $mac_algorithm;
1916
-
1917
- $mac_algorithm = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
1918
- if ($mac_algorithm === false) {
1919
- user_error('No compatible server to client message authentication algorithms found');
1920
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1921
- }
1922
 
1923
  $checkKeyLength = 0;
1924
  $this->hmac_size = 0;
1925
- switch ($mac_algorithm) {
1926
  case 'hmac-sha2-256':
1927
  $this->hmac_check = new Crypt_Hash('sha256');
1928
  $checkKeyLength = 32;
@@ -1948,7 +2062,7 @@ class Net_SSH2
1948
  $checkKeyLength = 16;
1949
  $this->hmac_size = 12;
1950
  }
1951
- $this->hmac_check->name = $mac_algorithm;
1952
 
1953
  $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
1954
  while ($createKeyLength > strlen($key)) {
@@ -1962,19 +2076,7 @@ class Net_SSH2
1962
  }
1963
  $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
1964
 
1965
- $compression_algorithm = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
1966
- if ($compression_algorithm === false) {
1967
- user_error('No compatible client to server compression algorithms found');
1968
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1969
- }
1970
- //$this->decompress = $compression_algorithm == 'zlib';
1971
-
1972
- $compression_algorithm = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server);
1973
- if ($compression_algorithm === false) {
1974
- user_error('No compatible server to client compression algorithms found');
1975
- return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1976
- }
1977
- //$this->compress = $compression_algorithm == 'zlib';
1978
 
1979
  return true;
1980
  }
@@ -2122,8 +2224,6 @@ class Net_SSH2
2122
  * The $password parameter can be a plaintext password, a Crypt_RSA object or an array
2123
  *
2124
  * @param string $username
2125
- * @param mixed $password
2126
- * @param mixed $...
2127
  * @return bool
2128
  * @see self::_login()
2129
  * @access public
@@ -2135,11 +2235,13 @@ class Net_SSH2
2135
 
2136
  // try logging with 'none' as an authentication method first since that's what
2137
  // PuTTY does
2138
- if ($this->_login($username)) {
2139
- return true;
2140
- }
2141
- if (count($args) == 1) {
2142
- return false;
 
 
2143
  }
2144
  return call_user_func_array(array(&$this, '_login'), $args);
2145
  }
@@ -2148,8 +2250,6 @@ class Net_SSH2
2148
  * Login Helper
2149
  *
2150
  * @param string $username
2151
- * @param mixed $password
2152
- * @param mixed $...
2153
  * @return bool
2154
  * @see self::_login_helper()
2155
  * @access private
@@ -2167,9 +2267,61 @@ class Net_SSH2
2167
  return $this->_login_helper($username);
2168
  }
2169
 
2170
- foreach ($args as $arg) {
2171
- if ($this->_login_helper($username, $arg)) {
2172
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2173
  }
2174
  }
2175
  return false;
@@ -2283,7 +2435,9 @@ class Net_SSH2
2283
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2284
  $this->bitmap |= NET_SSH2_MASK_LOGIN;
2285
  return true;
2286
- //case NET_SSH2_MSG_USERAUTH_FAILURE:
 
 
2287
  default:
2288
  return false;
2289
  }
@@ -2355,6 +2509,7 @@ class Net_SSH2
2355
  }
2356
  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2357
  $auth_methods = explode(',', $this->_string_shift($response, $length));
 
2358
  if (!strlen($response)) {
2359
  return false;
2360
  }
@@ -2414,7 +2569,6 @@ class Net_SSH2
2414
  /**
2415
  * Handle the keyboard-interactive requests / responses.
2416
  *
2417
- * @param string $responses...
2418
  * @return bool
2419
  * @access private
2420
  */
@@ -2528,6 +2682,8 @@ class Net_SSH2
2528
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2529
  return true;
2530
  case NET_SSH2_MSG_USERAUTH_FAILURE:
 
 
2531
  return false;
2532
  }
2533
 
@@ -2559,7 +2715,7 @@ class Net_SSH2
2559
  * Login with an RSA private key
2560
  *
2561
  * @param string $username
2562
- * @param Crypt_RSA $password
2563
  * @return bool
2564
  * @access private
2565
  * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
@@ -2636,13 +2792,21 @@ class Net_SSH2
2636
  if (strlen($response) < 4) {
2637
  return false;
2638
  }
2639
- extract(unpack('Nlength', $this->_string_shift($response, 4)));
2640
- $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length);
 
2641
  return false;
2642
  case NET_SSH2_MSG_USERAUTH_PK_OK:
2643
  // we'll just take it on faith that the public key blob and the public key algorithm name are as
2644
  // they should be
2645
  $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK');
 
 
 
 
 
 
 
2646
  }
2647
 
2648
  $packet = $part1 . chr(1) . $part2;
@@ -2671,13 +2835,16 @@ class Net_SSH2
2671
  switch ($type) {
2672
  case NET_SSH2_MSG_USERAUTH_FAILURE:
2673
  // either the login is bad or the server employs multi-factor authentication
 
 
2674
  return false;
2675
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2676
  $this->bitmap |= NET_SSH2_MASK_LOGIN;
2677
  return true;
2678
  }
2679
 
2680
- return false;
 
2681
  }
2682
 
2683
  /**
@@ -2694,6 +2861,19 @@ class Net_SSH2
2694
  $this->timeout = $this->curTimeout = $timeout;
2695
  }
2696
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2697
  /**
2698
  * Get the output from stdError
2699
  *
@@ -2783,26 +2963,13 @@ class Net_SSH2
2783
  return false;
2784
  }
2785
 
2786
- $response = $this->_get_binary_packet();
2787
- if ($response === false) {
2788
- $this->bitmap = 0;
2789
- user_error('Connection closed by server');
2790
- return false;
2791
- }
2792
 
2793
- if (!strlen($response)) {
2794
- return false;
 
2795
  }
2796
- list(, $type) = unpack('C', $this->_string_shift($response, 1));
2797
 
2798
- switch ($type) {
2799
- case NET_SSH2_MSG_CHANNEL_SUCCESS:
2800
- break;
2801
- case NET_SSH2_MSG_CHANNEL_FAILURE:
2802
- default:
2803
- user_error('Unable to request pseudo-terminal');
2804
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2805
- }
2806
  $this->in_request_pty_exec = true;
2807
  }
2808
 
@@ -2924,26 +3091,11 @@ class Net_SSH2
2924
  return false;
2925
  }
2926
 
2927
- $response = $this->_get_binary_packet();
2928
- if ($response === false) {
2929
- $this->bitmap = 0;
2930
- user_error('Connection closed by server');
2931
- return false;
2932
- }
2933
-
2934
- if (!strlen($response)) {
2935
- return false;
2936
- }
2937
- list(, $type) = unpack('C', $this->_string_shift($response, 1));
2938
 
2939
- switch ($type) {
2940
- case NET_SSH2_MSG_CHANNEL_SUCCESS:
2941
- // if a pty can't be opened maybe commands can still be executed
2942
- case NET_SSH2_MSG_CHANNEL_FAILURE:
2943
- break;
2944
- default:
2945
- user_error('Unable to request pseudo-terminal');
2946
- return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2947
  }
2948
 
2949
  $packet = pack(
@@ -2958,8 +3110,6 @@ class Net_SSH2
2958
  return false;
2959
  }
2960
 
2961
- $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
2962
-
2963
  $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
2964
  if ($response === false) {
2965
  return false;
@@ -3333,9 +3483,57 @@ class Net_SSH2
3333
  */
3334
  function _get_binary_packet($skip_channel_filter = false)
3335
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3336
  if (!is_resource($this->fsock) || feof($this->fsock)) {
3337
  $this->bitmap = 0;
3338
- user_error('Connection closed prematurely');
3339
  return false;
3340
  }
3341
 
@@ -3343,7 +3541,8 @@ class Net_SSH2
3343
  $raw = fread($this->fsock, $this->decrypt_block_size);
3344
 
3345
  if (!strlen($raw)) {
3346
- return '';
 
3347
  }
3348
 
3349
  if ($this->decrypt !== false) {
@@ -3406,9 +3605,41 @@ class Net_SSH2
3406
  }
3407
  }
3408
 
3409
- //if ($this->decompress) {
3410
- // $payload = gzinflate(substr($payload, 2));
3411
- //}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3412
 
3413
  $this->get_seq_no++;
3414
 
@@ -3483,10 +3714,24 @@ class Net_SSH2
3483
 
3484
  // only called when we've already logged in
3485
  if (($this->bitmap & NET_SSH2_MASK_CONNECTED) && $this->isAuthenticated()) {
 
 
 
 
3486
  switch (ord($payload[0])) {
 
 
 
 
 
 
 
 
 
 
 
3487
  case NET_SSH2_MSG_CHANNEL_DATA:
3488
  case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
3489
- case NET_SSH2_MSG_CHANNEL_REQUEST:
3490
  case NET_SSH2_MSG_CHANNEL_CLOSE:
3491
  case NET_SSH2_MSG_CHANNEL_EOF:
3492
  if (!$skip_channel_filter && !empty($this->server_channels)) {
@@ -3668,14 +3913,28 @@ class Net_SSH2
3668
  *
3669
  * Returns the data as a string if it's available and false if not.
3670
  *
3671
- * @param $client_channel
3672
- * @return mixed
 
3673
  * @access private
3674
  */
3675
  function _get_channel_packet($client_channel, $skip_extended = false)
3676
  {
3677
  if (!empty($this->channel_buffers[$client_channel])) {
3678
- return array_shift($this->channel_buffers[$client_channel]);
 
 
 
 
 
 
 
 
 
 
 
 
 
3679
  }
3680
 
3681
  while (true) {
@@ -3683,36 +3942,13 @@ class Net_SSH2
3683
  $response = $this->binary_packet_buffer;
3684
  $this->binary_packet_buffer = false;
3685
  } else {
3686
- $read = array($this->fsock);
3687
- $write = $except = null;
3688
-
3689
- if (!$this->curTimeout) {
3690
- @stream_select($read, $write, $except, null);
3691
- } else {
3692
- if ($this->curTimeout < 0) {
3693
- $this->is_timeout = true;
3694
- return true;
3695
- }
3696
-
3697
- $read = array($this->fsock);
3698
- $write = $except = null;
3699
-
3700
- $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
3701
- $sec = floor($this->curTimeout);
3702
- $usec = 1000000 * ($this->curTimeout - $sec);
3703
- // on windows this returns a "Warning: Invalid CRT parameters detected" error
3704
- if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
3705
- $this->is_timeout = true;
3706
- if ($client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) {
3707
- $this->_close_channel($client_channel);
3708
- }
3709
- return true;
3710
  }
3711
- $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
3712
- $this->curTimeout-= $elapsed;
3713
  }
3714
-
3715
- $response = $this->_get_binary_packet(true);
3716
  if ($response === false) {
3717
  $this->bitmap = 0;
3718
  user_error('Connection closed by server');
@@ -3772,10 +4008,7 @@ class Net_SSH2
3772
  if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
3773
  return $data;
3774
  }
3775
- if (!isset($this->channel_buffers[$channel])) {
3776
- $this->channel_buffers[$channel] = array();
3777
- }
3778
- $this->channel_buffers[$channel][] = $data;
3779
 
3780
  continue 2;
3781
  case NET_SSH2_MSG_CHANNEL_REQUEST:
@@ -3854,10 +4087,15 @@ class Net_SSH2
3854
  $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
3855
  $this->_on_channel_open();
3856
  return $result;
3857
- //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
3858
- default:
3859
  user_error('Unable to open channel');
3860
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
 
 
 
 
 
 
3861
  }
3862
  break;
3863
  case NET_SSH2_MSG_CHANNEL_REQUEST:
@@ -3866,6 +4104,14 @@ class Net_SSH2
3866
  return true;
3867
  case NET_SSH2_MSG_CHANNEL_FAILURE:
3868
  return false;
 
 
 
 
 
 
 
 
3869
  default:
3870
  user_error('Unable to fulfill channel request');
3871
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
@@ -3905,10 +4151,7 @@ class Net_SSH2
3905
  if ($client_channel == $channel) {
3906
  return $data;
3907
  }
3908
- if (!isset($this->channel_buffers[$channel])) {
3909
- $this->channel_buffers[$channel] = array();
3910
- }
3911
- $this->channel_buffers[$channel][] = $data;
3912
  break;
3913
  case NET_SSH2_MSG_CHANNEL_CLOSE:
3914
  $this->curTimeout = 5;
@@ -3927,7 +4170,7 @@ class Net_SSH2
3927
  case NET_SSH2_MSG_CHANNEL_EOF:
3928
  break;
3929
  default:
3930
- user_error('Error reading channel data');
3931
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
3932
  }
3933
  }
@@ -3952,11 +4195,27 @@ class Net_SSH2
3952
  return false;
3953
  }
3954
 
3955
- //if ($this->compress) {
3956
- // // the -4 removes the checksum:
3957
- // // http://php.net/function.gzcompress#57710
3958
- // $data = substr(gzcompress($data), 0, -4);
3959
- //}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3960
 
3961
  // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
3962
  $packet_length = strlen($data) + 9;
@@ -3979,15 +4238,15 @@ class Net_SSH2
3979
  $packet.= $hmac;
3980
 
3981
  $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
3982
- $result = strlen($packet) == fputs($this->fsock, $packet);
3983
  $stop = strtok(microtime(), ' ') + strtok('');
3984
 
3985
  if (defined('NET_SSH2_LOGGING')) {
3986
  $current = strtok(microtime(), ' ') + strtok('');
3987
- $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')';
3988
  $message_number = '-> ' . $message_number .
3989
  ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
3990
- $this->_append_log($message_number, isset($logged) ? $logged : $data);
3991
  $this->last_packet = $current;
3992
  }
3993
 
@@ -3999,7 +4258,8 @@ class Net_SSH2
3999
  *
4000
  * Makes sure that only the last 1MB worth of packets will be logged
4001
  *
4002
- * @param string $data
 
4003
  * @access private
4004
  */
4005
  function _append_log($message_number, $message)
@@ -4204,7 +4464,6 @@ class Net_SSH2
4204
  * named constants from it, using the value as the name of the constant and the index as the value of the constant.
4205
  * If any of the constants that would be defined already exists, none of the constants will be defined.
4206
  *
4207
- * @param array $array
4208
  * @access private
4209
  */
4210
  function _define_array()
@@ -4608,11 +4867,15 @@ class Net_SSH2
4608
  //'none' // OPTIONAL no encryption; NOT RECOMMENDED
4609
  );
4610
 
4611
- $engines = array(
4612
- CRYPT_ENGINE_OPENSSL,
4613
- CRYPT_ENGINE_MCRYPT,
4614
- CRYPT_ENGINE_INTERNAL
4615
- );
 
 
 
 
4616
 
4617
  $ciphers = array();
4618
  foreach ($engines as $engine) {
@@ -4666,10 +4929,12 @@ class Net_SSH2
4666
  */
4667
  function getSupportedCompressionAlgorithms()
4668
  {
4669
- return array(
4670
- 'none' // REQUIRED no compression
4671
- //'zlib' // OPTIONAL ZLIB (LZ77) compression
4672
- );
 
 
4673
  }
4674
 
4675
  /**
@@ -4684,18 +4949,24 @@ class Net_SSH2
4684
  {
4685
  $this->_connect();
4686
 
 
 
 
 
 
 
4687
  return array(
4688
  'kex' => $this->kex_algorithm,
4689
  'hostkey' => $this->signature_format,
4690
  'client_to_server' => array(
4691
  'crypt' => $this->encrypt->name,
4692
  'mac' => $this->hmac_create->name,
4693
- 'comp' => 'none',
4694
  ),
4695
  'server_to_client' => array(
4696
  'crypt' => $this->decrypt->name,
4697
  'mac' => $this->hmac_check->name,
4698
- 'comp' => 'none',
4699
  )
4700
  );
4701
  }
@@ -5100,4 +5371,31 @@ class Net_SSH2
5100
  );
5101
  }
5102
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5103
  }
150
  define('NET_SSH2_READ_NEXT', 3);
151
  /**#@-*/
152
 
153
+ /**#@+
154
+ * @access private
155
+ */
156
+ /**
157
+ * No compression
158
+ */
159
+ define('NET_SSH2_COMPRESSION_NONE', 1);
160
+ /**
161
+ * zlib compression
162
+ */
163
+ define('NET_SSH2_COMPRESSION_ZLIB', 2);
164
+ /**
165
+ * zlib@openssh.com
166
+ */
167
+ define('NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH', 3);
168
+ /**#@-*/
169
+
170
  /**
171
  * Pure-PHP implementation of SSHv2.
172
  *
709
  */
710
  var $curTimeout;
711
 
712
+ /**
713
+ * Keep Alive Interval
714
+ *
715
+ * @see self::setKeepAlive()
716
+ * @access private
717
+ */
718
+ var $keepAlive;
719
+
720
  /**
721
  * Real-time log file pointer
722
  *
992
  */
993
  var $auth = array();
994
 
995
+ /**
996
+ * The authentication methods that may productively continue authentication.
997
+ *
998
+ * @see https://tools.ietf.org/html/rfc4252#section-5.1
999
+ * @var array|null
1000
+ * @access private
1001
+ */
1002
+ var $auth_methods_to_continue = null;
1003
+
1004
+ /**
1005
+ * Compression method
1006
+ *
1007
+ * @var int
1008
+ * @access private
1009
+ */
1010
+ var $compress = NET_SSH2_COMPRESSION_NONE;
1011
+
1012
+ /**
1013
+ * Decompression method
1014
+ *
1015
+ * @var resource|object
1016
+ * @access private
1017
+ */
1018
+ var $decompress = NET_SSH2_COMPRESSION_NONE;
1019
+
1020
+ /**
1021
+ * Compression context
1022
+ *
1023
+ * @var int
1024
+ * @access private
1025
+ */
1026
+ var $compress_context;
1027
+
1028
+ /**
1029
+ * Decompression context
1030
+ *
1031
+ * @var resource|object
1032
+ * @access private
1033
+ */
1034
+ var $decompress_context;
1035
+
1036
+ /**
1037
+ * Regenerate Compression Context
1038
+ *
1039
+ * @var bool
1040
+ * @access private
1041
+ */
1042
+ var $regenerate_compression_context = false;
1043
+
1044
+ /**
1045
+ * Regenerate Decompression Context
1046
+ *
1047
+ * @var bool
1048
+ * @access private
1049
+ */
1050
+ var $regenerate_decompression_context = false;
1051
+
1052
+ /**
1053
+ * Smart multi-factor authentication flag
1054
+ *
1055
+ * @var bool
1056
+ * @access private
1057
+ */
1058
+ var $smartMFA = true;
1059
+
1060
  /**
1061
  * Default Constructor.
1062
  *
1442
  function _key_exchange($kexinit_payload_server = false)
1443
  {
1444
  $preferred = $this->preferred;
1445
+ $send_kex = true;
1446
 
1447
  $kex_algorithms = isset($preferred['kex']) ?
1448
  $preferred['kex'] :
1526
  0
1527
  );
1528
 
1529
+ if ($kexinit_payload_server === false) {
1530
  if (!$this->_send_binary_packet($kexinit_payload_client)) {
1531
  return false;
1532
  }
1542
  user_error('Expected SSH_MSG_KEXINIT');
1543
  return false;
1544
  }
1545
+
1546
+ $send_kex = false;
1547
  }
1548
 
1549
  $response = $kexinit_payload_server;
1616
  extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1)));
1617
  $first_kex_packet_follows = $first_kex_packet_follows != 0;
1618
 
1619
+ if ($send_kex && !$this->_send_binary_packet($kexinit_payload_client)) {
1620
  return false;
1621
  }
1622
 
1645
  user_error('No compatible key exchange algorithms found');
1646
  return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1647
  }
1648
+
1649
+ $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms);
1650
+ if ($server_host_key_algorithm === false) {
1651
+ user_error('No compatible server host key algorithms found');
1652
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1653
+ }
1654
+
1655
+ $mac_algorithm_out = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server);
1656
+ if ($mac_algorithm_out === false) {
1657
+ user_error('No compatible client to server message authentication algorithms found');
1658
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1659
+ }
1660
+
1661
+ $mac_algorithm_in = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client);
1662
+ if ($mac_algorithm_in === false) {
1663
+ user_error('No compatible server to client message authentication algorithms found');
1664
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1665
+ }
1666
+
1667
+ $compression_map = array(
1668
+ 'none' => NET_SSH2_COMPRESSION_NONE,
1669
+ 'zlib' => NET_SSH2_COMPRESSION_ZLIB,
1670
+ 'zlib@openssh.com' => NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH
1671
+ );
1672
+
1673
+ $compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server);
1674
+ if ($compression_algorithm_out === false) {
1675
+ user_error('No compatible client to server compression algorithms found');
1676
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1677
+ }
1678
+ $this->compress = $compression_map[$compression_algorithm_out];
1679
+
1680
+ $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client);
1681
+ if ($compression_algorithm_in === false) {
1682
+ user_error('No compatible server to client compression algorithms found');
1683
+ return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED);
1684
+ }
1685
+ $this->decompress = $compression_map[$compression_algorithm_in];
1686
+
1687
  if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) {
1688
  $dh_group_sizes_packed = pack(
1689
  'NNN',
1891
  $this->session_id = $this->exchange_hash;
1892
  }
1893
 
 
 
 
 
 
 
1894
  switch ($server_host_key_algorithm) {
1895
  case 'ssh-dss':
1896
  $expected_key_format = 'ssh-dss';
2010
  $this->decrypt->decrypt(str_repeat("\0", 1536));
2011
  }
2012
 
 
 
 
 
 
 
2013
  $createKeyLength = 0; // ie. $mac_algorithm == 'none'
2014
+ switch ($mac_algorithm_out) {
2015
  case 'hmac-sha2-256':
2016
  $this->hmac_create = new Crypt_Hash('sha256');
2017
  $createKeyLength = 32;
2032
  $this->hmac_create = new Crypt_Hash('md5-96');
2033
  $createKeyLength = 16;
2034
  }
2035
+ $this->hmac_create->name = $mac_algorithm_out;
 
 
 
 
 
 
2036
 
2037
  $checkKeyLength = 0;
2038
  $this->hmac_size = 0;
2039
+ switch ($mac_algorithm_in) {
2040
  case 'hmac-sha2-256':
2041
  $this->hmac_check = new Crypt_Hash('sha256');
2042
  $checkKeyLength = 32;
2062
  $checkKeyLength = 16;
2063
  $this->hmac_size = 12;
2064
  }
2065
+ $this->hmac_check->name = $mac_algorithm_in;
2066
 
2067
  $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id);
2068
  while ($createKeyLength > strlen($key)) {
2076
  }
2077
  $this->hmac_check->setKey(substr($key, 0, $checkKeyLength));
2078
 
2079
+ $this->regenerate_compression_context = $this->regenerate_decompression_context = true;
 
 
 
 
 
 
 
 
 
 
 
 
2080
 
2081
  return true;
2082
  }
2224
  * The $password parameter can be a plaintext password, a Crypt_RSA object or an array
2225
  *
2226
  * @param string $username
 
 
2227
  * @return bool
2228
  * @see self::_login()
2229
  * @access public
2235
 
2236
  // try logging with 'none' as an authentication method first since that's what
2237
  // PuTTY does
2238
+ if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) {
2239
+ if ($this->_login($username)) {
2240
+ return true;
2241
+ }
2242
+ if (count($args) == 1) {
2243
+ return false;
2244
+ }
2245
  }
2246
  return call_user_func_array(array(&$this, '_login'), $args);
2247
  }
2250
  * Login Helper
2251
  *
2252
  * @param string $username
 
 
2253
  * @return bool
2254
  * @see self::_login_helper()
2255
  * @access private
2267
  return $this->_login_helper($username);
2268
  }
2269
 
2270
+ while (count($args)) {
2271
+ if (!$this->auth_methods_to_continue || !$this->smartMFA) {
2272
+ $newargs = $args;
2273
+ $args = array();
2274
+ } else {
2275
+ $newargs = array();
2276
+ foreach ($this->auth_methods_to_continue as $method) {
2277
+ switch ($method) {
2278
+ case 'publickey':
2279
+ foreach ($args as $key => $arg) {
2280
+ if (is_object($arg)) {
2281
+ $newargs[] = $arg;
2282
+ unset($args[$key]);
2283
+ break;
2284
+ }
2285
+ }
2286
+ break;
2287
+ case 'keyboard-interactive':
2288
+ $hasArray = $hasString = false;
2289
+ foreach ($args as $arg) {
2290
+ if ($hasArray || is_array($arg)) {
2291
+ $hasArray = true;
2292
+ break;
2293
+ }
2294
+ if ($hasString || is_string($arg)) {
2295
+ $hasString = true;
2296
+ break;
2297
+ }
2298
+ }
2299
+ if ($hasArray && $hasString) {
2300
+ foreach ($args as $key => $arg) {
2301
+ if (is_array($arg)) {
2302
+ $newargs[] = $arg;
2303
+ break 2;
2304
+ }
2305
+ }
2306
+ }
2307
+ case 'password':
2308
+ foreach ($args as $key => $arg) {
2309
+ $newargs[] = $arg;
2310
+ unset($args[$key]);
2311
+ break;
2312
+ }
2313
+ }
2314
+ }
2315
+ }
2316
+
2317
+ if (!count($newargs)) {
2318
+ return false;
2319
+ }
2320
+
2321
+ foreach ($newargs as $arg) {
2322
+ if ($this->_login_helper($username, $arg)) {
2323
+ return true;
2324
+ }
2325
  }
2326
  }
2327
  return false;
2435
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2436
  $this->bitmap |= NET_SSH2_MASK_LOGIN;
2437
  return true;
2438
+ case NET_SSH2_MSG_USERAUTH_FAILURE:
2439
+ extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
2440
+ $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
2441
  default:
2442
  return false;
2443
  }
2509
  }
2510
  extract(unpack('Nlength', $this->_string_shift($response, 4)));
2511
  $auth_methods = explode(',', $this->_string_shift($response, $length));
2512
+ $this->auth_methods_to_continue = $auth_methods;
2513
  if (!strlen($response)) {
2514
  return false;
2515
  }
2569
  /**
2570
  * Handle the keyboard-interactive requests / responses.
2571
  *
 
2572
  * @return bool
2573
  * @access private
2574
  */
2682
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2683
  return true;
2684
  case NET_SSH2_MSG_USERAUTH_FAILURE:
2685
+ extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
2686
+ $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
2687
  return false;
2688
  }
2689
 
2715
  * Login with an RSA private key
2716
  *
2717
  * @param string $username
2718
+ * @param Crypt_RSA $privatekey
2719
  * @return bool
2720
  * @access private
2721
  * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis}
2792
  if (strlen($response) < 4) {
2793
  return false;
2794
  }
2795
+ extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
2796
+ $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
2797
+ $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE';
2798
  return false;
2799
  case NET_SSH2_MSG_USERAUTH_PK_OK:
2800
  // we'll just take it on faith that the public key blob and the public key algorithm name are as
2801
  // they should be
2802
  $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK');
2803
+ break;
2804
+ case NET_SSH2_MSG_USERAUTH_SUCCESS:
2805
+ $this->bitmap |= NET_SSH2_MASK_LOGIN;
2806
+ return true;
2807
+ default:
2808
+ user_error('Unexpected response to publickey authentication pt 1');
2809
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2810
  }
2811
 
2812
  $packet = $part1 . chr(1) . $part2;
2835
  switch ($type) {
2836
  case NET_SSH2_MSG_USERAUTH_FAILURE:
2837
  // either the login is bad or the server employs multi-factor authentication
2838
+ extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4)));
2839
+ $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen));
2840
  return false;
2841
  case NET_SSH2_MSG_USERAUTH_SUCCESS:
2842
  $this->bitmap |= NET_SSH2_MASK_LOGIN;
2843
  return true;
2844
  }
2845
 
2846
+ user_error('Unexpected response to publickey authentication pt 2');
2847
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2848
  }
2849
 
2850
  /**
2861
  $this->timeout = $this->curTimeout = $timeout;
2862
  }
2863
 
2864
+ /**
2865
+ * Set Keep Alive
2866
+ *
2867
+ * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number.
2868
+ *
2869
+ * @param int $interval
2870
+ * @access public
2871
+ */
2872
+ function setKeepAlive($interval)
2873
+ {
2874
+ $this->keepAlive = $interval;
2875
+ }
2876
+
2877
  /**
2878
  * Get the output from stdError
2879
  *
2963
  return false;
2964
  }
2965
 
2966
+ $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST;
 
 
 
 
 
2967
 
2968
+ if (!$this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC)) {
2969
+ user_error('Unable to request pseudo-terminal');
2970
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
2971
  }
 
2972
 
 
 
 
 
 
 
 
 
2973
  $this->in_request_pty_exec = true;
2974
  }
2975
 
3091
  return false;
3092
  }
3093
 
3094
+ $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST;
 
 
 
 
 
 
 
 
 
 
3095
 
3096
+ if (!$this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL)) {
3097
+ user_error('Unable to request pseudo-terminal');
3098
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
 
 
 
 
 
3099
  }
3100
 
3101
  $packet = pack(
3110
  return false;
3111
  }
3112
 
 
 
3113
  $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL);
3114
  if ($response === false) {
3115
  return false;
3483
  */
3484
  function _get_binary_packet($skip_channel_filter = false)
3485
  {
3486
+ if ($skip_channel_filter) {
3487
+ $read = array($this->fsock);
3488
+ $write = $except = null;
3489
+
3490
+ if (!$this->curTimeout) {
3491
+ if ($this->keepAlive <= 0) {
3492
+ @stream_select($read, $write, $except, null);
3493
+ } else {
3494
+ if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) {
3495
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
3496
+ return $this->_get_binary_packet(true);
3497
+ }
3498
+ }
3499
+ } else {
3500
+ if ($this->curTimeout < 0) {
3501
+ $this->is_timeout = true;
3502
+ return true;
3503
+ }
3504
+
3505
+ $read = array($this->fsock);
3506
+ $write = $except = null;
3507
+
3508
+ $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
3509
+
3510
+ if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) {
3511
+ if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) {
3512
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0));
3513
+ $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
3514
+ $this->curTimeout-= $elapsed;
3515
+ return $this->_get_binary_packet(true);
3516
+ }
3517
+ $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
3518
+ $this->curTimeout-= $elapsed;
3519
+ }
3520
+
3521
+ $sec = floor($this->curTimeout);
3522
+ $usec = 1000000 * ($this->curTimeout - $sec);
3523
+
3524
+ // on windows this returns a "Warning: Invalid CRT parameters detected" error
3525
+ if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) {
3526
+ $this->is_timeout = true;
3527
+ return true;
3528
+ }
3529
+ $elapsed = strtok(microtime(), ' ') + strtok('') - $start;
3530
+ $this->curTimeout-= $elapsed;
3531
+ }
3532
+ }
3533
+
3534
  if (!is_resource($this->fsock) || feof($this->fsock)) {
3535
  $this->bitmap = 0;
3536
+ user_error('Connection closed (by server) prematurely ' . $elapsed . 's');
3537
  return false;
3538
  }
3539
 
3541
  $raw = fread($this->fsock, $this->decrypt_block_size);
3542
 
3543
  if (!strlen($raw)) {
3544
+ user_error('No data received from server');
3545
+ return false;
3546
  }
3547
 
3548
  if ($this->decrypt !== false) {
3605
  }
3606
  }
3607
 
3608
+ switch ($this->decompress) {
3609
+ case NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
3610
+ if (!$this->isAuthenticated()) {
3611
+ break;
3612
+ }
3613
+ case NET_SSH2_COMPRESSION_ZLIB:
3614
+ if ($this->regenerate_decompression_context) {
3615
+ $this->regenerate_decompression_context = false;
3616
+
3617
+ $cmf = ord($payload[0]);
3618
+ $cm = $cmf & 0x0F;
3619
+ if ($cm != 8) { // deflate
3620
+ user_error("Only CM = 8 ('deflate') is supported ($cm)");
3621
+ }
3622
+ $cinfo = ($cmf & 0xF0) >> 4;
3623
+ if ($cinfo > 7) {
3624
+ user_error("CINFO above 7 is not allowed ($cinfo)");
3625
+ }
3626
+ $windowSize = 1 << ($cinfo + 8);
3627
+
3628
+ $flg = ord($payload[1]);
3629
+ //$fcheck = $flg && 0x0F;
3630
+ if ((($cmf << 8) | $flg) % 31) {
3631
+ user_error('fcheck failed');
3632
+ }
3633
+ $fdict = boolval($flg & 0x20);
3634
+ $flevel = ($flg & 0xC0) >> 6;
3635
+
3636
+ $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, array('window' => $cinfo + 8));
3637
+ $payload = substr($payload, 2);
3638
+ }
3639
+ if ($this->decompress_context) {
3640
+ $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH);
3641
+ }
3642
+ }
3643
 
3644
  $this->get_seq_no++;
3645
 
3714
 
3715
  // only called when we've already logged in
3716
  if (($this->bitmap & NET_SSH2_MASK_CONNECTED) && $this->isAuthenticated()) {
3717
+ if (is_bool($payload)) {
3718
+ return $payload;
3719
+ }
3720
+
3721
  switch (ord($payload[0])) {
3722
+ case NET_SSH2_MSG_CHANNEL_REQUEST:
3723
+ if (strlen($payload) == 31) {
3724
+ extract(unpack('cpacket_type/Nchannel/Nlength', $payload));
3725
+ if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) {
3726
+ if (ord(substr($payload, 9 + $length))) { // want reply
3727
+ $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel]));
3728
+ }
3729
+ $payload = $this->_get_binary_packet($skip_channel_filter);
3730
+ }
3731
+ }
3732
+ break;
3733
  case NET_SSH2_MSG_CHANNEL_DATA:
3734
  case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA:
 
3735
  case NET_SSH2_MSG_CHANNEL_CLOSE:
3736
  case NET_SSH2_MSG_CHANNEL_EOF:
3737
  if (!$skip_channel_filter && !empty($this->server_channels)) {
3913
  *
3914
  * Returns the data as a string if it's available and false if not.
3915
  *
3916
+ * @param int $client_channel
3917
+ * @param bool $skip_extended
3918
+ * @return mixed|bool
3919
  * @access private
3920
  */
3921
  function _get_channel_packet($client_channel, $skip_extended = false)
3922
  {
3923
  if (!empty($this->channel_buffers[$client_channel])) {
3924
+ switch ($this->channel_status[$client_channel]) {
3925
+ case NET_SSH2_MSG_CHANNEL_REQUEST:
3926
+ foreach ($this->channel_buffers[$client_channel] as $i => $packet) {
3927
+ switch (ord($packet[0])) {
3928
+ case NET_SSH2_MSG_CHANNEL_SUCCESS:
3929
+ case NET_SSH2_MSG_CHANNEL_FAILURE:
3930
+ unset($this->channel_buffers[$client_channel][$i]);
3931
+ return substr($packet, 1);
3932
+ }
3933
+ }
3934
+ break;
3935
+ default:
3936
+ return substr(array_shift($this->channel_buffers[$client_channel]), 1);
3937
+ }
3938
  }
3939
 
3940
  while (true) {
3942
  $response = $this->binary_packet_buffer;
3943
  $this->binary_packet_buffer = false;
3944
  } else {
3945
+ $response = $this->_get_binary_packet(true);
3946
+ if ($response === true && $this->is_timeout) {
3947
+ if ($client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) {
3948
+ $this->_close_channel($client_channel);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3949
  }
3950
+ return true;
 
3951
  }
 
 
3952
  if ($response === false) {
3953
  $this->bitmap = 0;
3954
  user_error('Connection closed by server');
4008
  if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) {
4009
  return $data;
4010
  }
4011
+ $this->channel_buffers[$channel][] = chr($type) . $data;
 
 
 
4012
 
4013
  continue 2;
4014
  case NET_SSH2_MSG_CHANNEL_REQUEST:
4087
  $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended);
4088
  $this->_on_channel_open();
4089
  return $result;
4090
+ case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE:
 
4091
  user_error('Unable to open channel');
4092
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4093
+ default:
4094
+ if ($client_channel == $channel) {
4095
+ user_error('Unexpected response to open request');
4096
+ return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4097
+ }
4098
+ return $this->_get_channel_packet($client_channel, $skip_extended);
4099
  }
4100
  break;
4101
  case NET_SSH2_MSG_CHANNEL_REQUEST:
4104
  return true;
4105
  case NET_SSH2_MSG_CHANNEL_FAILURE:
4106
  return false;
4107
+ case NET_SSH2_MSG_CHANNEL_DATA:
4108
+ if (strlen($response) < 4) {
4109
+ return false;
4110
+ }
4111
+ extract(unpack('Nlength', $this->_string_shift($response, 4)));
4112
+ $data = $this->_string_shift($response, $length);
4113
+ $this->channel_buffers[$channel][] = chr($type) . $data;
4114
+ return $this->_get_channel_packet($client_channel, $skip_extended);
4115
  default:
4116
  user_error('Unable to fulfill channel request');
4117
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4151
  if ($client_channel == $channel) {
4152
  return $data;
4153
  }
4154
+ $this->channel_buffers[$channel][] = chr($type) . $data;
 
 
 
4155
  break;
4156
  case NET_SSH2_MSG_CHANNEL_CLOSE:
4157
  $this->curTimeout = 5;
4170
  case NET_SSH2_MSG_CHANNEL_EOF:
4171
  break;
4172
  default:
4173
+ user_error("Error reading channel data ($type)");
4174
  return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION);
4175
  }
4176
  }
4195
  return false;
4196
  }
4197
 
4198
+ if (!isset($logged)) {
4199
+ $logged = $data;
4200
+ }
4201
+
4202
+ switch ($this->compress) {
4203
+ case NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH:
4204
+ if (!$this->isAuthenticated()) {
4205
+ break;
4206
+ }
4207
+ case NET_SSH2_COMPRESSION_ZLIB:
4208
+ if (!$this->regenerate_compression_context) {
4209
+ $header = '';
4210
+ } else {
4211
+ $this->regenerate_compression_context = false;
4212
+ $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, ['window' => 15]);
4213
+ $header = "\x78\x9C";
4214
+ }
4215
+ if ($this->compress_context) {
4216
+ $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH);
4217
+ }
4218
+ }
4219
 
4220
  // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9
4221
  $packet_length = strlen($data) + 9;
4238
  $packet.= $hmac;
4239
 
4240
  $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
4241
+ $result = strlen($packet) == @fputs($this->fsock, $packet);
4242
  $stop = strtok(microtime(), ' ') + strtok('');
4243
 
4244
  if (defined('NET_SSH2_LOGGING')) {
4245
  $current = strtok(microtime(), ' ') + strtok('');
4246
+ $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')';
4247
  $message_number = '-> ' . $message_number .
4248
  ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)';
4249
+ $this->_append_log($message_number, $logged);
4250
  $this->last_packet = $current;
4251
  }
4252
 
4258
  *
4259
  * Makes sure that only the last 1MB worth of packets will be logged
4260
  *
4261
+ * @param string $message_number
4262
+ * @param string $message
4263
  * @access private
4264
  */
4265
  function _append_log($message_number, $message)
4464
  * named constants from it, using the value as the name of the constant and the index as the value of the constant.
4465
  * If any of the constants that would be defined already exists, none of the constants will be defined.
4466
  *
 
4467
  * @access private
4468
  */
4469
  function _define_array()
4867
  //'none' // OPTIONAL no encryption; NOT RECOMMENDED
4868
  );
4869
 
4870
+ if ($this->crypto_engine) {
4871
+ $engines = array($this->crypto_engine);
4872
+ } else {
4873
+ $engines = array(
4874
+ CRYPT_ENGINE_OPENSSL,
4875
+ CRYPT_ENGINE_MCRYPT,
4876
+ CRYPT_ENGINE_INTERNAL
4877
+ );
4878
+ }
4879
 
4880
  $ciphers = array();
4881
  foreach ($engines as $engine) {
4929
  */
4930
  function getSupportedCompressionAlgorithms()
4931
  {
4932
+ $algos = array('none'); // REQUIRED no compression
4933
+ if (function_exists('deflate_init')) {
4934
+ $algos[] = 'zlib@openssh.com'; // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed
4935
+ $algos[] = 'zlib';
4936
+ }
4937
+ return $algos;
4938
  }
4939
 
4940
  /**
4949
  {
4950
  $this->_connect();
4951
 
4952
+ $compression_map = array(
4953
+ NET_SSH2_COMPRESSION_NONE => 'none',
4954
+ NET_SSH2_COMPRESSION_ZLIB => 'zlib',
4955
+ NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com'
4956
+ );
4957
+
4958
  return array(
4959
  'kex' => $this->kex_algorithm,
4960
  'hostkey' => $this->signature_format,
4961
  'client_to_server' => array(
4962
  'crypt' => $this->encrypt->name,
4963
  'mac' => $this->hmac_create->name,
4964
+ 'comp' => $compression_map[$this->compress],
4965
  ),
4966
  'server_to_client' => array(
4967
  'crypt' => $this->decrypt->name,
4968
  'mac' => $this->hmac_check->name,
4969
+ 'comp' => $compression_map[$this->decompress],
4970
  )
4971
  );
4972
  }
5371
  );
5372
  }
5373
  }
5374
+
5375
+ /**
5376
+ * Return the list of authentication methods that may productively continue authentication.
5377
+ *
5378
+ * @see https://tools.ietf.org/html/rfc4252#section-5.1
5379
+ * @return array|null
5380
+ */
5381
+ function getAuthMethodsToContinue()
5382
+ {
5383
+ return $this->auth_methods_to_continue;
5384
+ }
5385
+
5386
+ /**
5387
+ * Enables "smart" multi-factor authentication (MFA)
5388
+ */
5389
+ function enableSmartMFA()
5390
+ {
5391
+ $this->smartMFA = true;
5392
+ }
5393
+
5394
+ /**
5395
+ * Disables "smart" multi-factor authentication (MFA)
5396
+ */
5397
+ function disableSmartMFA()
5398
+ {
5399
+ $this->smartMFA = false;
5400
+ }
5401
  }
phpseclib/bootstrap.php CHANGED
@@ -7,7 +7,8 @@
7
 
8
  if (extension_loaded('mbstring')) {
9
  // 2 - MB_OVERLOAD_STRING
10
- if (ini_get('mbstring.func_overload') & 2) {
 
11
  throw new \UnexpectedValueException(
12
  'Overloading of string functions using mbstring.func_overload ' .
13
  'is not supported by phpseclib.'
7
 
8
  if (extension_loaded('mbstring')) {
9
  // 2 - MB_OVERLOAD_STRING
10
+ // mbstring.func_overload is deprecated in php 7.2 and removed in php 8.0.
11
+ if (version_compare(PHP_VERSION, '8.0.0') < 0 && ini_get('mbstring.func_overload') & 2) {
12
  throw new \UnexpectedValueException(
13
  'Overloading of string functions using mbstring.func_overload ' .
14
  'is not supported by phpseclib.'
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://sourceforge.net/donate/index.php?group_id=198487
4
  Tags: ssh, sftp
5
  Requires at least: 3.1
6
  Tested up to: 6.1
7
- Stable tag: 0.8.4
8
 
9
  "SSH SFTP Updater Support" is the easiest way to keep your WordPress installation up-to-date with SFTP.
10
 
@@ -16,7 +16,7 @@ To use it, after installing and activating the plugins, add the necessary consta
16
 
17
  a) `define('FS_METHOD', 'ssh2');`
18
 
19
- b) Others as <a href="https://codex.wordpress.org/Editing_wp-config.php#WordPress_Upgrade_Constants">detailed in the official WP codex</a>
20
 
21
  This plugin is offered and maintained as a free service to the WP community. You might also be interested in enhancing your WordPress site with our other top plugins, below.
22
 
@@ -35,10 +35,14 @@ This plugin is offered and maintained as a free service to the WP community. You
35
 
36
  a) `define('FS_METHOD', 'ssh2');`
37
 
38
- b) Others as <a href="https://codex.wordpress.org/Editing_wp-config.php#WordPress_Upgrade_Constants">detailed in the official WP codex</a> or various other articles (Google for things like WordPress updates via SFTP).
39
 
40
  == Changelog ==
41
 
 
 
 
 
42
  = 0.8.4 - 2020/Dec/30 =
43
 
44
  * TWEAK: Remove obsolete references to other plugins
@@ -124,4 +128,4 @@ b) Others as <a href="https://codex.wordpress.org/Editing_wp-config.php#WordPres
124
  * Initial Release
125
 
126
  == Upgrade Notice ==
127
- * 0.8.4 : Remove obsolete references to other plugins
4
  Tags: ssh, sftp
5
  Requires at least: 3.1
6
  Tested up to: 6.1
7
+ Stable tag: 0.8.5
8
 
9
  "SSH SFTP Updater Support" is the easiest way to keep your WordPress installation up-to-date with SFTP.
10
 
16
 
17
  a) `define('FS_METHOD', 'ssh2');`
18
 
19
+ b) Others as <a href="https://developer.wordpress.org/apis/wp-config-php/#wordpress-upgrade-constants">detailed in the official WP codex</a>
20
 
21
  This plugin is offered and maintained as a free service to the WP community. You might also be interested in enhancing your WordPress site with our other top plugins, below.
22
 
35
 
36
  a) `define('FS_METHOD', 'ssh2');`
37
 
38
+ b) Others as <a href="https://developer.wordpress.org/apis/wp-config-php/#wordpress-upgrade-constants">detailed in the official WP codex</a> or various other articles (Google for things like WordPress updates via SFTP).
39
 
40
  == Changelog ==
41
 
42
+ = 0.8.5 - 2022/Dec/08 =
43
+
44
+ * TWEAK: Update URL reference to current location
45
+
46
  = 0.8.4 - 2020/Dec/30 =
47
 
48
  * TWEAK: Remove obsolete references to other plugins
128
  * Initial Release
129
 
130
  == Upgrade Notice ==
131
+ * 0.8.5 : Update URL reference to current location
sftp.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: SSH SFTP Updater Support
4
  Plugin URI: https://wordpress.org/plugins/ssh-sftp-updater-support/
5
  Description: Update your WordPress blog / plugins via SFTP without libssh2
6
- Version: 0.8.4
7
  Author: TerraFrost, David Anderson + Team Updraft
8
  Author URI: https://updraftplus.com/
9
  */
@@ -12,7 +12,7 @@ if (!defined('ABSPATH')) die('No direct access allowed');
12
 
13
  define('SSH_SFTP_UPDATER_SUPPORT_MAIN_PATH', plugin_dir_path(__FILE__));
14
  define('SSH_SFTP_UPDATER_SUPPORT_BASENAME', plugin_basename(__FILE__));
15
- define('SSH_SFTP_UPDATER_SUPPORT_VERSION', '0.8.4');
16
  define('SSH_SFTP_UPDATER_SUPPORT_URL', plugin_dir_url(__FILE__));
17
  // see http://adambrown.info/p/wp_hooks/hook/<filter name>
18
  add_filter('filesystem_method', 'phpseclib_filesystem_method', 10, 2); // since 2.6 - WordPress will ignore the ssh option if the php ssh extension is not loaded
3
  Plugin Name: SSH SFTP Updater Support
4
  Plugin URI: https://wordpress.org/plugins/ssh-sftp-updater-support/
5
  Description: Update your WordPress blog / plugins via SFTP without libssh2
6
+ Version: 0.8.5
7
  Author: TerraFrost, David Anderson + Team Updraft
8
  Author URI: https://updraftplus.com/
9
  */
12
 
13
  define('SSH_SFTP_UPDATER_SUPPORT_MAIN_PATH', plugin_dir_path(__FILE__));
14
  define('SSH_SFTP_UPDATER_SUPPORT_BASENAME', plugin_basename(__FILE__));
15
+ define('SSH_SFTP_UPDATER_SUPPORT_VERSION', '0.8.5');
16
  define('SSH_SFTP_UPDATER_SUPPORT_URL', plugin_dir_url(__FILE__));
17
  // see http://adambrown.info/p/wp_hooks/hook/<filter name>
18
  add_filter('filesystem_method', 'phpseclib_filesystem_method', 10, 2); // since 2.6 - WordPress will ignore the ssh option if the php ssh extension is not loaded