aspera-cli 4.0.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +3592 -0
  3. data/bin/ascli +7 -0
  4. data/bin/asession +89 -0
  5. data/docs/Makefile +59 -0
  6. data/docs/README.erb.md +3012 -0
  7. data/docs/README.md +13 -0
  8. data/docs/diagrams.txt +49 -0
  9. data/docs/secrets.make +38 -0
  10. data/docs/test_env.conf +117 -0
  11. data/docs/transfer_spec.html +99 -0
  12. data/examples/aoc.rb +17 -0
  13. data/examples/proxy.pac +60 -0
  14. data/examples/transfer.rb +115 -0
  15. data/lib/aspera/api_detector.rb +60 -0
  16. data/lib/aspera/ascmd.rb +151 -0
  17. data/lib/aspera/ats_api.rb +43 -0
  18. data/lib/aspera/cli/basic_auth_plugin.rb +38 -0
  19. data/lib/aspera/cli/extended_value.rb +88 -0
  20. data/lib/aspera/cli/formater.rb +238 -0
  21. data/lib/aspera/cli/listener/line_dump.rb +17 -0
  22. data/lib/aspera/cli/listener/logger.rb +20 -0
  23. data/lib/aspera/cli/listener/progress.rb +52 -0
  24. data/lib/aspera/cli/listener/progress_multi.rb +91 -0
  25. data/lib/aspera/cli/main.rb +304 -0
  26. data/lib/aspera/cli/manager.rb +440 -0
  27. data/lib/aspera/cli/plugin.rb +90 -0
  28. data/lib/aspera/cli/plugins/alee.rb +24 -0
  29. data/lib/aspera/cli/plugins/ats.rb +231 -0
  30. data/lib/aspera/cli/plugins/bss.rb +71 -0
  31. data/lib/aspera/cli/plugins/config.rb +806 -0
  32. data/lib/aspera/cli/plugins/console.rb +62 -0
  33. data/lib/aspera/cli/plugins/cos.rb +106 -0
  34. data/lib/aspera/cli/plugins/faspex.rb +377 -0
  35. data/lib/aspera/cli/plugins/faspex5.rb +93 -0
  36. data/lib/aspera/cli/plugins/node.rb +438 -0
  37. data/lib/aspera/cli/plugins/oncloud.rb +937 -0
  38. data/lib/aspera/cli/plugins/orchestrator.rb +169 -0
  39. data/lib/aspera/cli/plugins/preview.rb +464 -0
  40. data/lib/aspera/cli/plugins/server.rb +216 -0
  41. data/lib/aspera/cli/plugins/shares.rb +63 -0
  42. data/lib/aspera/cli/plugins/shares2.rb +114 -0
  43. data/lib/aspera/cli/plugins/sync.rb +65 -0
  44. data/lib/aspera/cli/plugins/xnode.rb +115 -0
  45. data/lib/aspera/cli/transfer_agent.rb +251 -0
  46. data/lib/aspera/cli/version.rb +5 -0
  47. data/lib/aspera/colors.rb +39 -0
  48. data/lib/aspera/command_line_builder.rb +137 -0
  49. data/lib/aspera/fasp/aoc.rb +24 -0
  50. data/lib/aspera/fasp/connect.rb +99 -0
  51. data/lib/aspera/fasp/error.rb +21 -0
  52. data/lib/aspera/fasp/error_info.rb +60 -0
  53. data/lib/aspera/fasp/http_gw.rb +81 -0
  54. data/lib/aspera/fasp/installation.rb +240 -0
  55. data/lib/aspera/fasp/listener.rb +11 -0
  56. data/lib/aspera/fasp/local.rb +377 -0
  57. data/lib/aspera/fasp/manager.rb +69 -0
  58. data/lib/aspera/fasp/node.rb +88 -0
  59. data/lib/aspera/fasp/parameters.rb +235 -0
  60. data/lib/aspera/fasp/resume_policy.rb +76 -0
  61. data/lib/aspera/fasp/uri.rb +51 -0
  62. data/lib/aspera/faspex_gw.rb +196 -0
  63. data/lib/aspera/hash_ext.rb +28 -0
  64. data/lib/aspera/log.rb +80 -0
  65. data/lib/aspera/nagios.rb +71 -0
  66. data/lib/aspera/node.rb +14 -0
  67. data/lib/aspera/oauth.rb +319 -0
  68. data/lib/aspera/on_cloud.rb +421 -0
  69. data/lib/aspera/open_application.rb +72 -0
  70. data/lib/aspera/persistency_action_once.rb +42 -0
  71. data/lib/aspera/persistency_folder.rb +91 -0
  72. data/lib/aspera/preview/file_types.rb +300 -0
  73. data/lib/aspera/preview/generator.rb +258 -0
  74. data/lib/aspera/preview/image_error.png +0 -0
  75. data/lib/aspera/preview/options.rb +35 -0
  76. data/lib/aspera/preview/utils.rb +131 -0
  77. data/lib/aspera/preview/video_error.png +0 -0
  78. data/lib/aspera/proxy_auto_config.erb.js +287 -0
  79. data/lib/aspera/proxy_auto_config.rb +34 -0
  80. data/lib/aspera/rest.rb +296 -0
  81. data/lib/aspera/rest_call_error.rb +13 -0
  82. data/lib/aspera/rest_error_analyzer.rb +98 -0
  83. data/lib/aspera/rest_errors_aspera.rb +58 -0
  84. data/lib/aspera/ssh.rb +53 -0
  85. data/lib/aspera/sync.rb +82 -0
  86. data/lib/aspera/temp_file_manager.rb +37 -0
  87. data/lib/aspera/uri_reader.rb +25 -0
  88. metadata +288 -0
