abiquo-installer-tests 20120104 → 20121023.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (265) hide show
  1. data/Rakefile +11 -7
  2. data/bin/abiquo-installer-tests +68 -43
  3. data/tests/1.8.5/abiquo_ciab.rb +21 -0
  4. data/tests/1.8.5/abiquo_kvm.rb +25 -0
  5. data/tests/1.8.5/abiquo_lvmiscsi.rb +17 -0
  6. data/tests/1.8.5/abiquo_monolithic.rb +12 -0
  7. data/tests/1.8.5/abiquo_nfs_repository.rb +23 -0
  8. data/tests/1.8.5/abiquo_platform.rb +39 -0
  9. data/tests/1.8.5/abiquo_remote_services.rb +115 -0
  10. data/tests/1.8.5/abiquo_server.rb +63 -0
  11. data/tests/1.8.5/abiquo_v2v.rb +37 -0
  12. data/tests/1.8.5/abiquo_vbox.rb +27 -0
  13. data/tests/1.8.5/abiquo_xen.rb +27 -0
  14. data/tests/2.0/abiquo_ciab.rb +9 -0
  15. data/tests/2.0/abiquo_community_hypervisor.rb +52 -0
  16. data/tests/2.0/abiquo_kvm.rb +5 -23
  17. data/tests/2.0/abiquo_monolithic.rb +12 -1
  18. data/tests/2.0/abiquo_platform.rb +2 -2
  19. data/tests/2.0/abiquo_remote_services.rb +15 -38
  20. data/tests/2.0/abiquo_server.rb +7 -1
  21. data/tests/2.0/abiquo_v2v.rb +42 -0
  22. data/tests/2.0/abiquo_vbox.rb +5 -25
  23. data/tests/2.0/abiquo_xen.rb +5 -25
  24. data/tests/2.2.0/abiquo_ciab.rb +30 -0
  25. data/tests/2.2.0/abiquo_community_hypervisor.rb +52 -0
  26. data/tests/2.2.0/abiquo_kvm.rb +7 -0
  27. data/tests/2.2.0/abiquo_lvmiscsi.rb +17 -0
  28. data/tests/2.2.0/abiquo_monolithic.rb +23 -0
  29. data/tests/2.2.0/abiquo_nfs_repository.rb +23 -0
  30. data/tests/2.2.0/abiquo_platform.rb +35 -0
  31. data/tests/2.2.0/abiquo_remote_services.rb +96 -0
  32. data/tests/2.2.0/abiquo_server.rb +95 -0
  33. data/tests/2.2.0/abiquo_v2v.rb +79 -0
  34. data/tests/2.2.0/abiquo_vbox.rb +7 -0
  35. data/tests/2.2.0/abiquo_xen.rb +7 -0
  36. data/tests/2.3.0/abiquo_ciab.rb +30 -0
  37. data/tests/2.3.0/abiquo_community_hypervisor.rb +52 -0
  38. data/tests/2.3.0/abiquo_kvm.rb +7 -0
  39. data/tests/2.3.0/abiquo_lvmiscsi.rb +17 -0
  40. data/tests/2.3.0/abiquo_monolithic.rb +23 -0
  41. data/tests/2.3.0/abiquo_nfs_repository.rb +23 -0
  42. data/tests/2.3.0/abiquo_platform.rb +35 -0
  43. data/tests/2.3.0/abiquo_remote_services.rb +96 -0
  44. data/tests/2.3.0/abiquo_server.rb +95 -0
  45. data/tests/2.3.0/abiquo_v2v.rb +79 -0
  46. data/tests/2.3.0/abiquo_vbox.rb +7 -0
  47. data/tests/2.3.0/abiquo_xen.rb +7 -0
  48. data/tests/abiquo_postinst_test.rb +12 -1
  49. data/vendor/net-scp-1.0.4/CHANGELOG.rdoc +29 -0
  50. data/vendor/net-scp-1.0.4/Manifest +17 -0
  51. data/vendor/net-scp-1.0.4/README.rdoc +98 -0
  52. data/vendor/net-scp-1.0.4/Rakefile +83 -0
  53. data/vendor/net-scp-1.0.4/lib/net/scp.rb +432 -0
  54. data/vendor/net-scp-1.0.4/lib/net/scp/download.rb +150 -0
  55. data/vendor/net-scp-1.0.4/lib/net/scp/errors.rb +5 -0
  56. data/vendor/net-scp-1.0.4/lib/net/scp/upload.rb +142 -0
  57. data/vendor/net-scp-1.0.4/lib/net/scp/version.rb +18 -0
  58. data/vendor/net-scp-1.0.4/lib/uri/open-scp.rb +18 -0
  59. data/vendor/net-scp-1.0.4/lib/uri/scp.rb +35 -0
  60. data/vendor/net-scp-1.0.4/net-scp.gemspec +34 -0
  61. data/vendor/net-scp-1.0.4/setup.rb +1331 -0
  62. data/vendor/net-scp-1.0.4/test/common.rb +153 -0
  63. data/vendor/net-scp-1.0.4/test/test_all.rb +3 -0
  64. data/vendor/net-scp-1.0.4/test/test_download.rb +170 -0
  65. data/vendor/net-scp-1.0.4/test/test_scp.rb +60 -0
  66. data/vendor/net-scp-1.0.4/test/test_upload.rb +269 -0
  67. data/vendor/net-sftp-2.0.5/CHANGELOG.rdoc +49 -0
  68. data/vendor/net-sftp-2.0.5/Manifest +55 -0
  69. data/vendor/net-sftp-2.0.5/README.rdoc +96 -0
  70. data/vendor/net-sftp-2.0.5/Rakefile +30 -0
  71. data/vendor/net-sftp-2.0.5/lib/net/sftp.rb +70 -0
  72. data/vendor/net-sftp-2.0.5/lib/net/sftp/constants.rb +187 -0
  73. data/vendor/net-sftp-2.0.5/lib/net/sftp/errors.rb +39 -0
  74. data/vendor/net-sftp-2.0.5/lib/net/sftp/operations/dir.rb +93 -0
  75. data/vendor/net-sftp-2.0.5/lib/net/sftp/operations/download.rb +364 -0
  76. data/vendor/net-sftp-2.0.5/lib/net/sftp/operations/file.rb +176 -0
  77. data/vendor/net-sftp-2.0.5/lib/net/sftp/operations/file_factory.rb +60 -0
  78. data/vendor/net-sftp-2.0.5/lib/net/sftp/operations/upload.rb +387 -0
  79. data/vendor/net-sftp-2.0.5/lib/net/sftp/packet.rb +21 -0
  80. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol.rb +32 -0
  81. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/01/attributes.rb +315 -0
  82. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/01/base.rb +268 -0
  83. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/01/name.rb +43 -0
  84. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/02/base.rb +31 -0
  85. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/03/base.rb +35 -0
  86. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/04/attributes.rb +152 -0
  87. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/04/base.rb +94 -0
  88. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/04/name.rb +67 -0
  89. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/05/base.rb +66 -0
  90. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/06/attributes.rb +107 -0
  91. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/06/base.rb +63 -0
  92. data/vendor/net-sftp-2.0.5/lib/net/sftp/protocol/base.rb +50 -0
  93. data/vendor/net-sftp-2.0.5/lib/net/sftp/request.rb +91 -0
  94. data/vendor/net-sftp-2.0.5/lib/net/sftp/response.rb +76 -0
  95. data/vendor/net-sftp-2.0.5/lib/net/sftp/session.rb +952 -0
  96. data/vendor/net-sftp-2.0.5/lib/net/sftp/version.rb +18 -0
  97. data/vendor/net-sftp-2.0.5/net-sftp.gemspec +34 -0
  98. data/vendor/net-sftp-2.0.5/setup.rb +1331 -0
  99. data/vendor/net-sftp-2.0.5/test/common.rb +172 -0
  100. data/vendor/net-sftp-2.0.5/test/protocol/01/test_attributes.rb +97 -0
  101. data/vendor/net-sftp-2.0.5/test/protocol/01/test_base.rb +210 -0
  102. data/vendor/net-sftp-2.0.5/test/protocol/01/test_name.rb +27 -0
  103. data/vendor/net-sftp-2.0.5/test/protocol/02/test_base.rb +26 -0
  104. data/vendor/net-sftp-2.0.5/test/protocol/03/test_base.rb +27 -0
  105. data/vendor/net-sftp-2.0.5/test/protocol/04/test_attributes.rb +148 -0
  106. data/vendor/net-sftp-2.0.5/test/protocol/04/test_base.rb +74 -0
  107. data/vendor/net-sftp-2.0.5/test/protocol/04/test_name.rb +53 -0
  108. data/vendor/net-sftp-2.0.5/test/protocol/05/test_base.rb +62 -0
  109. data/vendor/net-sftp-2.0.5/test/protocol/06/test_attributes.rb +124 -0
  110. data/vendor/net-sftp-2.0.5/test/protocol/06/test_base.rb +51 -0
  111. data/vendor/net-sftp-2.0.5/test/protocol/test_base.rb +42 -0
  112. data/vendor/net-sftp-2.0.5/test/test_all.rb +7 -0
  113. data/vendor/net-sftp-2.0.5/test/test_dir.rb +47 -0
  114. data/vendor/net-sftp-2.0.5/test/test_download.rb +252 -0
  115. data/vendor/net-sftp-2.0.5/test/test_file.rb +159 -0
  116. data/vendor/net-sftp-2.0.5/test/test_file_factory.rb +48 -0
  117. data/vendor/net-sftp-2.0.5/test/test_packet.rb +9 -0
  118. data/vendor/net-sftp-2.0.5/test/test_protocol.rb +17 -0
  119. data/vendor/net-sftp-2.0.5/test/test_request.rb +71 -0
  120. data/vendor/net-sftp-2.0.5/test/test_response.rb +53 -0
  121. data/vendor/net-sftp-2.0.5/test/test_session.rb +741 -0
  122. data/vendor/net-sftp-2.0.5/test/test_upload.rb +219 -0
  123. data/vendor/net-ssh-2.5.2/CHANGELOG.rdoc +295 -0
  124. data/vendor/net-ssh-2.5.2/LICENSE.rdoc +19 -0
  125. data/vendor/net-ssh-2.5.2/Manifest +132 -0
  126. data/vendor/net-ssh-2.5.2/README.rdoc +184 -0
  127. data/vendor/net-ssh-2.5.2/Rakefile +88 -0
  128. data/vendor/net-ssh-2.5.2/Rudyfile +96 -0
  129. data/vendor/net-ssh-2.5.2/THANKS.rdoc +19 -0
  130. data/vendor/net-ssh-2.5.2/lib/net/ssh.rb +223 -0
  131. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/agent.rb +23 -0
  132. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/agent/java_pageant.rb +85 -0
  133. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/agent/socket.rb +170 -0
  134. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/constants.rb +18 -0
  135. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/key_manager.rb +253 -0
  136. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/methods/abstract.rb +60 -0
  137. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/methods/hostbased.rb +75 -0
  138. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/methods/keyboard_interactive.rb +70 -0
  139. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/methods/password.rb +43 -0
  140. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/methods/publickey.rb +96 -0
  141. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/pageant.rb +264 -0
  142. data/vendor/net-ssh-2.5.2/lib/net/ssh/authentication/session.rb +154 -0
  143. data/vendor/net-ssh-2.5.2/lib/net/ssh/buffer.rb +350 -0
  144. data/vendor/net-ssh-2.5.2/lib/net/ssh/buffered_io.rb +198 -0
  145. data/vendor/net-ssh-2.5.2/lib/net/ssh/config.rb +207 -0
  146. data/vendor/net-ssh-2.5.2/lib/net/ssh/connection/channel.rb +630 -0
  147. data/vendor/net-ssh-2.5.2/lib/net/ssh/connection/constants.rb +33 -0
  148. data/vendor/net-ssh-2.5.2/lib/net/ssh/connection/session.rb +597 -0
  149. data/vendor/net-ssh-2.5.2/lib/net/ssh/connection/term.rb +178 -0
  150. data/vendor/net-ssh-2.5.2/lib/net/ssh/errors.rb +88 -0
  151. data/vendor/net-ssh-2.5.2/lib/net/ssh/key_factory.rb +107 -0
  152. data/vendor/net-ssh-2.5.2/lib/net/ssh/known_hosts.rb +141 -0
  153. data/vendor/net-ssh-2.5.2/lib/net/ssh/loggable.rb +61 -0
  154. data/vendor/net-ssh-2.5.2/lib/net/ssh/packet.rb +102 -0
  155. data/vendor/net-ssh-2.5.2/lib/net/ssh/prompt.rb +93 -0
  156. data/vendor/net-ssh-2.5.2/lib/net/ssh/proxy/command.rb +75 -0
  157. data/vendor/net-ssh-2.5.2/lib/net/ssh/proxy/errors.rb +14 -0
  158. data/vendor/net-ssh-2.5.2/lib/net/ssh/proxy/http.rb +94 -0
  159. data/vendor/net-ssh-2.5.2/lib/net/ssh/proxy/socks4.rb +70 -0
  160. data/vendor/net-ssh-2.5.2/lib/net/ssh/proxy/socks5.rb +142 -0
  161. data/vendor/net-ssh-2.5.2/lib/net/ssh/ruby_compat.rb +51 -0
  162. data/vendor/net-ssh-2.5.2/lib/net/ssh/service/forward.rb +298 -0
  163. data/vendor/net-ssh-2.5.2/lib/net/ssh/test.rb +89 -0
  164. data/vendor/net-ssh-2.5.2/lib/net/ssh/test/channel.rb +129 -0
  165. data/vendor/net-ssh-2.5.2/lib/net/ssh/test/extensions.rb +152 -0
  166. data/vendor/net-ssh-2.5.2/lib/net/ssh/test/kex.rb +44 -0
  167. data/vendor/net-ssh-2.5.2/lib/net/ssh/test/local_packet.rb +51 -0
  168. data/vendor/net-ssh-2.5.2/lib/net/ssh/test/packet.rb +81 -0
  169. data/vendor/net-ssh-2.5.2/lib/net/ssh/test/remote_packet.rb +38 -0
  170. data/vendor/net-ssh-2.5.2/lib/net/ssh/test/script.rb +157 -0
  171. data/vendor/net-ssh-2.5.2/lib/net/ssh/test/socket.rb +64 -0
  172. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/algorithms.rb +407 -0
  173. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/cipher_factory.rb +106 -0
  174. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/constants.rb +32 -0
  175. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/ctr.rb +95 -0
  176. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac.rb +45 -0
  177. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/abstract.rb +79 -0
  178. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/md5.rb +12 -0
  179. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/md5_96.rb +11 -0
  180. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/none.rb +15 -0
  181. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/ripemd160.rb +13 -0
  182. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/sha1.rb +13 -0
  183. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/sha1_96.rb +11 -0
  184. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/sha2_256.rb +15 -0
  185. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/sha2_256_96.rb +13 -0
  186. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/sha2_512.rb +14 -0
  187. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/hmac/sha2_512_96.rb +13 -0
  188. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/identity_cipher.rb +55 -0
  189. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/kex.rb +28 -0
  190. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +44 -0
  191. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +216 -0
  192. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +80 -0
  193. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +15 -0
  194. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +93 -0
  195. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +13 -0
  196. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +13 -0
  197. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/key_expander.rb +26 -0
  198. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/openssl.rb +237 -0
  199. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/packet_stream.rb +235 -0
  200. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/server_version.rb +71 -0
  201. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/session.rb +278 -0
  202. data/vendor/net-ssh-2.5.2/lib/net/ssh/transport/state.rb +206 -0
  203. data/vendor/net-ssh-2.5.2/lib/net/ssh/verifiers/lenient.rb +30 -0
  204. data/vendor/net-ssh-2.5.2/lib/net/ssh/verifiers/null.rb +12 -0
  205. data/vendor/net-ssh-2.5.2/lib/net/ssh/verifiers/strict.rb +53 -0
  206. data/vendor/net-ssh-2.5.2/lib/net/ssh/version.rb +62 -0
  207. data/vendor/net-ssh-2.5.2/net-ssh.gemspec +172 -0
  208. data/vendor/net-ssh-2.5.2/setup.rb +1585 -0
  209. data/vendor/net-ssh-2.5.2/support/arcfour_check.rb +20 -0
  210. data/vendor/net-ssh-2.5.2/support/ssh_tunnel_bug.rb +65 -0
  211. data/vendor/net-ssh-2.5.2/test/README.txt +43 -0
  212. data/vendor/net-ssh-2.5.2/test/authentication/methods/common.rb +28 -0
  213. data/vendor/net-ssh-2.5.2/test/authentication/methods/test_abstract.rb +51 -0
  214. data/vendor/net-ssh-2.5.2/test/authentication/methods/test_hostbased.rb +114 -0
  215. data/vendor/net-ssh-2.5.2/test/authentication/methods/test_keyboard_interactive.rb +100 -0
  216. data/vendor/net-ssh-2.5.2/test/authentication/methods/test_password.rb +52 -0
  217. data/vendor/net-ssh-2.5.2/test/authentication/methods/test_publickey.rb +148 -0
  218. data/vendor/net-ssh-2.5.2/test/authentication/test_agent.rb +205 -0
  219. data/vendor/net-ssh-2.5.2/test/authentication/test_key_manager.rb +218 -0
  220. data/vendor/net-ssh-2.5.2/test/authentication/test_session.rb +106 -0
  221. data/vendor/net-ssh-2.5.2/test/common.rb +107 -0
  222. data/vendor/net-ssh-2.5.2/test/configs/eqsign +3 -0
  223. data/vendor/net-ssh-2.5.2/test/configs/exact_match +8 -0
  224. data/vendor/net-ssh-2.5.2/test/configs/host_plus +10 -0
  225. data/vendor/net-ssh-2.5.2/test/configs/multihost +4 -0
  226. data/vendor/net-ssh-2.5.2/test/configs/nohost +19 -0
  227. data/vendor/net-ssh-2.5.2/test/configs/numeric_host +4 -0
  228. data/vendor/net-ssh-2.5.2/test/configs/wild_cards +14 -0
  229. data/vendor/net-ssh-2.5.2/test/connection/test_channel.rb +467 -0
  230. data/vendor/net-ssh-2.5.2/test/connection/test_session.rb +488 -0
  231. data/vendor/net-ssh-2.5.2/test/known_hosts/github +1 -0
  232. data/vendor/net-ssh-2.5.2/test/manual/test_forward.rb +223 -0
  233. data/vendor/net-ssh-2.5.2/test/start/test_transport.rb +28 -0
  234. data/vendor/net-ssh-2.5.2/test/test_all.rb +9 -0
  235. data/vendor/net-ssh-2.5.2/test/test_buffer.rb +426 -0
  236. data/vendor/net-ssh-2.5.2/test/test_buffered_io.rb +63 -0
  237. data/vendor/net-ssh-2.5.2/test/test_config.rb +120 -0
  238. data/vendor/net-ssh-2.5.2/test/test_key_factory.rb +121 -0
  239. data/vendor/net-ssh-2.5.2/test/test_known_hosts.rb +13 -0
  240. data/vendor/net-ssh-2.5.2/test/transport/hmac/test_md5.rb +39 -0
  241. data/vendor/net-ssh-2.5.2/test/transport/hmac/test_md5_96.rb +25 -0
  242. data/vendor/net-ssh-2.5.2/test/transport/hmac/test_none.rb +34 -0
  243. data/vendor/net-ssh-2.5.2/test/transport/hmac/test_ripemd160.rb +34 -0
  244. data/vendor/net-ssh-2.5.2/test/transport/hmac/test_sha1.rb +34 -0
  245. data/vendor/net-ssh-2.5.2/test/transport/hmac/test_sha1_96.rb +25 -0
  246. data/vendor/net-ssh-2.5.2/test/transport/hmac/test_sha2_256.rb +35 -0
  247. data/vendor/net-ssh-2.5.2/test/transport/hmac/test_sha2_256_96.rb +25 -0
  248. data/vendor/net-ssh-2.5.2/test/transport/hmac/test_sha2_512.rb +35 -0
  249. data/vendor/net-ssh-2.5.2/test/transport/hmac/test_sha2_512_96.rb +25 -0
  250. data/vendor/net-ssh-2.5.2/test/transport/kex/test_diffie_hellman_group14_sha1.rb +13 -0
  251. data/vendor/net-ssh-2.5.2/test/transport/kex/test_diffie_hellman_group1_sha1.rb +146 -0
  252. data/vendor/net-ssh-2.5.2/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +92 -0
  253. data/vendor/net-ssh-2.5.2/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +33 -0
  254. data/vendor/net-ssh-2.5.2/test/transport/kex/test_ecdh_sha2_nistp256.rb +161 -0
  255. data/vendor/net-ssh-2.5.2/test/transport/kex/test_ecdh_sha2_nistp384.rb +37 -0
  256. data/vendor/net-ssh-2.5.2/test/transport/kex/test_ecdh_sha2_nistp521.rb +37 -0
  257. data/vendor/net-ssh-2.5.2/test/transport/test_algorithms.rb +330 -0
  258. data/vendor/net-ssh-2.5.2/test/transport/test_cipher_factory.rb +441 -0
  259. data/vendor/net-ssh-2.5.2/test/transport/test_hmac.rb +34 -0
  260. data/vendor/net-ssh-2.5.2/test/transport/test_identity_cipher.rb +40 -0
  261. data/vendor/net-ssh-2.5.2/test/transport/test_packet_stream.rb +1745 -0
  262. data/vendor/net-ssh-2.5.2/test/transport/test_server_version.rb +78 -0
  263. data/vendor/net-ssh-2.5.2/test/transport/test_session.rb +315 -0
  264. data/vendor/net-ssh-2.5.2/test/transport/test_state.rb +179 -0
  265. metadata +325 -78
