require_relative '../client' require_relative '../gmail_client' module NaviEmailSync ## # This class represents the client for local lockbox mode. # In local-lockbox mode, all the content will be saved and also being used from the local file system # class Local CONFIG_PATH = File.join(ENV['HOME'], '/.navi/config.yml') def initialize(method, client_type = "local", debug = true) # flag to print Ruby library debug info (very detailed) @net_imap_debug = false # flag to mark email as read after gets downloaded. @mark_as_read = false # flag to turn on/off debug mode. @debug = debug # override the log file mkdir_if_not_exist(config['client_log_file']) @logger = Logger.new(config['client_log_file']) # sso_web (authentication) config. @sso_web_url = ENV['api_url'] # authentication token received from sso_web used to authenticate the request to database_api @token = nil @download_path = config['download_path'] + config['identifier'] + "/#{method}/" @current_user_email = config['username'] # client_type @client_type = client_type @gen_csv_url = "#{ENV['api_url']}/v2/generate_csv_input" @env = ENV['env']?ENV['env']:Rails.env end # # login # # login to the navi-cloud and get the authentication token # def login url = "#{@sso_web_url}/oauth/token" provider_url = url token = HTTParty.post(provider_url, body: { client_id: config["uid"], # get from sso_web application client_secret: config["secret_key"], grant_type: "client_credentials" } )['access_token'] @token = "Token: #{token}##{config['username']}" end def override_logger(logger) @logger = logger end def set_token(token) @token = token end ## # Downloads the email content from imap-server and save it to the download_path # def download(message, custom_uid) mime_type = message["mime_type"].nil? ? message.mime_type : message["mime_type"] if ['text/plain', 'text/html', 'text/alternative'].include? mime_type h = Hash.new out_file = @download_path + mime_type + "/"+custom_uid mkdir_if_not_exist(out_file) File.open(out_file, 'w') { |file| file.write(encrypt(message["decoded"].nil? ? message.decoded : message["decoded"])) } key = mime_type.split("/").join("_") h[key] = out_file return h end end ## # save data to download_path with file named by filename params. # Input is hashmap, and it save the hashmap as yml format. # def save(data={}, filename, stamp) temp_path = @download_path + "inputs/temp-#{stamp}" filepath = @download_path + filename + ".yml" mkdir_if_not_exist(filepath) mkdir_if_not_exist(temp_path) File.open(filepath, 'w') do |f| f.puts(data.to_yaml) end File.open(temp_path, 'a') do |f| f.puts(filepath) end return filepath end ## # send bulk request to navi-ai service with list of input files downloaded from imap server. # def send_request(in_filenames = [], stamp, is_last: false, email_count:) unless in_filenames.empty? filename = @download_path + "inputs/#{stamp}" mkdir_if_not_exist(filename) File.open(filename, 'w') do |f| in_filenames.each { |element| f.puts(element) } end response = HTTPService::NaviAI.start(filepath: filename, client_type: @client_type, token: @token, email: config['username'], is_last: is_last, email_count: email_count) return response end end def mkdir_if_not_exist(filepath) dirname = File.dirname(filepath) unless File.directory?(dirname) FileUtils.mkdir_p(dirname) end end # # idle_loop # # check for any further mail with "real-time" responsiveness. # retrieve any mail from a folder, following specified search condition # for any mail retrieved call a specified block # def idle_loop(search_condition, folder, server, username, password, callback = nil, navi_control = nil) @logger.info "\nwaiting new mails (IDLE loop)..." loop do begin @imap.select folder @imap.idle do |resp| # You'll get all the things from the server. For new emails (EXISTS) if resp.kind_of?(Net::IMAP::UntaggedResponse) and resp.name == "EXISTS" # @logger.debug resp.inspect if @debug # Got something. Send DONE. This breaks you out of the blocking call @logger.debug "New mail received" if @debug @imap.idle_done end end # We're out, which means there are some emails ready for us. # Go do a search for UNSEEN and fetch them. filenames = [] stamp = (Time.now.to_f * 1000).to_s init_messages folder, search_condition fetch_emails() { |mail, i, isLast, id| callback.call(mail, i, id, false) unless callback.nil? filenames << process_email(mail, id, stamp) callback.call(mail, i, id, true) unless callback.nil? } @logger.info "Sending Request for #{filenames.length} emails to Navi AI." navi_control.call(true) unless navi_control.nil? self.send_request(filenames, stamp, is_last: true, email_count: filenames.length) # HTTPService::NaviAI.generate_csv("#{@gen_csv_url}?email=#{username}", @token) @logger.debug "Process Completed." if @debug navi_control.call(false) unless navi_control.nil? rescue SignalException => e # http://stackoverflow.com/questions/2089421/capturing-ctrl-c-in-ruby @logger.info "Signal received at #{time_now}: #{e.class}. #{e.message}" shutdown @imap rescue Net::IMAP::Error => e @logger.error "Net::IMAP::Error at #{time_now}: #{e.class}. #{e.message}" @imap = imap_connection(server, username, password) #if e.message == 'connection closed' @logger.info "reconnected to server: #{server}" # rescue Exception => e # @logger.error "Something went wrong at #{time_now}: #{e.class}. #{e.message}" # # @imap = imap_connection(server, username, password) # @logger.info "reconnected to server: #{server}" end end end def initate_csv_generation(username) HTTPService::NaviAI.generate_csv("#{@gen_csv_url}?email=#{username}", @token) end def config YAML.load_file(CONFIG_PATH) end def save_aes_key_iv(key, iv) aes_key_path = File.join(ENV['HOME'] + '/.navi/.aes.key') iv_path = File.join(ENV['HOME'] + '/.navi/.aes.iv') File.open(aes_key_path, "w") { |f| f.write(Base64.encode64(key)) } File.open(iv_path, "w") { |f| f.write(Base64.encode64(iv)) } return aes_key_path, iv_path end def getMessageUUIds(path) File.directory?(path) ? (Dir.entries path).map { |i| i.split("_").first } : [] end def update_config(data) File.write(CONFIG_PATH, YAML.dump(data)) end def parse_prev_failed_emails changes = false Dir[@download_path + 'inputs/temp*'].each do |file_name| if File.file? file_name HTTPService::NaviAI.start(filepath: file_name, client_type: @client_type, token: @token, email: @current_user_email ) changes = true end end return changes end end class GmailApi < Local include GmailClient end class ImapApi < Local include Client end end