aspera-cli 4.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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