@@ -0,0 +1,107 @@
1
+ require 'net/sftp/protocol/04/attributes'
2
+
3
+ module Net; module SFTP; module Protocol; module V06
4
+
5
+ # A class representing the attributes of a file or directory on the server.
6
+ # It may be used to specify new attributes, or to query existing attributes.
7
+ # This particular class is specific to versions 6 and higher of the SFTP
8
+ # protocol.
9
+ #
10
+ # To specify new attributes, just pass a hash as the argument to the
11
+ # constructor. The following keys are supported:
12
+ #
13
+ # * :type:: the type of the item (integer, one of the T_ constants)
14
+ # * :size:: the size of the item (integer)
15
+ # * :allocation_size:: the actual number of bytes that the item uses on disk (integer)
16
+ # * :uid:: the user-id that owns the file (integer)
17
+ # * :gid:: the group-id that owns the file (integer)
18
+ # * :owner:: the name of the user that owns the file (string)
19
+ # * :group:: the name of the group that owns the file (string)
20
+ # * :permissions:: the permissions on the file (integer, e.g. 0755)
21
+ # * :atime:: the access time of the file (integer, seconds since epoch)
22
+ # * :atime_nseconds:: the nanosecond component of atime (integer)
23
+ # * :createtime:: the time at which the file was created (integer, seconds since epoch)
24
+ # * :createtime_nseconds:: the nanosecond component of createtime (integer)
25
+ # * :mtime:: the modification time of the file (integer, seconds since epoch)
26
+ # * :mtime_nseconds:: the nanosecond component of mtime (integer)
27
+ # * :ctime:: the time that the file's attributes were last changed (integer)
28
+ # * :ctime_nseconds:: the nanosecond component of ctime (integer)
29
+ # * :acl:: an array of ACL entries for the item
30
+ # * :attrib_bits:: other attributes of the file or directory (as a bit field) (integer)
31
+ # * :attrib_bits_valid:: a mask describing which bits in attrib_bits are valid (integer)
32
+ # * :text_hint:: whether the file may or may not contain textual data (integer)
33
+ # * :mime_type:: the mime type of the file (string)
34
+ # * :link_count:: the hard link count of the file (integer)
35
+ # * :untranslated_name:: the value of the filename before filename translation was attempted (string)
36
+ # * :extended:: a hash of name/value pairs identifying extended info
37
+ #
38
+ # Likewise, when the server sends an Attributes object, all of the
39
+ # above attributes are exposed as methods (though not all will be set with
40
+ # non-nil values from the server).
41
+ class Attributes < V04::Attributes
42
+ F_BITS = 0x00000200
43
+ F_ALLOCATION_SIZE = 0x00000400
44
+ F_TEXT_HINT = 0x00000800
45
+ F_MIME_TYPE = 0x00001000
46
+ F_LINK_COUNT = 0x00002000
47
+ F_UNTRANSLATED_NAME = 0x00004000
48
+ F_CTIME = 0x00008000
49
+
50
+ # The array of elements that describe this structure, in order. Used when
51
+ # parsing and serializing attribute objects.
52
+ def self.elements #:nodoc:
53
+ @elements ||= [
54
+ [:type, :byte, 0],
55
+ [:size, :int64, F_SIZE],
56
+ [:allocation_size, :int64, F_ALLOCATION_SIZE],
57
+ [:owner, :string, F_OWNERGROUP],
58
+ [:group, :string, F_OWNERGROUP],
59
+ [:permissions, :long, F_PERMISSIONS],
60
+ [:atime, :int64, F_ACCESSTIME],
61
+ [:atime_nseconds, :long, F_ACCESSTIME | F_SUBSECOND_TIMES],
62
+ [:createtime, :int64, F_CREATETIME],
63
+ [:createtime_nseconds, :long, F_CREATETIME | F_SUBSECOND_TIMES],
64
+ [:mtime, :int64, F_MODIFYTIME],
65
+ [:mtime_nseconds, :long, F_MODIFYTIME | F_SUBSECOND_TIMES],
66
+ [:ctime, :int64, F_CTIME],
67
+ [:ctime_nseconds, :long, F_CTIME | F_SUBSECOND_TIMES],
68
+ [:acl, :special, F_ACL],
69
+ [:attrib_bits, :long, F_BITS],
70
+ [:attrib_bits_valid, :long, F_BITS],
71
+ [:text_hint, :byte, F_TEXT_HINT],
72
+ [:mime_type, :string, F_MIME_TYPE],
73
+ [:link_count, :long, F_LINK_COUNT],
74
+ [:untranslated_name, :string, F_UNTRANSLATED_NAME],
75
+ [:extended, :special, F_EXTENDED]
76
+ ]
77
+ end
78
+
79
+ # The size on-disk of the file
80
+ attr_accessor :allocation_size
81
+
82
+ # The time at which the file's attributes were last changed
83
+ attr_accessor :ctime
84
+
85
+ # The nanosecond component of #ctime
86
+ attr_accessor :ctime_nseconds
87
+
88
+ # Other attributes of this file or directory (as a bit field)
89
+ attr_accessor :attrib_bits
90
+
91
+ # A bit mask describing which bits in #attrib_bits are valid
92
+ attr_accessor :attrib_bits_valid
93
+
94
+ # Describes whether the file may or may not contain textual data
95
+ attr_accessor :text_hint
96
+
97
+ # The mime-type of the file
98
+ attr_accessor :mime_type
99
+
100
+ # The hard link count for the file
101
+ attr_accessor :link_count
102
+
103
+ # The value of the file name before filename translation was attempted
104
+ attr_accessor :untranslated_name
105
+ end
106
+
107
+ end; end; end; end
@@ -0,0 +1,63 @@
1
+ require 'net/sftp/protocol/05/base'
2
+ require 'net/sftp/protocol/06/attributes'
3
+
4
+ module Net; module SFTP; module Protocol; module V06
5
+
6
+ # Wraps the low-level SFTP calls for version 6 of the SFTP protocol.
7
+ #
8
+ # None of these protocol methods block--all of them return immediately,
9
+ # requiring the SSH event loop to be run while the server response is
10
+ # pending.
11
+ #
12
+ # You will almost certainly never need to use this driver directly. Please
13
+ # see Net::SFTP::Session for the recommended interface.
14
+ class Base < V05::Base
15
+
16
+ # Returns the protocol version implemented by this driver. (6, in this
17
+ # case)
18
+ def version
19
+ 6
20
+ end
21
+
22
+ # Sends a FXP_LINK packet to the server to request that a link be created
23
+ # at +new_link_path+, pointing to +existing_path+. If +symlink+ is true, a
24
+ # symbolic link will be created; otherwise a hard link will be created.
25
+ def link(new_link_path, existing_path, symlink)
26
+ send_request(FXP_LINK, :string, new_link_path, :string, existing_path, :bool, symlink)
27
+ end
28
+
29
+ # Provided for backwards compatibility; v6 of the SFTP protocol removes the
30
+ # older FXP_SYMLINK packet type, so this method simply calls the #link
31
+ # method.
32
+ def symlink(path, target)
33
+ link(path, target, true)
34
+ end
35
+
36
+ # Sends a FXP_BLOCK packet to the server to request that a byte-range lock
37
+ # be obtained on the given +handle+, for the given byte +offset+ and
38
+ # +length+. The +mask+ parameter is a bitfield indicating what kind of
39
+ # lock to acquire, and must be a combination of one or more of the
40
+ # Net::SFTP::Constants::LockTypes constants.
41
+ def block(handle, offset, length, mask)
42
+ send_request(FXP_BLOCK, :string, handle, :int64, offset, :int64, length, :long, mask)
43
+ end
44
+
45
+ # Sends a FXP_UNBLOCK packet to the server to request that a previously
46
+ # acquired byte-range lock be released on the given +handle+, for the
47
+ # given byte +offset+ and +length+. The +handle+, +offset+, and +length+
48
+ # must all exactly match the parameters that were given when the lock was
49
+ # originally acquired (see #block).
50
+ def unblock(handle, offset, length)
51
+ send_request(FXP_UNBLOCK, :string, handle, :int64, offset, :int64, length)
52
+ end
53
+
54
+ protected
55
+
56
+ # Returns the Attributes class used by this version of the protocol
57
+ # (Net::SFTP::Protocol::V06::Attributes, in this case)
58
+ def attribute_factory
59
+ V06::Attributes
60
+ end
61
+ end
62
+
63
+ end; end; end; end
@@ -0,0 +1,50 @@
1
+ require 'net/ssh/loggable'
2
+ require 'net/sftp/constants'
3
+
4
+ module Net; module SFTP; module Protocol
5
+
6
+ # The abstract superclass of the specific implementations for each supported
7
+ # SFTP protocol version. It implements general packet parsing logic, and
8
+ # provides a way for subclasses to send requests.
9
+ class Base
10
+ include Net::SSH::Loggable
11
+ include Net::SFTP::Constants
12
+ include Net::SFTP::Constants::PacketTypes
13
+
14
+ # The SFTP session object that acts as client to this protocol instance
15
+ attr_reader :session
16
+
17
+ # Create a new instance of a protocol driver, servicing the given session.
18
+ def initialize(session)
19
+ @session = session
20
+ self.logger = session.logger
21
+ @request_id_counter = -1
22
+ end
23
+
24
+ # Attept to parse the given packet. If the packet is of an unsupported
25
+ # type, an exception will be raised. Returns the parsed data as a hash
26
+ # (the keys in the hash are packet-type specific).
27
+ def parse(packet)
28
+ case packet.type
29
+ when FXP_STATUS then parse_status_packet(packet)
30
+ when FXP_HANDLE then parse_handle_packet(packet)
31
+ when FXP_DATA then parse_data_packet(packet)
32
+ when FXP_NAME then parse_name_packet(packet)
33
+ when FXP_ATTRS then parse_attrs_packet(packet)
34
+ else raise NotImplementedError, "unknown packet type: #{packet.type}"
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ # Send a new packet of the given type, and with the given data arguments.
41
+ # A new request identifier will be allocated to this request, and will
42
+ # be returned.
43
+ def send_request(type, *args)
44
+ @request_id_counter += 1
45
+ session.send_packet(type, :long, @request_id_counter, *args)
46
+ return @request_id_counter
47
+ end
48
+ end
49
+
50
+ end; end; end
@@ -0,0 +1,91 @@
1
+ require 'net/sftp/constants'
2
+ require 'net/sftp/response'
3
+
4
+ module Net; module SFTP
5
+
6
+ # Encapsulates a single active SFTP request. This is instantiated
7
+ # automatically by the Net::SFTP::Session class when an operation is
8
+ # executed.
9
+ #
10
+ # request = sftp.open("/path/to/file")
11
+ # puts request.pending? #-> true
12
+ # request.wait
13
+ # puts request.pending? #-> false
14
+ # result = request.response
15
+ class Request
16
+ include Constants::PacketTypes
17
+
18
+ # The Net::SFTP session object that is servicing this request
19
+ attr_reader :session
20
+
21
+ # The SFTP packet identifier for this request
22
+ attr_reader :id
23
+
24
+ # The type of this request (e.g., :open, :symlink, etc.)
25
+ attr_reader :type
26
+
27
+ # The callback (if any) associated with this request. When the response
28
+ # is recieved for this request, the callback will be invoked.
29
+ attr_reader :callback
30
+
31
+ # The hash of properties associated with this request. Properties allow
32
+ # programmers to associate arbitrary data with a request, making state
33
+ # machines richer.
34
+ attr_reader :properties
35
+
36
+ # The response that was received for this request (see Net::SFTP::Response)
37
+ attr_reader :response
38
+
39
+ # Instantiate a new Request object, serviced by the given +session+, and
40
+ # being of the given +type+. The +id+ is the packet identifier for this
41
+ # request.
42
+ def initialize(session, type, id, &callback) #:nodoc:
43
+ @session, @id, @type, @callback = session, id, type, callback
44
+ @response = nil
45
+ @properties = {}
46
+ end
47
+
48
+ # Returns the value of property with the given +key+. If +key+ is not a
49
+ # symbol, it will be converted to a symbol before lookup.
50
+ def [](key)
51
+ properties[key.to_sym]
52
+ end
53
+
54
+ # Sets the value of the property with name +key+ to +value+. If +key+ is
55
+ # not a symbol, it will be converted to a symbol before lookup.
56
+ def []=(key, value)
57
+ properties[key.to_sym] = value
58
+ end
59
+
60
+ # Returns +true+ if the request is still waiting for a response from the
61
+ # server, and +false+ otherwise. The SSH event loop must be run in order
62
+ # for a request to be processed; see #wait.
63
+ def pending?
64
+ session.pending_requests.key?(id)
65
+ end
66
+
67
+ # Waits (blocks) until the server responds to this packet. If prior
68
+ # SFTP packets were also pending, they will be processed as well (since
69
+ # SFTP packets are processed in the order in which they are received by
70
+ # the server). Returns the request object itself.
71
+ def wait
72
+ session.loop { pending? }
73
+ self
74
+ end
75
+
76
+ public # but not "published". Internal use only
77
+
78
+ # When the server responds to this request, the packet is passed to
79
+ # this method, which parses the packet and builds a Net::SFTP::Response
80
+ # object to encapsulate it. If a #callback has been provided for this
81
+ # request, the callback is invoked with the new response object.
82
+ def respond_to(packet) #:nodoc:
83
+ data = session.protocol.parse(packet)
84
+ data[:type] = packet.type
85
+ @response = Response.new(self, data)
86
+
87
+ callback.call(@response) if callback
88
+ end
89
+ end
90
+
91
+ end; end
@@ -0,0 +1,76 @@
1
+ require 'net/sftp/constants'
2
+
3
+ module Net; module SFTP
4
+
5
+ # Encapsulates a response from the remote server, to a specific client
6
+ # request. Response objects are passed as parameters to callbacks when you
7
+ # are performing asynchronous operations; when you call Net::SFTP::Request#wait,
8
+ # you can get the corresponding response object via Net::SFTP::Request#response.
9
+ #
10
+ # sftp.open("/path/to/file") do |response|
11
+ # p response.ok?
12
+ # p response[:handle]
13
+ # end
14
+ #
15
+ # sftp.loop
16
+ class Response
17
+ include Net::SFTP::Constants::StatusCodes
18
+
19
+ # The request object that this object is in response to
20
+ attr_reader :request
21
+
22
+ # A hash of request-specific data, such as a file handle or attribute information
23
+ attr_reader :data
24
+
25
+ # The numeric code, one of the FX_* constants
26
+ attr_reader :code
27
+
28
+ # The textual message for this response (possibly blank)
29
+ attr_reader :message
30
+
31
+ # Create a new Response object for the given Net::SFTP::Request instance,
32
+ # and with the given data. If there is no :code key in the data, the
33
+ # code is assumed to be FX_OK.
34
+ def initialize(request, data={}) #:nodoc:
35
+ @request, @data = request, data
36
+ @code, @message = data[:code] || FX_OK, data[:message]
37
+ end
38
+
39
+ # Retrieve the data item with the given +key+. The key is converted to a
40
+ # symbol before being used to lookup the value.
41
+ def [](key)
42
+ data[key.to_sym]
43
+ end
44
+
45
+ # Returns a textual description of this response, including the status
46
+ # code and name.
47
+ def to_s
48
+ if message && !message.empty? && message.downcase != MAP[code]
49
+ "#{message} (#{MAP[code]}, #{code})"
50
+ else
51
+ "#{MAP[code]} (#{code})"
52
+ end
53
+ end
54
+
55
+ alias :to_str :to_s
56
+
57
+ # Returns +true+ if the status code is FX_OK; +false+ otherwise.
58
+ def ok?
59
+ code == FX_OK
60
+ end
61
+
62
+ # Returns +true+ if the status code is FX_EOF; +false+ otherwise.
63
+ def eof?
64
+ code == FX_EOF
65
+ end
66
+
67
+ #--
68
+ MAP = constants.inject({}) do |memo, name|
69
+ next memo unless name =~ /^FX_(.*)/
70
+ memo[const_get(name)] = $1.downcase.tr("_", " ")
71
+ memo
72
+ end
73
+ #++
74
+ end
75
+
76
+ end; end
@@ -0,0 +1,952 @@
1
+ require 'net/ssh'
2
+ require 'net/sftp/constants'
3
+ require 'net/sftp/errors'
4
+ require 'net/sftp/protocol'
5
+ require 'net/sftp/request'
6
+ require 'net/sftp/operations/dir'
7
+ require 'net/sftp/operations/upload'
8
+ require 'net/sftp/operations/download'
9
+ require 'net/sftp/operations/file_factory'
10
+
11
+ module Net; module SFTP
12
+
13
+ # The Session class encapsulates a single SFTP channel on a Net::SSH
14
+ # connection. Instances of this class are what most applications will
15
+ # interact with most, as it provides access to both low-level (mkdir,
16
+ # rename, remove, symlink, etc.) and high-level (upload, download, etc.)
17
+ # SFTP operations.
18
+ #
19
+ # Although Session makes it easy to do SFTP operations serially, you can
20
+ # also set up multiple operations to be done in parallel, too, without
21
+ # needing to resort to threading. You merely need to fire off the requests,
22
+ # and then run the event loop until all of the requests have completed:
23
+ #
24
+ # handle1 = sftp.open!("/path/to/file1")
25
+ # handle2 = sftp.open!("/path/to/file2")
26
+ #
27
+ # r1 = sftp.read(handle1, 0, 1024)
28
+ # r2 = sftp.read(handle2, 0, 1024)
29
+ # sftp.loop { [r1, r2].any? { |r| r.pending? } }
30
+ #
31
+ # puts "chunk #1: #{r1.response[:data]}"
32
+ # puts "chunk #2: #{r2.response[:data]}"
33
+ #
34
+ # By passing blocks to the operations, you can set up powerful state
35
+ # machines, to fire off subsequent operations. In fact, the Net::SFTP::Operations::Upload
36
+ # and Net::SFTP::Operations::Download classes set up such state machines, so that
37
+ # multiple uploads and/or downloads can be running simultaneously.
38
+ #
39
+ # The convention with the names of the operations is as follows: if the method
40
+ # name ends with an exclamation mark, like #read!, it will be synchronous
41
+ # (e.g., it will block until the server responds). Methods without an
42
+ # exclamation mark (e.g. #read) are asynchronous, and return before the
43
+ # server has responded. You will need to make sure the SSH event loop is
44
+ # run in order to process these requests. (See #loop.)
45
+ class Session
46
+ include Net::SSH::Loggable
47
+ include Net::SFTP::Constants::PacketTypes
48
+
49
+ # The highest protocol version supported by the Net::SFTP library.
50
+ HIGHEST_PROTOCOL_VERSION_SUPPORTED = 6
51
+
52
+ # A reference to the Net::SSH session object that powers this SFTP session.
53
+ attr_reader :session
54
+
55
+ # The Net::SSH::Connection::Channel object that the SFTP session is being
56
+ # processed by.
57
+ attr_reader :channel
58
+
59
+ # The state of the SFTP connection. It will be :opening, :subsystem, :init,
60
+ # :open, or :closed.
61
+ attr_reader :state
62
+
63
+ # The protocol instance being used by this SFTP session. Useful for
64
+ # querying the protocol version in effect.
65
+ attr_reader :protocol
66
+
67
+ # The hash of pending requests. Any requests that have been sent and which
68
+ # the server has not yet responded to will be represented here.
69
+ attr_reader :pending_requests
70
+
71
+ # Creates a new Net::SFTP instance atop the given Net::SSH connection.
72
+ # This will return immediately, before the SFTP connection has been properly
73
+ # initialized. Once the connection is ready, the given block will be called.
74
+ # If you want to block until the connection has been initialized, try this:
75
+ #
76
+ # sftp = Net::SFTP::Session.new(ssh)
77
+ # sftp.loop { sftp.opening? }
78
+ def initialize(session, &block)
79
+ @session = session
80
+ @input = Net::SSH::Buffer.new
81
+ self.logger = session.logger
82
+ @state = :closed
83
+
84
+ connect(&block)
85
+ end
86
+
87
+ public # high-level SFTP operations
88
+
89
+ # Initiates an upload from +local+ to +remote+, asynchronously. This
90
+ # method will return a new Net::SFTP::Operations::Upload instance, and requires
91
+ # the event loop to be run in order for the upload to progress. See
92
+ # Net::SFTP::Operations::Upload for a full discussion of how this method can be
93
+ # used.
94
+ #
95
+ # uploader = sftp.upload("/local/path", "/remote/path")
96
+ # uploader.wait
97
+ def upload(local, remote, options={}, &block)
98
+ Operations::Upload.new(self, local, remote, options, &block)
99
+ end
100
+
101
+ # Identical to #upload, but blocks until the upload is complete.
102
+ def upload!(local, remote, options={}, &block)
103
+ upload(local, remote, options, &block).wait
104
+ end
105
+
106
+ # Initiates a download from +remote+ to +local+, asynchronously. This
107
+ # method will return a new Net::SFTP::Operations::Download instance, and requires
108
+ # that the event loop be run in order for the download to progress. See
109
+ # Net::SFTP::Operations::Download for a full discussion of hos this method can be
110
+ # used.
111
+ #
112
+ # download = sftp.download("/remote/path", "/local/path")
113
+ # download.wait
114
+ def download(remote, local, options={}, &block)
115
+ Operations::Download.new(self, local, remote, options, &block)
116
+ end
117
+
118
+ # Identical to #download, but blocks until the download is complete.
119
+ # If +local+ is omitted, downloads the file to an in-memory buffer
120
+ # and returns the result as a string; otherwise, returns the
121
+ # Net::SFTP::Operations::Download instance.
122
+ def download!(remote, local=nil, options={}, &block)
123
+ require 'stringio' unless defined?(StringIO)
124
+ destination = local || StringIO.new
125
+ result = download(remote, destination, options, &block).wait
126
+ local ? result : destination.string
127
+ end
128
+
129
+ # Returns an Net::SFTP::Operations::FileFactory instance, which can be used to
130
+ # mimic synchronous, IO-like file operations on a remote file via
131
+ # SFTP.
132
+ #
133
+ # sftp.file.open("/path/to/file") do |file|
134
+ # while line = file.gets
135
+ # puts line
136
+ # end
137
+ # end
138
+ #
139
+ # See Net::SFTP::Operations::FileFactory and Net::SFTP::Operations::File for more details.
140
+ def file
141
+ @file ||= Operations::FileFactory.new(self)
142
+ end
143
+
144
+ # Returns a Net::SFTP::Operations::Dir instance, which can be used to
145
+ # conveniently iterate over and search directories on the remote server.
146
+ #
147
+ # sftp.dir.glob("/base/path", "*/**/*.rb") do |entry|
148
+ # p entry.name
149
+ # end
150
+ #
151
+ # See Net::SFTP::Operations::Dir for a more detailed discussion of how
152
+ # to use this.
153
+ def dir
154
+ @dir ||= Operations::Dir.new(self)
155
+ end
156
+
157
+ public # low-level SFTP operations
158
+
159
+ # :call-seq:
160
+ # open(path, flags="r", options={}) -> request
161
+ # open(path, flags="r", options={}) { |response| ... } -> request
162
+ #
163
+ # Opens a file on the remote server. The +flags+ parameter determines
164
+ # how the flag is open, and accepts the same format as IO#open (e.g.,
165
+ # either a string like "r" or "w", or a combination of the IO constants).
166
+ # The +options+ parameter is a hash of attributes to be associated
167
+ # with the file, and varies greatly depending on the SFTP protocol
168
+ # version in use, but some (like :permissions) are always available.
169
+ #
170
+ # Returns immediately with a Request object. If a block is given, it will
171
+ # be invoked when the server responds, with a Response object as the only
172
+ # parameter. The :handle property of the response is the handle of the
173
+ # opened file, and may be passed to other methods (like #close, #read,
174
+ # #write, and so forth).
175
+ #
176
+ # sftp.open("/path/to/file") do |response|
177
+ # raise "fail!" unless response.ok?
178
+ # sftp.close(response[:handle])
179
+ # end
180
+ # sftp.loop
181
+ def open(path, flags="r", options={}, &callback)
182
+ request :open, path, flags, options, &callback
183
+ end
184
+
185
+ # Identical to #open, but blocks until the server responds. It will raise
186
+ # a StatusException if the request was unsuccessful. Otherwise, it will
187
+ # return the handle of the newly opened file.
188
+ #
189
+ # handle = sftp.open!("/path/to/file")
190
+ def open!(path, flags="r", options={}, &callback)
191
+ wait_for(open(path, flags, options, &callback), :handle)
192
+ end
193
+
194
+ # :call-seq:
195
+ # close(handle) -> request
196
+ # close(handle) { |response| ... } -> request
197
+ #
198
+ # Closes an open handle, whether obtained via #open, or #opendir. Returns
199
+ # immediately with a Request object. If a block is given, it will be
200
+ # invoked when the server responds.
201
+ #
202
+ # sftp.open("/path/to/file") do |response|
203
+ # raise "fail!" unless response.ok?
204
+ # sftp.close(response[:handle])
205
+ # end
206
+ # sftp.loop
207
+ def close(handle, &callback)
208
+ request :close, handle, &callback
209
+ end
210
+
211
+ # Identical to #close, but blocks until the server responds. It will
212
+ # raise a StatusException if the request was unsuccessful. Otherwise,
213
+ # it returns the Response object for this request.
214
+ #
215
+ # sftp.close!(handle)
216
+ def close!(handle, &callback)
217
+ wait_for(close(handle, &callback))
218
+ end
219
+
220
+ # :call-seq:
221
+ # read(handle, offset, length) -> request
222
+ # read(handle, offset, length) { |response| ... } -> request
223
+ #
224
+ # Requests that +length+ bytes, starting at +offset+ bytes from the
225
+ # beginning of the file, be read from the file identified by
226
+ # +handle+. (The +handle+ should be a value obtained via the #open
227
+ # method.) Returns immediately with a Request object. If a block is
228
+ # given, it will be invoked when the server responds.
229
+ #
230
+ # The :data property of the response will contain the requested data,
231
+ # assuming the call was successful.
232
+ #
233
+ # request = sftp.read(handle, 0, 1024) do |response|
234
+ # if response.eof?
235
+ # puts "end of file reached before reading any data"
236
+ # elsif !response.ok?
237
+ # puts "error (#{response})"
238
+ # else
239
+ # print(response[:data])
240
+ # end
241
+ # end
242
+ # request.wait
243
+ #
244
+ # To read an entire file will usually require multiple calls to #read,
245
+ # unless you know in advance how large the file is.
246
+ def read(handle, offset, length, &callback)
247
+ request :read, handle, offset, length, &callback
248
+ end
249
+
250
+ # Identical to #read, but blocks until the server responds. It will raise
251
+ # a StatusException if the request was unsuccessful. If the end of the file
252
+ # was reached, +nil+ will be returned. Otherwise, it returns the data that
253
+ # was read, as a String.
254
+ #
255
+ # data = sftp.read!(handle, 0, 1024)
256
+ def read!(handle, offset, length, &callback)
257
+ wait_for(read(handle, offset, length, &callback), :data)
258
+ end
259
+
260
+ # :call-seq:
261
+ # write(handle, offset, data) -> request
262
+ # write(handle, offset, data) { |response| ... } -> request
263
+ #
264
+ # Requests that +data+ be written to the file identified by +handle+,
265
+ # starting at +offset+ bytes from the start of the file. The file must
266
+ # have been opened for writing via #open. Returns immediately with a
267
+ # Request object. If a block is given, it will be invoked when the
268
+ # server responds.
269
+ #
270
+ # request = sftp.write(handle, 0, "hello, world!\n")
271
+ # request.wait
272
+ def write(handle, offset, data, &callback)
273
+ request :write, handle, offset, data, &callback
274
+ end
275
+
276
+ # Identical to #write, but blocks until the server responds. It will raise
277
+ # a StatusException if the request was unsuccessful, or the end of the file
278
+ # was reached. Otherwise, it returns the Response object for this request.
279
+ #
280
+ # sftp.write!(handle, 0, "hello, world!\n")
281
+ def write!(handle, offset, data, &callback)
282
+ wait_for(write(handle, offset, data, &callback))
283
+ end
284
+
285
+ # :call-seq:
286
+ # lstat(path, flags=nil) -> request
287
+ # lstat(path, flags=nil) { |response| ... } -> request
288
+ #
289
+ # This method is identical to the #stat method, with the exception that
290
+ # it will not follow symbolic links (thus allowing you to stat the
291
+ # link itself, rather than what it refers to). The +flags+ parameter
292
+ # is not used in SFTP protocol versions prior to 4, and will be ignored
293
+ # in those versions of the protocol that do not use it. For those that
294
+ # do, however, you may provide hints as to which file proprties you wish
295
+ # to query (e.g., if all you want is permissions, you could pass the
296
+ # Net::SFTP::Protocol::V04::Attributes::F_PERMISSIONS flag as the value
297
+ # for the +flags+ parameter).
298
+ #
299
+ # The method returns immediately with a Request object. If a block is given,
300
+ # it will be invoked when the server responds. The :attrs property of
301
+ # the response will contain an Attributes instance appropriate for the
302
+ # the protocol version (see Protocol::V01::Attributes, Protocol::V04::Attributes,
303
+ # and Protocol::V06::Attributes).
304
+ #
305
+ # request = sftp.lstat("/path/to/file") do |response|
306
+ # raise "fail!" unless response.ok?
307
+ # puts "permissions: %04o" % response[:attrs].permissions
308
+ # end
309
+ # request.wait
310
+ def lstat(path, flags=nil, &callback)
311
+ request :lstat, path, flags, &callback
312
+ end
313
+
314
+ # Identical to the #lstat method, but blocks until the server responds.
315
+ # It will raise a StatusException if the request was unsuccessful.
316
+ # Otherwise, it will return the attribute object describing the path.
317
+ #
318
+ # puts sftp.lstat!("/path/to/file").permissions
319
+ def lstat!(path, flags=nil, &callback)
320
+ wait_for(lstat(path, flags, &callback), :attrs)
321
+ end
322
+
323
+ # The fstat method is identical to the #stat and #lstat methods, with
324
+ # the exception that it takes a +handle+ as the first parameter, such
325
+ # as would be obtained via the #open or #opendir methods. (See the #lstat
326
+ # method for full documentation).
327
+ def fstat(handle, flags=nil, &callback)
328
+ request :fstat, handle, flags, &callback
329
+ end
330
+
331
+ # Identical to the #fstat method, but blocks until the server responds.
332
+ # It will raise a StatusException if the request was unsuccessful.
333
+ # Otherwise, it will return the attribute object describing the path.
334
+ #
335
+ # puts sftp.fstat!(handle).permissions
336
+ def fstat!(handle, flags=nil, &callback)
337
+ wait_for(fstat(handle, flags, &callback), :attrs)
338
+ end
339
+
340
+ # :call-seq:
341
+ # setstat(path, attrs) -> request
342
+ # setstat(path, attrs) { |response| ... } -> request
343
+ #
344
+ # This method may be used to set file metadata (such as permissions, or
345
+ # user/group information) on a remote file. The exact metadata that may
346
+ # be tweaked is dependent on the SFTP protocol version in use, but in
347
+ # general you may set at least the permissions, user, and group. (See
348
+ # Protocol::V01::Attributes, Protocol::V04::Attributes, and Protocol::V06::Attributes
349
+ # for the full lists of attributes that may be set for the different
350
+ # protocols.)
351
+ #
352
+ # The +attrs+ parameter is a hash, where the keys are symbols identifying
353
+ # the attributes to set.
354
+ #
355
+ # The method returns immediately with a Request object. If a block is given,
356
+ # it will be invoked when the server responds.
357
+ #
358
+ # request = sftp.setstat("/path/to/file", :permissions => 0644)
359
+ # request.wait
360
+ # puts "success: #{request.response.ok?}"
361
+ def setstat(path, attrs, &callback)
362
+ request :setstat, path, attrs, &callback
363
+ end
364
+
365
+ # Identical to the #setstat method, but blocks until the server responds.
366
+ # It will raise a StatusException if the request was unsuccessful.
367
+ # Otherwise, it will return the Response object for the request.
368
+ #
369
+ # sftp.setstat!("/path/to/file", :permissions => 0644)
370
+ def setstat!(path, attrs, &callback)
371
+ wait_for(setstat(path, attrs, &callback))
372
+ end
373
+
374
+ # The fsetstat method is identical to the #setstat method, with the
375
+ # exception that it takes a +handle+ as the first parameter, such as
376
+ # would be obtained via the #open or #opendir methods. (See the
377
+ # #setstat method for full documentation.)
378
+ def fsetstat(handle, attrs, &callback)
379
+ request :fsetstat, handle, attrs, &callback
380
+ end
381
+
382
+ # Identical to the #fsetstat method, but blocks until the server responds.
383
+ # It will raise a StatusException if the request was unsuccessful.
384
+ # Otherwise, it will return the Response object for the request.
385
+ #
386
+ # sftp.fsetstat!(handle, :permissions => 0644)
387
+ def fsetstat!(handle, attrs, &callback)
388
+ wait_for(fsetstat(handle, attrs, &callback))
389
+ end
390
+
391
+ # :call-seq:
392
+ # opendir(path) -> request
393
+ # opendir(path) { |response| ... } -> request
394
+ #
395
+ # Attempts to open a directory on the remote host for reading. Once the
396
+ # handle is obtained, directory entries may be retrieved using the
397
+ # #readdir method. The method returns immediately with a Request object.
398
+ # If a block is given, it will be invoked when the server responds.
399
+ #
400
+ # sftp.opendir("/path/to/directory") do |response|
401
+ # raise "fail!" unless response.ok?
402
+ # sftp.close(response[:handle])
403
+ # end
404
+ # sftp.loop
405
+ def opendir(path, &callback)
406
+ request :opendir, path, &callback
407
+ end
408
+
409
+ # Identical to #opendir, but blocks until the server responds. It will raise
410
+ # a StatusException if the request was unsuccessful. Otherwise, it will
411
+ # return a handle to the given path.
412
+ #
413
+ # handle = sftp.opendir!("/path/to/directory")
414
+ def opendir!(path, &callback)
415
+ wait_for(opendir(path, &callback), :handle)
416
+ end
417
+
418
+ # :call-seq:
419
+ # readdir(handle) -> request
420
+ # raeddir(handle) { |response| ... } -> request
421
+ #
422
+ # Reads a set of entries from the given directory handle (which must
423
+ # have been obtained via #opendir). If the response is EOF, then there
424
+ # are no more entries in the directory. Otherwise, the entries will be
425
+ # in the :names property of the response:
426
+ #
427
+ # loop do
428
+ # request = sftp.readdir(handle).wait
429
+ # break if request.response.eof?
430
+ # raise "fail!" unless request.response.ok?
431
+ # request.response[:names].each do |entry|
432
+ # puts entry.name
433
+ # end
434
+ # end
435
+ #
436
+ # See also Protocol::V01::Name and Protocol::V04::Name for the specific
437
+ # properties of each individual entry (which vary based on the SFTP
438
+ # protocol version in use).
439
+ def readdir(handle, &callback)
440
+ request :readdir, handle, &callback
441
+ end
442
+
443
+ # Identical to #readdir, but blocks until the server responds. It will raise
444
+ # a StatusException if the request was unsuccessful. Otherwise, it will
445
+ # return nil if there were no more names to read, or an array of name
446
+ # entries.
447
+ #
448
+ # while (entries = sftp.readdir!(handle)) do
449
+ # entries.each { |entry| puts(entry.name) }
450
+ # end
451
+ def readdir!(handle, &callback)
452
+ wait_for(readdir(handle, &callback), :names)
453
+ end
454
+
455
+ # :call-seq:
456
+ # remove(filename) -> request
457
+ # remove(filename) { |response| ... } -> request
458
+ #
459
+ # Attempts to remove the given file from the remote file system. Returns
460
+ # immediately with a Request object. If a block is given, the block will
461
+ # be invoked when the server responds, and will be passed a Response
462
+ # object.
463
+ #
464
+ # sftp.remove("/path/to/file").wait
465
+ def remove(filename, &callback)
466
+ request :remove, filename, &callback
467
+ end
468
+
469
+ # Identical to #remove, but blocks until the server responds. It will raise
470
+ # a StatusException if the request was unsuccessful. Otherwise, it will
471
+ # return the Response object for the request.
472
+ #
473
+ # sftp.remove!("/path/to/file")
474
+ def remove!(filename, &callback)
475
+ wait_for(remove(filename, &callback))
476
+ end
477
+
478
+ # :call-seq:
479
+ # mkdir(path, attrs={}) -> request
480
+ # mkdir(path, attrs={}) { |response| ... } -> request
481
+ #
482
+ # Creates the named directory on the remote server. If an attribute hash
483
+ # is given, it must map to the set of attributes supported by the version
484
+ # of the SFTP protocol in use. (See Protocol::V01::Attributes,
485
+ # Protocol::V04::Attributes, and Protocol::V06::Attributes.)
486
+ #
487
+ # sftp.mkdir("/path/to/directory", :permissions => 0550).wait
488
+ def mkdir(path, attrs={}, &callback)
489
+ request :mkdir, path, attrs, &callback
490
+ end
491
+
492
+ # Identical to #mkdir, but blocks until the server responds. It will raise
493
+ # a StatusException if the request was unsuccessful. Otherwise, it will
494
+ # return the Response object for the request.
495
+ #
496
+ # sftp.mkdir!("/path/to/directory", :permissions => 0550)
497
+ def mkdir!(path, attrs={}, &callback)
498
+ wait_for(mkdir(path, attrs, &callback))
499
+ end
500
+
501
+ # :call-seq:
502
+ # rmdir(path) -> request
503
+ # rmdir(path) { |response| ... } -> request
504
+ #
505
+ # Removes the named directory on the remote server. The directory must
506
+ # be empty before it can be removed.
507
+ #
508
+ # sftp.rmdir("/path/to/directory").wait
509
+ def rmdir(path, &callback)
510
+ request :rmdir, path, &callback
511
+ end
512
+
513
+ # Identical to #rmdir, but blocks until the server responds. It will raise
514
+ # a StatusException if the request was unsuccessful. Otherwise, it will
515
+ # return the Response object for the request.
516
+ #
517
+ # sftp.rmdir!("/path/to/directory")
518
+ def rmdir!(path, &callback)
519
+ wait_for(rmdir(path, &callback))
520
+ end
521
+
522
+ # :call-seq:
523
+ # realpath(path) -> request
524
+ # realpath(path) { |response| ... } -> request
525
+ #
526
+ # Tries to canonicalize the given path, turning any given path into an
527
+ # absolute path. This is primarily useful for converting a path with
528
+ # ".." or "." segments into an identical path without those segments.
529
+ # The answer will be in the response's :names attribute, as a
530
+ # one-element array.
531
+ #
532
+ # request = sftp.realpath("/path/../to/../directory").wait
533
+ # puts request[:names].first.name
534
+ def realpath(path, &callback)
535
+ request :realpath, path, &callback
536
+ end
537
+
538
+ # Identical to #realpath, but blocks until the server responds. It will raise
539
+ # a StatusException if the request was unsuccessful. Otherwise, it will
540
+ # return a name object identifying the path.
541
+ #
542
+ # puts(sftp.realpath!("/path/../to/../directory"))
543
+ def realpath!(path, &callback)
544
+ wait_for(realpath(path, &callback), :names).first
545
+ end
546
+
547
+ # Identical to the #lstat method, except that it follows symlinks
548
+ # (e.g., if you give it the path to a symlink, it will stat the target
549
+ # of the symlink rather than the symlink itself). See the #lstat method
550
+ # for full documentation.
551
+ def stat(path, flags=nil, &callback)
552
+ request :stat, path, flags, &callback
553
+ end
554
+
555
+ # Identical to #stat, but blocks until the server responds. It will raise
556
+ # a StatusException if the request was unsuccessful. Otherwise, it will
557
+ # return an attribute object for the named path.
558
+ #
559
+ # attrs = sftp.stat!("/path/to/file")
560
+ def stat!(path, flags=nil, &callback)
561
+ wait_for(stat(path, flags, &callback), :attrs)
562
+ end
563
+
564
+ # :call-seq:
565
+ # rename(name, new_name, flags=nil) -> request
566
+ # rename(name, new_name, flags=nil) { |response| ... } -> request
567
+ #
568
+ # Renames the given file. This operation is only available in SFTP
569
+ # protocol versions two and higher. The +flags+ parameter is ignored
570
+ # in versions prior to 5. In versions 5 and higher, the +flags+
571
+ # parameter can be used to specify how the rename should be performed
572
+ # (atomically, etc.).
573
+ #
574
+ # The following flags are defined in protocol version 5:
575
+ #
576
+ # * 0x0001 - overwrite an existing file if the new name specifies a file
577
+ # that already exists.
578
+ # * 0x0002 - perform the rewrite atomically.
579
+ # * 0x0004 - allow the server to perform the rename as it prefers.
580
+ def rename(name, new_name, flags=nil, &callback)
581
+ request :rename, name, new_name, flags, &callback
582
+ end
583
+
584
+ # Identical to #rename, but blocks until the server responds. It will raise
585
+ # a StatusException if the request was unsuccessful. Otherwise, it will
586
+ # return the Response object for the request.
587
+ #
588
+ # sftp.rename!("/path/to/old", "/path/to/new")
589
+ def rename!(name, new_name, flags=nil, &callback)
590
+ wait_for(rename(name, new_name, flags, &callback))
591
+ end
592
+
593
+ # :call-seq:
594
+ # readlink(path) -> request
595
+ # readlink(path) { |response| ... } -> request
596
+ #
597
+ # Queries the server for the target of the specified symbolic link.
598
+ # This operation is only available in protocol versions 3 and higher.
599
+ # The response to this request will include a names property, a one-element
600
+ # array naming the target of the symlink.
601
+ #
602
+ # request = sftp.readlink("/path/to/symlink").wait
603
+ # puts request.response[:names].first.name
604
+ def readlink(path, &callback)
605
+ request :readlink, path, &callback
606
+ end
607
+
608
+ # Identical to #readlink, but blocks until the server responds. It will raise
609
+ # a StatusException if the request was unsuccessful. Otherwise, it will
610
+ # return the Name object for the path that the symlink targets.
611
+ #
612
+ # item = sftp.readlink!("/path/to/symlink")
613
+ def readlink!(path, &callback)
614
+ wait_for(readlink(path, &callback), :names).first
615
+ end
616
+
617
+ # :call-seq:
618
+ # symlink(path, target) -> request
619
+ # symlink(path, target) { |response| ... } -> request
620
+ #
621
+ # Attempts to create a symlink to +path+ at +target+. This operation
622
+ # is only available in protocol versions 3, 4, and 5, but the Net::SFTP
623
+ # library mimics the symlink behavior in protocol version 6 using the
624
+ # #link method, so it is safe to use this method in protocol version 6.
625
+ #
626
+ # sftp.symlink("/path/to/file", "/path/to/symlink").wait
627
+ def symlink(path, target, &callback)
628
+ request :symlink, path, target, &callback
629
+ end
630
+
631
+ # Identical to #symlink, but blocks until the server responds. It will raise
632
+ # a StatusException if the request was unsuccessful. Otherwise, it will
633
+ # return the Response object for the request.
634
+ #
635
+ # sftp.symlink!("/path/to/file", "/path/to/symlink")
636
+ def symlink!(path, target, &callback)
637
+ wait_for(symlink(path, target, &callback))
638
+ end
639
+
640
+ # :call-seq:
641
+ # link(new_link_path, existing_path, symlink=true) -> request
642
+ # link(new_link_path, existing_path, symlink=true) { |response| ... } -> request
643
+ #
644
+ # Attempts to create a link, either hard or symbolic. This operation is
645
+ # only available in SFTP protocol versions 6 and higher. If the +symlink+
646
+ # paramter is true, a symbolic link will be created, otherwise a hard
647
+ # link will be created. The link will be named +new_link_path+, and will
648
+ # point to the path +existing_path+.
649
+ #
650
+ # sftp.link("/path/to/symlink", "/path/to/file", true).wait
651
+ #
652
+ # Note that #link is only available for SFTP protocol 6 and higher. You
653
+ # can use #symlink for protocols 3 and higher.
654
+ def link(new_link_path, existing_path, symlink=true, &callback)
655
+ request :link, new_link_path, existing_path, symlink, &callback
656
+ end
657
+
658
+ # Identical to #link, but blocks until the server responds. It will raise
659
+ # a StatusException if the request was unsuccessful. Otherwise, it will
660
+ # return the Response object for the request.
661
+ #
662
+ # sftp.link!("/path/to/symlink", "/path/to/file", true)
663
+ def link!(new_link_path, existing_path, symlink=true, &callback)
664
+ wait_for(link(new_link_path, existing_path, symlink, &callback))
665
+ end
666
+
667
+ # :call-seq:
668
+ # block(handle, offset, length, mask) -> request
669
+ # block(handle, offset, length, mask) { |response| ... } -> request
670
+ #
671
+ # Creates a byte-range lock on the file specified by the given +handle+.
672
+ # This operation is only available in SFTP protocol versions 6 and
673
+ # higher. The lock may be either mandatory or advisory.
674
+ #
675
+ # The +handle+ parameter is a file handle, as obtained by the #open method.
676
+ #
677
+ # The +offset+ and +length+ parameters describe the location and size of
678
+ # the byte range.
679
+ #
680
+ # The +mask+ describes how the lock should be defined, and consists of
681
+ # some combination of the following bit masks:
682
+ #
683
+ # * 0x0040 - Read lock. The byte range may not be accessed for reading
684
+ # by via any other handle, though it may be written to.
685
+ # * 0x0080 - Write lock. The byte range may not be written to via any
686
+ # other handle, though it may be read from.
687
+ # * 0x0100 - Delete lock. No other handle may delete this file.
688
+ # * 0x0200 - Advisory lock. The server need not honor the lock instruction.
689
+ #
690
+ # Once created, the lock may be removed via the #unblock method.
691
+ def block(handle, offset, length, mask, &callback)
692
+ request :block, handle, offset, length, mask, &callback
693
+ end
694
+
695
+ # Identical to #block, but blocks until the server responds. It will raise
696
+ # a StatusException if the request was unsuccessful. Otherwise, it will
697
+ # return the Response object for the request.
698
+ def block!(handle, offset, length, mask, &callback)
699
+ wait_for(block(handle, offset, length, mask, &callback))
700
+ end
701
+
702
+ # :call-seq:
703
+ # unblock(handle, offset, length) -> request
704
+ # unblock(handle, offset, length) { |response| ... } -> request
705
+ #
706
+ # Removes a previously created byte-range lock. This operation is only
707
+ # available in protocol versions 6 and higher. The +offset+ and +length+
708
+ # parameters must exactly match those that were given to #block when the
709
+ # lock was acquired.
710
+ def unblock(handle, offset, length, &callback)
711
+ request :unblock, handle, offset, length, &callback
712
+ end
713
+
714
+ # Identical to #unblock, but blocks until the server responds. It will raise
715
+ # a StatusException if the request was unsuccessful. Otherwise, it will
716
+ # return the Response object for the request.
717
+ def unblock!(handle, offset, length, &callback)
718
+ wait_for(unblock(handle, offset, length, &callback))
719
+ end
720
+
721
+ public # miscellaneous methods
722
+
723
+ # Closes the SFTP connection, but not the SSH connection. Blocks until the
724
+ # session has terminated. Once the session has terminated, further operations
725
+ # on this object will result in errors. You can reopen the SFTP session
726
+ # via the #connect method.
727
+ def close_channel
728
+ return unless open?
729
+ channel.close
730
+ loop { !closed? }
731
+ end
732
+
733
+ # Returns true if the connection has been initialized.
734
+ def open?
735
+ state == :open
736
+ end
737
+
738
+ # Returns true if the connection has been closed.
739
+ def closed?
740
+ state == :closed
741
+ end
742
+
743
+ # Returns true if the connection is in the process of being initialized
744
+ # (e.g., it is not closed, but is not yet fully open).
745
+ def opening?
746
+ !(open? || closed?)
747
+ end
748
+
749
+ # Attempts to establish an SFTP connection over the SSH session given when
750
+ # this object was instantiated. If the object is already open, this will
751
+ # simply execute the given block (if any), passing the SFTP session itself
752
+ # as argument. If the session is currently being opened, this will add
753
+ # the given block to the list of callbacks, to be executed when the session
754
+ # is fully open.
755
+ #
756
+ # This method does not block, and will return immediately. If you pass a
757
+ # block to it, that block will be invoked when the connection has been
758
+ # fully established. Thus, you can do something like this:
759
+ #
760
+ # sftp.connect do
761
+ # puts "open!"
762
+ # end
763
+ #
764
+ # If you just want to block until the connection is ready, see the #connect!
765
+ # method.
766
+ def connect(&block)
767
+ case state
768
+ when :open
769
+ block.call(self) if block
770
+ when :closed
771
+ @state = :opening
772
+ @channel = session.open_channel(&method(:when_channel_confirmed))
773
+ @packet_length = nil
774
+ @protocol = nil
775
+ @on_ready = Array(block)
776
+ else # opening
777
+ @on_ready << block if block
778
+ end
779
+
780
+ self
781
+ end
782
+
783
+ # Same as the #connect method, but blocks until the SFTP connection has
784
+ # been fully initialized.
785
+ def connect!(&block)
786
+ connect(&block)
787
+ loop { opening? }
788
+ self
789
+ end
790
+
791
+ alias :loop_forever :loop
792
+
793
+ # Runs the SSH event loop while the given block returns true. This lets
794
+ # you set up a state machine and then "fire it off". If you do not specify
795
+ # a block, the event loop will run for as long as there are any pending
796
+ # SFTP requests. This makes it easy to do thing like this:
797
+ #
798
+ # sftp.remove("/path/to/file")
799
+ # sftp.loop
800
+ def loop(&block)
801
+ block ||= Proc.new { pending_requests.any? }
802
+ session.loop(&block)
803
+ end
804
+
805
+ # Formats, constructs, and sends an SFTP packet of the given type and with
806
+ # the given data. This does not block, but merely enqueues the packet for
807
+ # sending and returns.
808
+ #
809
+ # You should probably use the operation methods, rather than building and
810
+ # sending the packet directly. (See #open, #close, etc.)
811
+ def send_packet(type, *args)
812
+ data = Net::SSH::Buffer.from(*args)
813
+ msg = Net::SSH::Buffer.from(:long, data.length+1, :byte, type, :raw, data)
814
+ channel.send_data(msg.to_s)
815
+ end
816
+
817
+ private
818
+
819
+ #--
820
+ # "ruby -w" hates private attributes, so we have to do this longhand
821
+ #++
822
+
823
+ # The input buffer used to accumulate packet data
824
+ def input; @input; end
825
+
826
+ # Create and enqueue a new SFTP request of the given type, with the
827
+ # given arguments. Returns a new Request instance that encapsulates the
828
+ # request.
829
+ def request(type, *args, &callback)
830
+ request = Request.new(self, type, protocol.send(type, *args), &callback)
831
+ info { "sending #{type} packet (#{request.id})" }
832
+ pending_requests[request.id] = request
833
+ end
834
+
835
+ # Waits for the given request to complete. If the response is
836
+ # EOF, nil is returned. If the response was not successful
837
+ # (e.g., !response.ok?), a StatusException will be raised.
838
+ # If +property+ is given, the corresponding property from the response
839
+ # will be returned; otherwise, the response object itself will be
840
+ # returned.
841
+ def wait_for(request, property=nil)
842
+ request.wait
843
+ if request.response.eof?
844
+ nil
845
+ elsif !request.response.ok?
846
+ raise StatusException.new(request.response)
847
+ elsif property
848
+ request.response[property.to_sym]
849
+ else
850
+ request.response
851
+ end
852
+ end
853
+
854
+ # Called when the SSH channel is confirmed as "open" by the server.
855
+ # This is one of the states of the SFTP state machine, and is followed
856
+ # by the #when_subsystem_started state.
857
+ def when_channel_confirmed(channel)
858
+ debug { "requesting sftp subsystem" }
859
+ @state = :subsystem
860
+ channel.subsystem("sftp", &method(:when_subsystem_started))
861
+ end
862
+
863
+ # Called when the SSH server confirms that the SFTP subsystem was
864
+ # successfully started. This sets up the appropriate callbacks on the
865
+ # SSH channel and then starts the SFTP protocol version negotiation
866
+ # process.
867
+ def when_subsystem_started(channel, success)
868
+ raise Net::SFTP::Exception, "could not start SFTP subsystem" unless success
869
+
870
+ debug { "sftp subsystem successfully started" }
871
+ @state = :init
872
+
873
+ channel.on_data { |c,data| input.append(data) }
874
+ channel.on_extended_data { |c,t,data| debug { data } }
875
+
876
+ channel.on_close(&method(:when_channel_closed))
877
+ channel.on_process(&method(:when_channel_polled))
878
+
879
+ send_packet(FXP_INIT, :long, HIGHEST_PROTOCOL_VERSION_SUPPORTED)
880
+ end
881
+
882
+ # Called when the SSH server closes the underlying channel.
883
+ def when_channel_closed(channel)
884
+ debug { "sftp channel closed" }
885
+ @channel = nil
886
+ @state = :closed
887
+ end
888
+
889
+ # Called whenever Net::SSH polls the SFTP channel for pending activity.
890
+ # This basically checks the input buffer to see if enough input has been
891
+ # accumulated to handle. If there has, the packet is parsed and
892
+ # dispatched, according to its type (see #do_version and #dispatch_request).
893
+ def when_channel_polled(channel)
894
+ while input.length > 0
895
+ if @packet_length.nil?
896
+ # make sure we've read enough data to tell how long the packet is
897
+ return unless input.length >= 4
898
+ @packet_length = input.read_long
899
+ end
900
+
901
+ return unless input.length >= @packet_length
902
+ packet = Net::SFTP::Packet.new(input.read(@packet_length))
903
+ input.consume!
904
+ @packet_length = nil
905
+
906
+ debug { "received sftp packet #{packet.type} len #{packet.length}" }
907
+
908
+ if packet.type == FXP_VERSION
909
+ do_version(packet)
910
+ else
911
+ dispatch_request(packet)
912
+ end
913
+ end
914
+ end
915
+
916
+ # Called to handle FXP_VERSION packets. This performs the SFTP protocol
917
+ # version negotiation, instantiating the appropriate Protocol instance
918
+ # and invoking the callback given to #connect, if any.
919
+ def do_version(packet)
920
+ debug { "negotiating sftp protocol version, mine is #{HIGHEST_PROTOCOL_VERSION_SUPPORTED}" }
921
+
922
+ server_version = packet.read_long
923
+ debug { "server reports sftp version #{server_version}" }
924
+
925
+ negotiated_version = [server_version, HIGHEST_PROTOCOL_VERSION_SUPPORTED].min
926
+ info { "negotiated version is #{negotiated_version}" }
927
+
928
+ extensions = {}
929
+ until packet.eof?
930
+ name = packet.read_string
931
+ data = packet.read_string
932
+ extensions[name] = data
933
+ end
934
+
935
+ @protocol = Protocol.load(self, negotiated_version)
936
+ @pending_requests = {}
937
+
938
+ @state = :open
939
+ @on_ready.each { |callback| callback.call(self) }
940
+ @on_ready = nil
941
+ end
942
+
943
+ # Parses the packet, finds the associated Request instance, and tells
944
+ # the Request instance to respond to the packet (see Request#respond_to).
945
+ def dispatch_request(packet)
946
+ id = packet.read_long
947
+ request = pending_requests.delete(id) or raise Net::SFTP::Exception, "no such request `#{id}'"
948
+ request.respond_to(packet)
949
+ end
950
+ end
951
+
952
+ end; end