require "mail" require "time" require 'base64' require 'fileutils' require 'yaml' require "logger" require "httparty" require "http_service/naviai" require 'openssl' require 'google/apis/gmail_v1' require 'googleauth' require 'googleauth/stores/file_token_store' ## # This module provides the common functionality that will be needed for local and cloud module. # @client_type defines if the gem is being used for local-lockbox or cloud-version. # module GmailClient OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'.freeze APPLICATION_NAME = 'Gmail Api Sync'.freeze attr_accessor :downloaded attr_accessor :message_ids attr_accessor :total_emails @logglyTag def logger @logger end def errors @errors end def init client_path, user_cred_path, user_id client_id = Google::Auth::ClientId.from_file(client_path) token_store = Google::Auth::Stores::FileTokenStore.new(file: user_cred_path) @authorizer = Google::Auth::UserAuthorizer.new(client_id, Google::Apis::GmailV1::AUTH_GMAIL_READONLY, token_store) credentials = @authorizer.get_credentials(user_id) unless credentials.nil? init_apis credentials end credentials end def authorize code, user_id credentials = @authorizer.get_and_store_credentials_from_code( user_id: user_id, code: code, base_url: OOB_URI ) unless credentials.nil? init_apis credentials end credentials end def init_apis cred @service = Google::Apis::GmailV1::GmailService.new @service.client_options.application_name = APPLICATION_NAME @service.authorization = cred end def authorization_url @authorizer.get_authorization_url(base_url: OOB_URI) end # # retrieve_emails # # retrieve any mail from a folder, followin specified serach condition # for any mail retrieved call a specified block # def fetch_emails(&process_email_block) @message_ids.each_with_index do |message_id, i| begin # fetch all the email contents # data = imap.uid_fetch(message_id, "RFC822") result = @service.get_user_message('me', message_id) process_email_block.call result, i, i == @message_ids.length-1, message_id rescue => e unless logger.nil? logger.info "Issue processing email for uuid ##{message_id}, #{e.message}" end logToLoggly({event:"EMAIL_SYNC_FAILED", env: @env, storage: @client_type, email: @current_user_email, uuid: message_id, error: e.message}) raise e end end end ## # Process each email downloaded from imap-server and creates meta file that will be sent over to the navi-ai service. # Meta will content information like: ['from', 'to', 'cc', ..., 'body_url']. This information is then used by navi-ai to parse the body content. # def process_email(mail, uid, stamp = nil) meta = Hash.new custom_uid = (Time.now.to_f * 1000).to_s + "_" + uid payload = mail.payload headers = payload.headers date = headers.any? { |h| h.name == 'Date' } ? headers.find { |h| h.name == 'Date' }.value : '' from = headers.any? { |h| h.name == 'From' } ? headers.find { |h| h.name == 'From' }.value : '' to = headers.any? { |h| h.name == 'To' } ? headers.find { |h| h.name == 'To' }.value : '' subject = headers.any? { |h| h.name == 'Subject' } ? headers.find { |h| h.name == 'Subject' }.value : '' cc = headers.any? { |h| h.name == 'Cc' } ? headers.find { |h| h.name == 'Cc' }.value : '' meta["from"] = from meta["to"] = to meta["cc"] = cc meta["subject"] = subject meta["date"] = date if payload.parts&.any? payload.parts.map { |part| m = download({"decoded" => part.body.data, "mime_type" => part.mime_type}, custom_uid) meta.merge!(m) unless m.nil? } else m = download({"decoded" => payload.body.data, "mime_type" => "text/html"}, custom_uid) meta.merge!(m) unless m.nil? end if stamp.nil? save(meta, "meta/#{uid.to_s + '_' + custom_uid}") else save(meta, "meta/#{uid.to_s + '_' + custom_uid}", stamp) end end def all_messages label messages = [] next_page = nil begin result = @service.list_user_messages('me', max_results: 1000, page_token: next_page, label_ids: [label]) messages += result.messages # break if messages.size >= 1 next_page = result.next_page_token end while next_page messages end def get_user_info user_id @service.get_user_profile user_id end def all_message_ids label mids = [] messages = all_messages label messages.each { |m| mids << m.id } mids end def init_messages label @message_ids = all_message_ids label @total_emails = @message_ids.length downloaded_email = getMessageUUIds("#{@download_path}meta/") @downloaded = downloaded_email.length @downloaded = @downloaded - 2 if @downloaded > 1 @downloaded = [@downloaded, @total_emails].min @message_ids = @message_ids - downloaded_email if @debug if @message_ids.empty? @logger.info "No new emails found..." elsif downloaded_email.any? @logger.info "Found emails saved in your #{@client_type}. Downloading only #{@message_ids.count} new emails..." else @logger.info "Downloading #{@message_ids.count} emails." end end end def logToLoggly(messageBody) if(@logglyTag) begin HTTParty.post("http://logs-01.loggly.com/bulk/0d67f93b-6568-4b00-9eca-97d0ea0bd5a1/tag/#{@logglyTag}/", body: messageBody.to_json, headers: { 'Content-Type' => 'application/json' } ) rescue true end else if @debug && !@logger.nil? @logger.info "Logging to Loggly disabled" @logger.info messageBody end end end def setupLoggly(tag) @logglyTag = tag end def encrypt(data) Base64.encode64(data) end end