@@ -0,0 +1,24 @@
1
+ require 'aspera/fasp/node'
2
+ require 'aspera/log'
3
+ require 'aspera/on_cloud.rb'
4
+
5
+ module Aspera
6
+ module Fasp
7
+ class Aoc < Node
8
+ def initialize(on_cloud_options)
9
+ @app=on_cloud_options[:app] || OnCloud::FILES_APP
10
+ @api_oncloud=OnCloud.new(on_cloud_options)
11
+ Log.log.warn("Under Development")
12
+ server_node_file = @api_oncloud.resolve_node_file(server_home_node_file,server_folder)
13
+ # force node as transfer agent
14
+ node_api=Fasp::Node.new(@api_oncloud.get_node_api(client_node_file[:node_info],OnCloud::SCOPE_NODE_USER))
15
+ super(node_api)
16
+ # additional node to node TS info
17
+ @add_ts={
18
+ 'remote_access_key' => server_node_file[:node_info]['access_key'],
19
+ 'destination_root_id' => server_node_file[:file_id]
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,99 @@
1
+ require 'aspera/fasp/manager'
2
+ require 'aspera/rest'
3
+ require 'aspera/open_application'
4
+ require 'securerandom'
5
+ require 'tty-spinner'
6
+
7
+ module Aspera
8
+ module Fasp
9
+ class Connect < Manager
10
+ MAX_CONNECT_START_RETRY=3
11
+ SLEEP_SEC_BETWEEN_RETRY=2
12
+ private_constant :MAX_CONNECT_START_RETRY,:SLEEP_SEC_BETWEEN_RETRY
13
+ def initialize
14
+ super
15
+ @connect_app_id=SecureRandom.uuid
16
+ # TODO: start here and create monitor
17
+ end
18
+
19
+ def start_transfer(transfer_spec,options=nil)
20
+ raise 'Using connect requires a graphical environment' if !OpenApplication.default_gui_mode.eql?(:graphical)
21
+ trynumber=0
22
+ begin
23
+ connect_url=Installation.instance.connect_uri
24
+ Log.log.debug("found: #{connect_url}")
25
+ @connect_api=Rest.new({base_url: "#{connect_url}/v5/connect",headers: {'Origin'=>Rest.user_agent}}) # could use v6 also now
26
+ cinfo=@connect_api.read('info/version')[:data]
27
+ rescue => e # Errno::ECONNREFUSED
28
+ raise StandardError,"Unable to start connect after #{trynumber} try" if trynumber >= MAX_CONNECT_START_RETRY
29
+ Log.log.warn("connect is not started. Retry ##{trynumber}, err=#{e}")
30
+ trynumber+=1
31
+ if !OpenApplication.uri_graphical('fasp://initialize')
32
+ OpenApplication.uri_graphical('https://downloads.asperasoft.com/connect2/')
33
+ raise StandardError,'Connect is not installed'
34
+ end
35
+ sleep SLEEP_SEC_BETWEEN_RETRY
36
+ retry
37
+ end
38
+ if transfer_spec['direction'] == 'send'
39
+ Log.log.warn("Connect requires upload selection using GUI, ignoring #{transfer_spec['paths']}".red)
40
+ transfer_spec.delete('paths')
41
+ resdata=@connect_api.create('windows/select-open-file-dialog/',{'title'=>'Select Files','suggestedName'=>'','allowMultipleSelection'=>true,'allowedFileTypes'=>'','aspera_connect_settings'=>{'app_id'=>@connect_app_id}})[:data]
42
+ transfer_spec['paths']=resdata['dataTransfer']['files'].map { |i| {'source'=>i['name']}}
43
+ end
44
+ @request_id=SecureRandom.uuid
45
+ # if there is a token, we ask connect client to use well known ssh private keys
46
+ # instead of asking password
47
+ transfer_spec['authentication']='token' if transfer_spec.has_key?('token')
48
+ connect_transfer_args={
49
+ 'transfer_specs'=>[{
50
+ 'transfer_spec'=>transfer_spec,
51
+ 'aspera_connect_settings'=>{
52
+ 'allow_dialogs'=>true,
53
+ 'app_id'=>@connect_app_id,
54
+ 'request_id'=>@request_id
55
+ }}]}
56
+ # asynchronous anyway
57
+ @connect_api.create('transfers/start',connect_transfer_args)
58
+ end
59
+
60
+ def wait_for_transfers_completion
61
+ connect_activity_args={'aspera_connect_settings'=>{'app_id'=>@connect_app_id}}
62
+ started=false
63
+ spinner=nil
64
+ loop do
65
+ result=@connect_api.create('transfers/activity',connect_activity_args)[:data]
66
+ if result['transfers']
67
+ trdata=result['transfers'].select{|i| i['aspera_connect_settings'] and i['aspera_connect_settings']['request_id'].eql?(@request_id)}.first
68
+ raise 'problem with connect, please kill it' unless trdata
69
+ # TODO: get session id
70
+ case trdata['status']
71
+ when 'completed'
72
+ notify_listeners('emulated',{Manager::LISTENER_SESSION_ID_B=>@connect_app_id,'Type'=>'DONE'})
73
+ break
74
+ when 'initiating'
75
+ if spinner.nil?
76
+ spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
77
+ spinner.start
78
+ end
79
+ spinner.update(title: trdata['status'])
80
+ spinner.spin
81
+ when 'running'
82
+ #puts "running: sessions:#{trdata['sessions'].length}, #{trdata['sessions'].map{|i| i['bytes_transferred']}.join(',')}"
83
+ if !started and trdata['bytes_expected'] != 0
84
+ notify_listeners('emulated',{Manager::LISTENER_SESSION_ID_B=>@connect_app_id,'Type'=>'NOTIFICATION','PreTransferBytes'=>trdata['bytes_expected']})
85
+ started=true
86
+ else
87
+ notify_listeners('emulated',{Manager::LISTENER_SESSION_ID_B=>@connect_app_id,'Type'=>'STATS','Bytescont'=>trdata['bytes_written']})
88
+ end
89
+ else
90
+ raise Fasp::Error.new("#{trdata['status']}: #{trdata['error_desc']}")
91
+ end
92
+ end
93
+ sleep 1
94
+ end
95
+ return [] #TODO
96
+ end # wait
97
+ end # Connect
98
+ end
99
+ end
@@ -0,0 +1,21 @@
1
+ require 'aspera/fasp/error_info'
2
+
3
+ module Aspera
4
+ module Fasp
5
+ # error raised if transfer fails
6
+ class Error < StandardError
7
+ attr_reader :err_code
8
+ def initialize(message,err_code=nil)
9
+ super(message)
10
+ @err_code = err_code
11
+ end
12
+
13
+ def info
14
+ r=Fasp::ERROR_INFO[@err_code] || {r: false , c: 'UNKNOWN', m: 'unknown', a: 'unknown'}
15
+ return r.merge({i: @err_code})
16
+ end
17
+
18
+ def retryable?; info[:r];end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,60 @@
1
+ module Aspera
2
+ module Fasp
3
+ # from https://www.google.com/search?q=FASP+error+codes
4
+ # Note that the fact that an error is retryable is not internally defined by protocol, it's client responsibility
5
+ ERROR_INFO = {
6
+ # id retryable mnemo message additional info
7
+ 1 => { r: false , c: 'FASP_PROTO', m: 'Generic fasp(tm) protocol error', a: 'fasp(tm) error'},
8
+ 2 => { r: false , c: 'ASCP', m: 'Generic SCP error', a: 'ASCP error'},
9
+ 3 => { r: false , c: 'AMBIGUOUS_TARGET', m: 'Target incorrectly specified', a: 'Ambiguous target'},
10
+ 4 => { r: false , c: 'NO_SUCH_FILE', m: 'No such file or directory', a: 'No such file or directory'},
11
+ 5 => { r: false , c: 'NO_PERMS', m: 'Insufficient permission to read or write', a: 'Insufficient permissions'},
12
+ 6 => { r: false , c: 'NOT_DIR', m: 'Target is not a directory', a: 'Target must be a directory'},
13
+ 7 => { r: false , c: 'IS_DIR', m: 'File is a directory - expected regular file', a: 'Expected regular file'},
14
+ 8 => { r: false , c: 'USAGE', m: 'Incorrect usage of scp command', a: 'Incorrect usage of Aspera scp command'},
15
+ 9 => { r: false , c: 'LIC_DUP', m: 'Duplicate license', a: 'Duplicate license'},
16
+ 10 => { r: false , c: 'LIC_RATE_EXCEEDED', m: 'Rate exceeds the cap imposed by license', a: 'Rate exceeds cap imposed by license'},
17
+ 11 => { r: false , c: 'INTERNAL_ERROR', m: 'Internal error (unexpected error)', a: 'Internal error'},
18
+ 12 => { r: true , c: 'TRANSFER_ERROR', m: 'Error establishing control connection', a: 'Error establishing SSH connection (check SSH port and firewall)'},
19
+ 13 => { r: true , c: 'TRANSFER_TIMEOUT', m: 'Timeout establishing control connection', a: 'Timeout establishing SSH connection (check SSH port and firewall)'},
20
+ 14 => { r: true , c: 'CONNECTION_ERROR', m: 'Error establishing data connection', a: 'Error establishing UDP connection (check UDP port and firewall)'},
21
+ 15 => { r: true , c: 'CONNECTION_TIMEOUT', m: 'Timeout establishing data connection', a: 'Timeout establishing UDP connection (check UDP port and firewall)'},
22
+ 16 => { r: true , c: 'CONNECTION_LOST', m: 'Connection lost', a: 'Connection lost'},
23
+ 17 => { r: true , c: 'RCVR_SEND_ERROR', m: 'Receiver fails to send feedback', a: 'Network failure (receiver can\'t send feedback)'},
24
+ 18 => { r: true , c: 'RCVR_RECV_ERROR', m: 'Receiver fails to receive data packets', a: 'Network failure (receiver can\'t receive UDP data)'},
25
+ 19 => { r: false , c: 'AUTH', m: 'Authentication failure', a: 'Authentication failure'},
26
+ 20 => { r: false , c: 'NOTHING', m: 'Nothing to transfer', a: 'Nothing to transfer'},
27
+ 21 => { r: false , c: 'NOT_REGULAR', m: 'Not a regular file (special file)', a: 'Not a regular file'},
28
+ 22 => { r: false , c: 'FILE_TABLE_OVR', m: 'File table overflow', a: 'File table overflow'},
29
+ 23 => { r: true , c: 'TOO_MANY_FILES', m: 'Too many files open', a: 'Too many files open'},
30
+ 24 => { r: false , c: 'FILE_TOO_BIG', m: 'File too big for file system', a: 'File too big for filesystem'},
31
+ 25 => { r: false , c: 'NO_SPACE_LEFT', m: 'No space left on disk', a: 'No space left on disk'},
32
+ 26 => { r: false , c: 'READ_ONLY_FS', m: 'Read only file system', a: 'Read only filesystem'},
33
+ 27 => { r: false , c: 'SOME_FILE_ERRS', m: 'Some individual files failed', a: 'One or more files failed'},
34
+ 28 => { r: false , c: 'USER_CANCEL', m: 'Cancelled by user', a: 'Cancelled by user'},
35
+ 29 => { r: false , c: 'LIC_NOLIC', m: 'License not found or unable to access', a: 'Unable to access license info'},
36
+ 30 => { r: false , c: 'LIC_EXPIRED', m: 'License expired', a: 'License expired'},
37
+ 31 => { r: false , c: 'SOCK_SETUP', m: 'Unable to setup socket (create, bind, etc ...)', a: 'Unable to set up socket'},
38
+ 32 => { r: true , c: 'OUT_OF_MEMORY', m: 'Out of memory, unable to allocate', a: 'Out of memory'},
39
+ 33 => { r: true , c: 'THREAD_SPAWN', m: 'Can\'t spawn thread', a: 'Unable to spawn thread'},
40
+ 34 => { r: false , c: 'UNAUTHORIZED', m: 'Unauthorized by external auth server', a: 'Unauthorized'},
41
+ 35 => { r: true , c: 'DISK_READ', m: 'Error reading source file from disk', a: 'Disk read error'},
42
+ 36 => { r: true , c: 'DISK_WRITE', m: 'Error writing to disk', a: 'Disk write error'},
43
+ 37 => { r: true , c: 'AUTHORIZATION', m: 'Used interchangeably with ERR_UNAUTHORIZED', a: 'Authorization failure'},
44
+ 38 => { r: false , c: 'LIC_ILLEGAL', m: 'Operation not permitted by license', a: 'Operation not permitted by license'},
45
+ 39 => { r: true , c: 'PEER_ABORTED_SESSION', m: 'Remote peer terminated session', a: 'Peer aborted session'},
46
+ 40 => { r: true , c: 'DATA_TRANSFER_TIMEOUT', m: 'Transfer stalled, timed out', a: 'Data transfer stalled, timed out'},
47
+ 41 => { r: false , c: 'BAD_PATH', m: 'Path violates docroot containment', a: 'File location is outside \'docroot\' hierarchy'},
48
+ 42 => { r: false , c: 'ALREADY_EXISTS', m: 'File or directory already exists', a: 'File or directory already exists'},
49
+ 43 => { r: false , c: 'STAT_FAILS', m: 'Cannot stat file', a: 'Cannot collect details about file or directory'},
50
+ 44 => { r: true , c: 'PMTU_BRTT_ERROR', m: 'UDP session initiation fatal error', a: 'UDP session initiation fatal error'},
51
+ 45 => { r: true , c: 'BWMEAS_ERROR', m: 'Bandwidth measurement fatal error', a: 'Bandwidth measurement fatal error'},
52
+ 46 => { r: false , c: 'VLINK_ERROR', m: 'Virtual link error', a: 'Virtual link error'},
53
+ 47 => { r: false , c: 'CONNECTION_ERROR_HTTP', m: 'Error establishing HTTP connection', a: 'Error establishing HTTP connection (check HTTP port and firewall)'},
54
+ 48 => { r: false , c: 'FILE_ENCRYPTION_ERROR', m: 'File encryption error, e.g. corrupt file', a: 'File encryption/decryption error, e.g. corrupt file'},
55
+ 49 => { r: false , c: 'FILE_DECRYPTION_PASS', m: 'File encryption/decryption error, e.g. corrupt file', a: 'File decryption error, bad passphrase'},
56
+ 50 => { r: false , c: 'BAD_CONFIGURATION', m: 'Aspera.conf contains invalid data and was rejected', a: 'Invalid configuration'},
57
+ 51 => { r: false , c: 'UNDEFINED', m: 'Should never happen, report to Aspera', a: 'Undefined error'},
58
+ }
59
+ end
60
+ end
@@ -0,0 +1,81 @@
1
+ #!/bin/echo this is a ruby class:
2
+ require 'aspera/fasp/manager'
3
+ require 'aspera/log'
4
+ require 'aspera/rest'
5
+
6
+ # ref: https://api.ibm.com/explorer/catalog/aspera/product/ibm-aspera/api/http-gateway-api/doc/guides-toc
7
+ module Aspera
8
+ module Fasp
9
+ # executes a local "ascp", connects mgt port, equivalent of "Fasp Manager"
10
+ class HttpGW < Manager
11
+ # start FASP transfer based on transfer spec (hash table)
12
+ # note that it is asynchronous
13
+ # HTTP download only supports file list
14
+ def start_transfer(transfer_spec,options={})
15
+ raise "GW URL must be set" unless !@gw_api.nil?
16
+ raise "option: must be hash (or nil)" unless options.is_a?(Hash)
17
+ raise "paths: must be Array" unless transfer_spec['paths'].is_a?(Array)
18
+ case transfer_spec['direction']
19
+ when 'send'
20
+ # this is a websocket
21
+ raise "error, not implemented"
22
+ when 'receive'
23
+ transfer_spec['zip_required']||=false
24
+ transfer_spec['authentication']||='token'
25
+ transfer_spec['source_root']||='/'
26
+ # is normally provided by application, like package name
27
+ if !transfer_spec.has_key?('download_name')
28
+ # by default it is the name of first file
29
+ dname=File.basename(transfer_spec['paths'].first['source'])
30
+ # we remove extension
31
+ dname=dname.gsub(/\.@gw_api.*$/,'')
32
+ # ands add indication of number of files if there is more than one
33
+ if transfer_spec['paths'].length > 1
34
+ dname=dname+" #{transfer_spec['paths'].length} Files"
35
+ end
36
+ transfer_spec['download_name']=dname
37
+ end
38
+ creation=@gw_api.create('download',{'transfer_spec'=>transfer_spec})[:data]
39
+ transfer_uuid=creation['url'].split('/').last
40
+ if transfer_spec['zip_required'] or transfer_spec['paths'].length > 1
41
+ # it is a zip file if zip is required or there is more than 1 file
42
+ file_dest=transfer_spec['download_name']+'.zip'
43
+ else
44
+ # it is a plain file if we don't require zip and there is only one file
45
+ file_dest=File.basename(transfer_spec['paths'].first['source'])
46
+ end
47
+ file_dest=File.join(transfer_spec['destination_root'],file_dest)
48
+ @gw_api.call({:operation=>'GET',:subpath=>"download/#{transfer_uuid}",:save_to_file=>file_dest})
49
+ else
50
+ raise "error"
51
+ end
52
+ end # start_transfer
53
+
54
+ # wait for completion of all jobs started
55
+ # @return list of :success or error message
56
+ def wait_for_transfers_completion
57
+ return [:success]
58
+ end
59
+
60
+ # terminates monitor thread
61
+ def shutdown
62
+ end
63
+
64
+ def url=(api_url)
65
+ end
66
+
67
+ private
68
+
69
+ def initialize(params)
70
+ raise "params must be Hash" unless params.is_a?(Hash)
71
+ params=params.symbolize_keys
72
+ raise "must have only one param: url" unless params.keys.eql?([:url])
73
+ super()
74
+ @gw_api=Rest.new({:base_url => params[:url]})
75
+ api_info = @gw_api.read('info')[:data]
76
+ Log.log.info("#{api_info}")
77
+ end
78
+
79
+ end # LocalHttp
80
+ end
81
+ end
@@ -0,0 +1,240 @@
1
+ require 'singleton'
2
+ require 'aspera/log'
3
+ require 'aspera/open_application' # current_os_type
4
+
5
+ require 'xmlsimple'
6
+ require 'zlib'
7
+ require 'base64'
8
+
9
+ module Aspera
10
+ module Fasp
11
+ # Singleton that tells where to find ascp and other local resources (keys..) , using the "path(symb)" method.
12
+ # It is used by object : Fasp::Local to find necessary resources
13
+ # By default it takes the first Aspera product found specified in product_locations
14
+ # but the user can specify ascp location by calling:
15
+ # Installation.instance.use_ascp_from_product(product_name)
16
+ # or
17
+ # Installation.instance.ascp_path=ascp_path
18
+ class Installation
19
+ include Singleton
20
+ # currently used ascp executable
21
+ attr_accessor :ascp_path
22
+ # where key files are generated and used
23
+ attr_accessor :config_folder
24
+ # find ascp in named product (use value : FIRST_FOUND='FIRST' to just use first one)
25
+ # or select one from installed_products()
26
+ def use_ascp_from_product(product_name)
27
+ if product_name.eql?(FIRST_FOUND)
28
+ pl=installed_products.first
29
+ raise "no FASP installation found\nPlease check manual on how to install FASP." if pl.nil?
30
+ else
31
+ pl=installed_products.select{|i|i[:name].eql?(product_name)}.first
32
+ raise "no such product installed: #{product_name}" if pl.nil?
33
+ end
34
+ @ascp_path=pl[:ascp_path]
35
+ Log.log.debug("ascp_path=#{@ascp_path}")
36
+ end
37
+
38
+ # @return the list of installed products in format of product_locations
39
+ def installed_products
40
+ if @found_products.nil?
41
+ @found_products=product_locations.select do |pl|
42
+ next false unless Dir.exist?(pl[:app_root])
43
+ Log.log.debug("found #{pl[:app_root]}")
44
+ sub_bin = pl[:sub_bin] || BIN_SUBFOLDER
45
+ exec_ext = OpenApplication.current_os_type.eql?(:windows) ? '.exe' : ''
46
+ pl[:ascp_path]=File.join(pl[:app_root],sub_bin,'ascp')+exec_ext
47
+ next false unless File.exist?(pl[:ascp_path])
48
+ product_info_file="#{pl[:app_root]}/#{PRODUCT_INFO}"
49
+ if File.exist?(product_info_file)
50
+ res_s=XmlSimple.xml_in(File.read(product_info_file),{"ForceArray"=>false})
51
+ pl[:name]=res_s['name']
52
+ pl[:version]=res_s['version']
53
+ else
54
+ pl[:name]=pl[:expected]
55
+ end
56
+ true # select this version
57
+ end
58
+ end
59
+ return @found_products
60
+ end
61
+
62
+ FILES=[:ascp,:ascp4,:ssh_bypass_key_dsa,:ssh_bypass_key_rsa,:fallback_cert,:fallback_key]
63
+
64
+ # get path of one resource file of currently activated product
65
+ # keys and certs are generated locally... (they are well known values, arch. independant)
66
+ def path(k)
67
+ case k
68
+ when :ascp,:ascp4
69
+ use_ascp_from_product(FIRST_FOUND) if @ascp_path.nil?
70
+ file=@ascp_path
71
+ # note that there might be a .exe at the end
72
+ file=file.gsub('ascp','ascp4') if k.eql?(:ascp4)
73
+ when :ssh_bypass_key_dsa
74
+ file=File.join(@config_folder,'aspera_bypass_dsa.pem')
75
+ File.write(file,Zlib::Inflate.inflate(Base64.decode64(SSH_BYPASS_DSA))) unless File.exist?(file)
76
+ File.chmod(0400,file)
77
+ when :ssh_bypass_key_rsa
78
+ file=File.join(@config_folder,'aspera_bypass_rsa.pem')
79
+ File.write(file,Zlib::Inflate.inflate(Base64.decode64(SSH_BYPASS_RSA))) unless File.exist?(file)
80
+ File.chmod(0400,file)
81
+ when :fallback_cert,:fallback_key
82
+ file_key=File.join(@config_folder,'aspera_fallback_key.pem')
83
+ file_cert=File.join(@config_folder,'aspera_fallback_cert.pem')
84
+ if !File.exist?(file_key) or !File.exist?(file_cert)
85
+ require 'openssl'
86
+ # create new self signed certificate forhttp fallback
87
+ private_key = OpenSSL::PKey::RSA.new(1024)
88
+ cert = OpenSSL::X509::Certificate.new
89
+ cert.subject = cert.issuer = OpenSSL::X509::Name.parse("/C=US/ST=California/L=Emeryville/O=Aspera Inc./OU=Corporate/CN=Aspera Inc./emailAddress=info@asperasoft.com")
90
+ cert.not_before = Time.now
91
+ cert.not_after = Time.now + 365 * 24 * 60 * 60
92
+ cert.public_key = private_key.public_key
93
+ cert.serial = 0x0
94
+ cert.version = 2
95
+ cert.sign(private_key, OpenSSL::Digest::SHA1.new)
96
+ File.write(file_key,private_key.to_pem)
97
+ File.write(file_cert,cert.to_pem)
98
+ File.chmod(0400,file_key)
99
+ File.chmod(0400,file_cert)
100
+ end
101
+ file = k.eql?(:fallback_cert) ? file_cert : file_key
102
+ else
103
+ raise "INTERNAL ERROR: #{k}"
104
+ end
105
+ raise "no such file: #{file}" unless File.exist?(file)
106
+ return file
107
+ end
108
+
109
+ # @returns the file path of local connect where API's URI can be read
110
+ def connect_uri
111
+ connect=get_product_folders('Aspera Connect')
112
+ folder=File.join(connect[:run_root],VARRUN_SUBFOLDER)
113
+ ['','s'].each do |ext|
114
+ uri_file=File.join(folder,"http#{ext}.uri")
115
+ Log.log.debug("checking connect port file: #{uri_file}")
116
+ if File.exist?(uri_file)
117
+ return File.open(uri_file){|f|f.gets}.strip
118
+ end
119
+ end
120
+ raise "no connect uri file found in #{folder}"
121
+ end
122
+
123
+ # @ return path to configuration file of aspera CLI
124
+ def cli_conf_file
125
+ connect=get_product_folders('Aspera CLI')
126
+ return File.join(connect[:app_root],BIN_SUBFOLDER,'.aspera_cli_conf')
127
+ end
128
+
129
+ # add Aspera private keys for web access, token based authorization
130
+ def bypass_keys
131
+ return [ "%08x-%04x-%04x-%04x-%04x%08x" % "t1(\xBF;\xF3E\xB5\xAB\x14F\x02\xC6\x7F)P".unpack("NnnnnN"),
132
+ Installation.instance.path(:ssh_bypass_key_dsa),
133
+ Installation.instance.path(:ssh_bypass_key_rsa) ]
134
+ end
135
+
136
+ # DEPRECATED ZONE
137
+
138
+ def activated;Log.log.warn("deprecated, use ascp_path accessor");nil;end
139
+
140
+ def activated=(product_name);Log.log.warn("deprecated, use method use_ascp_from_product");use_ascp_from_product(product_name);end
141
+
142
+ def paths;Log.log.warn("deprecated, no replacement");raise "deprecated";end
143
+
144
+ def paths=(res_paths)
145
+ raise "must be a hash" unless res_paths.is_a?(Hash)
146
+ raise "must have :ascp key" unless res_paths.has_key?(:ascp)
147
+ Log.log.warn("deprecated, use method: ascp_path=")
148
+ @ascp_path=res_paths[:ascp]
149
+ end
150
+
151
+ private
152
+
153
+ BIN_SUBFOLDER='bin'
154
+ ETC_SUBFOLDER='etc'
155
+ VARRUN_SUBFOLDER=File.join('var','run')
156
+ # product information manifest: XML (part of aspera product)
157
+ PRODUCT_INFO='product-info.mf'
158
+ # policy for product selection
159
+ FIRST_FOUND='FIRST'
160
+
161
+ private_constant :BIN_SUBFOLDER,:ETC_SUBFOLDER,:VARRUN_SUBFOLDER,:PRODUCT_INFO
162
+
163
+ # get some specific folder from specific applications: Connect or CLI
164
+ def get_product_folders(name)
165
+ found=installed_products.select{|i|i[:expected].eql?(name) or i[:name].eql?(name)}
166
+ raise "Product: #{name} not found, please install." if found.empty?
167
+ return found.first
168
+ end
169
+
170
+ def initialize
171
+ @ascp_path=nil
172
+ @config_folder='.'
173
+ @found_products=nil
174
+ end
175
+
176
+ # returns product folders depending on OS
177
+ # fields
178
+ # :expected M app name is taken from the manifest if present, else defaults to this value
179
+ # :app_root M main forlder for the application
180
+ # :log_root O location of log files (Linux uses syslog)
181
+ # :run_root O only for Connect Client, location of http port file
182
+ # :sub_bin O subfolder with executables, default : bin
183
+ def product_locations
184
+ case OpenApplication.current_os_type
185
+ when :windows; return [{
186
+ :expected =>'Aspera Connect',
187
+ :app_root =>File.join(ENV['LOCALAPPDATA'],'Programs','Aspera','Aspera Connect'),
188
+ :log_root =>File.join(ENV['LOCALAPPDATA'],'Aspera','Aspera Connect','var','log'),
189
+ :run_root =>File.join(ENV['LOCALAPPDATA'],'Aspera','Aspera Connect')
190
+ },{
191
+ :expected =>'Aspera CLI',
192
+ :app_root =>File.join('C:','Program Files','Aspera','cli'),
193
+ :log_root =>File.join('C:','Program Files','Aspera','cli','var','log'),
194
+ },{
195
+ :expected =>'Enterprise Server',
196
+ :app_root =>File.join('C:','Program Files','Aspera','Enterprise Server'),
197
+ :log_root =>File.join('C:','Program Files','Aspera','Enterprise Server','var','log'),
198
+ }]
199
+ when :mac; return [{
200
+ :expected =>'Aspera Connect',
201
+ :app_root =>File.join(Dir.home,'Applications','Aspera Connect.app'),
202
+ :log_root =>File.join(Dir.home,'Library','Logs','Aspera_Connect'),
203
+ :run_root =>File.join(Dir.home,'Library','Application Support','Aspera','Aspera Connect'),
204
+ :sub_bin =>File.join('Contents','Resources'),
205
+ },{
206
+ :expected =>'Aspera CLI',
207
+ :app_root =>File.join(Dir.home,'Applications','Aspera CLI'),
208
+ :log_root =>File.join(Dir.home,'Library','Logs','Aspera')
209
+ },{
210
+ :expected =>'Enterprise Server',
211
+ :app_root =>File.join('','Library','Aspera'),
212
+ :log_root =>File.join(Dir.home,'Library','Logs','Aspera'),
213
+ },{
214
+ :expected =>'Aspera Drive',
215
+ :app_root =>File.join('','Applications','Aspera Drive.app'),
216
+ :log_root =>File.join(Dir.home,'Library','Logs','Aspera_Drive'),
217
+ :sub_bin =>File.join('Contents','Resources'),
218
+ }]
219
+ else; return [{ # other: Linux and unix family
220
+ :expected =>'Aspera Connect',
221
+ :app_root =>File.join(Dir.home,'.aspera','connect'),
222
+ :run_root =>File.join(Dir.home,'.aspera','connect')
223
+ },{
224
+ :expected =>'Aspera CLI',
225
+ :app_root =>File.join(Dir.home,'.aspera','cli'),
226
+ },{
227
+ :expected =>'Enterprise Server',
228
+ :app_root =>File.join('','opt','aspera'),
229
+ }]
230
+ end
231
+ end
232
+
233
+ # not pass protected
234
+ SSH_BYPASS_DSA='eJxtksuSojAAAO98xdypKYFggGOA8HQ0ICBywwiooARRIH79zu55+9qnrurv719M7PrbL3uPvkjsZyjBXyE+/hXfwo/vm+/ZNxEKzSay2zDybHhXub80t7HikDy4Ckta5DgeA4+vVbYyh9znxzbboRYzIWymBAxJsr3z9nCY1X7aEVm0rzJdKQ470q6jCbu0B7rXO6TdJFm5p7iieTnlN0JcSbiC13p6mfqy4QC0EaiMyVht5ou00Lh+l9J5ndbO3DPTR/kUoCcw4bNkoy5GvymbeRR4NYwLwH1nlVaWB7UIZVoFjKHeRaQnL0xU/vFcJX/Rxarz8qS+jQ+GM/moFQlekpAmD1Cn0yO6B4l2lcLMip+gUTxlV/wcHFnh0i0d9Mh8F8rYA8urKu0gZ3d0Pm01Z6GiQHlmPDD8pPGASsJfygmLz+ZHZgRuovS4NDZYwvMkF67Y2r6Na5iC/nGj4emOIG0XIYFuOfXIao4Y9aeS2dOaKXXvidRdh5I2+o5tPKXY1t5h8OiG2xHlH4fqqQbnPGzeUDjkb6aUVLJ6MX4UTPPG0nBFLF4DyHrfYLtY0vPkTDqucjvdbPeJSuaufp72TmI42UX4tIea7SaUUr1uI3QphmlFMMwuTqTPEih0lw35op3AdjJjEdf+AqAe9paDed1JkyckpdbAuzv7P/nznESRXhej8O8xvLX//94fHTzUbwo='
235
+ # pass protected (but not fips compliant)
236
+ SSH_BYPASS_RSA='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'
237
+ private_constant :SSH_BYPASS_DSA,:SSH_BYPASS_RSA
238
+ end # Installation
239
+ end
240
+ end