Add puppet/archive module
[mirror/dsa-puppet.git] / 3rdparty / modules / archive / lib / puppet_x / bodeco / util.rb
diff --git a/3rdparty/modules/archive/lib/puppet_x/bodeco/util.rb b/3rdparty/modules/archive/lib/puppet_x/bodeco/util.rb
new file mode 100644 (file)
index 0000000..96ac6df
--- /dev/null
@@ -0,0 +1,180 @@
+module PuppetX
+  module Bodeco
+    module Util
+      def self.download(url, filepath, options = {})
+        uri = URI(url)
+        @connection = PuppetX::Bodeco.const_get(uri.scheme.upcase).new("#{uri.scheme}://#{uri.host}:#{uri.port}", options)
+        @connection.download(uri, filepath)
+      end
+
+      def self.content(url, options = {})
+        uri = URI(url)
+        @connection = PuppetX::Bodeco.const_get(uri.scheme.upcase).new("#{uri.scheme}://#{uri.host}:#{uri.port}", options)
+        @connection.content(uri)
+      end
+
+      #
+      # This allows you to use a puppet syntax for a file and return it's content.
+      #
+      # @example
+      #  puppet_download 'puppet:///modules/my_module_name/my_file.dat
+      #
+      # @param [String] url this is the puppet url of the file to be fetched
+      # @param [String] filepath this is path of the file to create
+      #
+      # @raise [ArgumentError] when the file doesn't exist
+      #
+      def self.puppet_download(url, filepath)
+        # Somehow there is no consistent way to determine what terminus to use. So we switch to a
+        # trial and error method. First we start withe the default. And if it doesn't work, we try the
+        # other ones
+        status = load_file_with_any_terminus(url)
+        raise ArgumentError, "Could not retrieve information from environment #{Puppet['environment']} source(s) #{url}'" unless status
+        File.open(filepath, 'w') { |file| file.write(status.content) }
+      end
+
+      # @private
+      # rubocop:disable HandleExceptions
+      def self.load_file_with_any_terminus(url)
+        termini_to_try = [:file_server, :rest]
+        termini_to_try.each do |terminus|
+          with_terminus(terminus) do
+            begin
+              content = Puppet::FileServing::Content.indirection.find(url)
+            rescue SocketError, Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTDOWN, Errno::EHOSTUNREACH, Errno::ETIMEDOUT
+              # rescue any network error
+            end
+            return content if content
+          end
+        end
+        nil
+      end
+      # rubocop:enable HandleExceptions
+
+      def self.with_terminus(terminus)
+        old_terminus = Puppet[:default_file_terminus]
+        Puppet[:default_file_terminus] = terminus
+        value = yield
+        Puppet[:default_file_terminus] = old_terminus
+        value
+      end
+    end
+    class HTTP
+      require 'net/http'
+
+      FOLLOW_LIMIT = 5
+      URI_UNSAFE = %r{[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]%]}
+
+      def initialize(_url, options)
+        @username = options[:username]
+        @password = options[:password]
+        @cookie = options[:cookie]
+        @insecure = options[:insecure]
+
+        if options[:proxy_server]
+          uri = URI(options[:proxy_server])
+          unless uri.scheme
+            uri = URI("#{options[:proxy_type]}://#{options[:proxy_server]}")
+          end
+          @proxy_addr = uri.hostname
+          @proxy_port = uri.port
+        end
+
+        ENV['SSL_CERT_FILE'] = File.expand_path(File.join(__FILE__, '..', 'cacert.pem')) if Facter.value(:osfamily) == 'windows' && !ENV.key?('SSL_CERT_FILE')
+      end
+
+      def generate_request(uri)
+        header = @cookie && { 'Cookie' => @cookie }
+
+        request = Net::HTTP::Get.new(uri.request_uri, header)
+        request.basic_auth(@username, @password) if @username && @password
+        request
+      end
+
+      def follow_redirect(uri, option = { limit: FOLLOW_LIMIT }, &block)
+        http_opts = if uri.scheme == 'https'
+                      { use_ssl: true,
+                        verify_mode: (@insecure ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER) }
+                    else
+                      { use_ssl: false }
+                    end
+        Net::HTTP.start(uri.host, uri.port, @proxy_addr, @proxy_port, http_opts) do |http|
+          http.request(generate_request(uri)) do |response|
+            case response
+            when Net::HTTPSuccess
+              yield response
+            when Net::HTTPRedirection
+              limit = option[:limit] - 1
+              raise Puppet::Error, "Redirect limit exceeded, last url: #{uri}" if limit < 0
+              location = safe_escape(response['location'])
+              new_uri = URI(location)
+              new_uri = URI(uri.to_s + location) if new_uri.relative?
+              follow_redirect(new_uri, limit: limit, &block)
+            else
+              raise Puppet::Error, "HTTP Error Code #{response.code}\nURL: #{uri}\nContent:\n#{response.body}"
+            end
+          end
+        end
+      end
+
+      def download(uri, file_path, option = { limit: FOLLOW_LIMIT })
+        follow_redirect(uri, option) do |response|
+          File.open file_path, 'wb' do |io|
+            response.read_body do |chunk|
+              io.write chunk
+            end
+          end
+        end
+      end
+
+      def content(uri, option = { limit: FOLLOW_LIMIT })
+        follow_redirect(uri, option) do |response|
+          return response.body
+        end
+      end
+
+      def safe_escape(uri)
+        uri.to_s.gsub(URI_UNSAFE) do |match|
+          '%' + match.unpack('H2' * match.bytesize).join('%').upcase
+        end
+      end
+    end
+
+    class HTTPS < HTTP
+    end
+
+    class FTP
+      require 'net/ftp'
+
+      def initialize(url, options)
+        uri = URI(url)
+        username = options[:username]
+        password = options[:password]
+        proxy_server = options[:proxy_server]
+        proxy_type = options[:proxy_type]
+
+        ENV["#{proxy_type}_proxy"] = proxy_server
+
+        @ftp = Net::FTP.new
+        @ftp.connect(uri.host, uri.port)
+        if username
+          @ftp.login(username, password)
+        else
+          @ftp.login
+        end
+      end
+
+      def download(uri, file_path)
+        @ftp.getbinaryfile(uri.path, file_path)
+      end
+    end
+
+    class FILE
+      def initialize(_url, _options) end
+
+      def download(uri, file_path)
+        FileUtils.copy(uri.path, file_path)
+      end
+    end
+  end
+end