require 'fog/ninefold' require 'fog/storage' module Fog module Storage class Ninefold < Fog::Service STORAGE_HOST = "onlinestorage.ninefold.com" #"api.ninefold.com" STORAGE_PATH = "" #"/storage/v1.0" STORAGE_PORT = "80" # "443" STORAGE_SCHEME = "http" # "https" requires :ninefold_storage_token, :ninefold_storage_secret model_path 'fog/ninefold/models/storage' model :directory collection :directories model :file collection :files request_path 'fog/ninefold/requests/storage' # request :delete_container request :get_namespace request :head_namespace request :post_namespace request :put_namespace request :delete_namespace module Utils end class Mock include Utils def initialize(options={}) require 'mime/types' @ninefold_storage_token = options[:ninefold_storage_token] @ninefold_storage_secret = options[:ninefold_storage_secret] end def request(options) raise "Ninefold Storage mocks not implemented" end end class Real include Utils def initialize(options={}) require 'mime/types' @ninefold_storage_token = options[:ninefold_storage_token] @ninefold_storage_secret = options[:ninefold_storage_secret] @ninefold_storage_secret_decoded = Base64.decode64( @ninefold_storage_secret ) @connection_options = options[:connection_options] || {} @hmac = Fog::HMAC.new('sha1', @ninefold_storage_secret_decoded) @persistent = options.fetch(:persistent, true) @connection = Fog::Connection.new("#{Fog::Storage::Ninefold::STORAGE_SCHEME}://#{Fog::Storage::Ninefold::STORAGE_HOST}:#{Fog::Storage::Ninefold::STORAGE_PORT}", @persistent, @connection_options) end def uid @ninefold_storage_token#.split('/')[-1] end def sign(string) value = @hmac.sign(string) Base64.encode64( value ).chomp() end def reload @connection.reset end def request(params, &block) req_path = params[:path] # Force set host and port params.merge!({ :host => Fog::Storage::Ninefold::STORAGE_HOST, :path => "#{Fog::Storage::Ninefold::STORAGE_PATH}/rest/#{params[:path]}", }) # Set default method and headers params = {:method => 'GET', :headers => {}}.merge params params[:headers]["Content-Type"] ||= "application/octet-stream" # Add request date params[:headers]["date"] = Time.now().httpdate() params[:headers]["x-emc-uid"] = @ninefold_storage_token # Build signature string signstring = "" signstring += params[:method] signstring += "\n" signstring += params[:headers]["Content-Type"] signstring += "\n" if( params[:headers]["range"] ) signstring += params[:headers]["range"] end signstring += "\n" signstring += params[:headers]["date"] signstring += "\n" signstring += "/rest/" + URI.unescape( req_path ).downcase query_str = params[:query].map{|k,v| "#{k}=#{v}"}.join('&') signstring += '?' + query_str unless query_str.empty? signstring += "\n" customheaders = {} params[:headers].each { |key,value| case key when 'x-emc-date', 'x-emc-signature' #skip when /^x-emc-/ customheaders[ key.downcase ] = value end } header_arr = customheaders.sort() header_arr.each { |key,value| # Values are lowercase and whitespace-normalized signstring += key + ":" + value.strip.chomp.squeeze( " " ) + "\n" } digest = @hmac.sign(signstring.chomp()) signature = Base64.encode64( digest ).chomp() params[:headers]["x-emc-signature"] = signature begin response = @connection.request(params, &block) rescue Excon::Errors::HTTPStatusError => error raise case error when Excon::Errors::NotFound Fog::Storage::Ninefold::NotFound.slurp(error) else error end end unless response.body.empty? if params[:parse] document = Fog::ToHashDocument.new parser = Nokogiri::XML::SAX::PushParser.new(document) parser << response.body parser.finish response.body = document.body end end response end end end end end