Parent

Included Modules

Vmail::ImapClient

Attributes

max_seqno[RW]

Public Class Methods

daemon(config) click to toggle source
# File lib/vmail/imap_client.rb, line 829
def self.daemon(config)
  $gmail = self.start(config)
  use_uri = config['drb_uri'] || nil # redundant but explicit
  DRb.start_service(use_uri, $gmail)
  uri = DRb.uri
  puts "Starting gmail service at #{uri}"
  uri
end
new(config) click to toggle source
# File lib/vmail/imap_client.rb, line 18
def initialize(config)
  @username, @password = config['username'], config['password']
  @name = config['name']
  @signature = config['signature']
  @mailbox = nil
  @logger = Logger.new(config['logfile'] || STDERR)
  @logger.level = Logger::DEBUG
  @imap_server = config['server'] || 'imap.gmail.com'
  @imap_port = config['port'] || 993
  @current_mail = nil
  @current_message_uid = nil
  @width = 140
end
start(config) click to toggle source
# File lib/vmail/imap_client.rb, line 823
def self.start(config)
  imap_client  = Vmail::ImapClient.new config
  imap_client.open
  imap_client
end

Public Instance Methods

add_more_message_line(res, start_seqno) click to toggle source
# File lib/vmail/imap_client.rb, line 399
def add_more_message_line(res, start_seqno)
  log "Add_more_message_line for start_seqno #{start_seqno}"
  if @all_search
    return res if start_seqno.nil?
    remaining = start_seqno - 1
  else # filter search
    remaining = (@ids.index(start_seqno) || 1) - 1
  end
  if remaining < 1
    log "None remaining"
    return "Showing all matches\n" + res
  end
  log "Remaining messages: #{remaining}"
  ">  Load #{[100, remaining].min} more messages. #{remaining} remaining.\n" + res
end
append_to_file(uid_set, file) click to toggle source
# File lib/vmail/imap_client.rb, line 599
def append_to_file(uid_set, file)
  uid_set = uid_set.split(',').map(&:to_i)
  log "Append to file uid set #{uid_set.inspect} to file: #{file}"
  uid_set.each do |uid|
    message = show_message(uid)
    File.open(file, 'a') {|f| f.puts(divider('=') + "\n" + message + "\n\n")}
    subject = (message[/^subject:(.*)/,1] || '').strip
    log "Appended message '#{subject}'"
  end
  "Printed #{uid_set.size} message#{uid_set.size == 1 ? '' : 's'} to #{file.strip}"
end
backup_sent_message(message) click to toggle source
# File lib/vmail/imap_client.rb, line 678
def backup_sent_message(message)
  File.open(SENT_MESSAGES_FILE, "a") {|f| f.write(divider('-') + "\n" + format_sent_message(message))}
end
clear_cached_message() click to toggle source
# File lib/vmail/imap_client.rb, line 91
def clear_cached_message
  return unless STDIN.tty?
  log "CLEARING CACHED MESSAGE"
  @current_mail = nil
  @current_message_uid = nil
  @current_message = nil
end
close() click to toggle source
# File lib/vmail/imap_client.rb, line 57
def close
  log "Closing connection"
  Timeout::timeout(10) do
    @imap.close rescue Net::IMAP::BadResponseError
    @imap.disconnect rescue IOError
  end
rescue Timeout::Error
end
copy_to(uid_set, mailbox) click to toggle source
# File lib/vmail/imap_client.rb, line 565
def copy_to(uid_set, mailbox)
  uid_set = uid_set.split(',').map(&:to_i)
  if mailbox_aliases[mailbox]
    mailbox = mailbox_aliases[mailbox]
  end
  create_if_necessary mailbox
  log "Copying #{uid_set.inspect} to #{mailbox}"
  spawn_thread_if_tty do 
    log @imap.uid_copy(uid_set, mailbox)
    log "Copied uid_set #{uid_set.inspect} to #{mailbox}"
  end
end
create_if_necessary(mailbox) click to toggle source
# File lib/vmail/imap_client.rb, line 588
def create_if_necessary(mailbox)
  current_mailboxes = mailboxes.map {|m| mailbox_aliases[m] || m}
  if !current_mailboxes.include?(mailbox)
    log "Current mailboxes: #{current_mailboxes.inspect}"
    log "Creating mailbox #{mailbox}"
    log @imap.create(mailbox) 
    @mailboxes = nil # force reload ...
    list_mailboxes
  end
end
decrement_max_seqno(num) click to toggle source
# File lib/vmail/imap_client.rb, line 346
def decrement_max_seqno(num)
  return unless STDIN.tty?
  log "Decremented max seqno from #{self.max_seqno} to #{self.max_seqno - num}"
  self.max_seqno -= num
end
deliver(text) click to toggle source
# File lib/vmail/imap_client.rb, line 682
def deliver(text)
  # parse the text. The headers are yaml. The rest is text body.
  require 'net/smtp'
  # prime_connection
  mail = new_mail_from_input(text)
  mail.delivery_method(*smtp_settings)
  res = mail.deliver!
  log res.inspect
  log "\n"
  msg = if res.is_a?(Mail::Message)
    backup_sent_message mail
    "Message '#{mail.subject}' sent and saved to #{SENT_MESSAGES_FILE}"
  else
    "Failed to deliver message '#{mail.subject}'!"
  end
  log msg
  msg
end
divider(str) click to toggle source
# File lib/vmail/imap_client.rb, line 661
def divider(str)
  str * DIVIDER_WIDTH
end
extract_row_data(fetch_data) click to toggle source

TODO extract this to another class or module and write unit tests

# File lib/vmail/imap_client.rb, line 217
def extract_row_data(fetch_data)
  seqno = fetch_data.seqno
  uid = fetch_data.attr['UID']
  # log "fetched seqno #{seqno} uid #{uid}"
  envelope = fetch_data.attr["ENVELOPE"]
  size = fetch_data.attr["RFC822.SIZE"]
  flags = fetch_data.attr["FLAGS"]
  address_struct = if @mailbox == mailbox_aliases['sent'] 
                     structs = envelope.to || envelope.cc
                     structs.nil? ? nil : structs.first 
                   else
                     envelope.from.first
                   end
  address = if address_struct.nil?
              "Unknown"
            elsif address_struct.name
              "#{Mail::Encodings.unquote_and_convert_to(address_struct.name, 'UTF-8')} <#{[address_struct.mailbox, address_struct.host].join('@')}>"
            else
              [Mail::Encodings.unquote_and_convert_to(address_struct.mailbox, 'UTF-8'), Mail::Encodings.unquote_and_convert_to(address_struct.host, 'UTF-8')].join('@') 
            end
  if @mailbox == mailbox_aliases['sent'] && envelope.to && envelope.cc
    total_recips = (envelope.to + envelope.cc).size
    address += " + #{total_recips - 1}"
  end
  date = begin 
           Time.parse(envelope.date).localtime
         rescue ArgumentError
           Time.now
         end

  date_formatted = if date.year != Time.now.year
                     date.strftime "%b %d %Y" rescue envelope.date.to_s 
                   else 
                     date.strftime "%b %d %I:%M%P" rescue envelope.date.to_s 
                   end
  subject = envelope.subject || ''
  subject = Mail::Encodings.unquote_and_convert_to(subject, 'UTF-8')
  flags = format_flags(flags)
  mid_width = @width - 38
  address_col_width = (mid_width * 0.3).ceil
  subject_col_width = (mid_width * 0.7).floor
  identifier = [seqno.to_i, uid.to_i].join(':')
  row_text = [ flags.col(2),
               (date_formatted || '').col(14),
               address.col(address_col_width),
               subject.col(subject_col_width), 
               number_to_human_size(size).rcol(7), 
               identifier.to_s
  ].join(' | ')
  {:uid => uid, :seqno => seqno, :row_text => row_text}
rescue 
  log "Error extracting header for uid #{uid} seqno #{seqno}: #$!\n#{$!.backtrace}"
  row_text = "#{seqno.to_s} : error extracting this header"
  {:uid => uid, :seqno => seqno, :row_text => row_text}
end
fetch_and_cache(uid) click to toggle source
# File lib/vmail/imap_client.rb, line 454
def fetch_and_cache(uid)
  if data = message_cache[[@mailbox, uid]] 
    return data
  end
  fetch_data = reconnect_if_necessary do 
    res = @imap.uid_fetch(uid, ["FLAGS", "RFC822", "RFC822.SIZE"])
    if res.nil?
      # retry one more time ( find a more elegant way to do this )
      res = @imap.uid_fetch(uid, ["FLAGS", "RFC822", "RFC822.SIZE"])
    end
    res[0] 
  end
  # USE THIS
  size = fetch_data.attr["RFC822.SIZE"]
  flags = fetch_data.attr["FLAGS"]
  mail = Mail.new(fetch_data.attr['RFC822'])
  formatter = Vmail::MessageFormatter.new(mail)
  message_text = #{@mailbox} uid:#{uid} #{number_to_human_size size} #{flags.inspect} #{format_parts_info(formatter.list_parts)}#{divider '-'}#{format_headers(formatter.extract_headers)}#{formatter.process_body}
  # log "Storing message_cache[[#{@mailbox}, #{uid}]]"
  d = {:mail => mail, :size => size, :message_text => message_text, :seqno => fetch_data.seqno, :flags => flags}
  message_cache[[@mailbox, uid]] = d
rescue
  msg = "Error encountered parsing message uid  #{uid}:\n#{$!}\n#{$!.backtrace.join("\n")}" + 
    "\n\nRaw message:\n\n" + mail.to_s
  log msg
  log message_text
  {:message_text => msg}
end
fetch_envelopes(id_set, are_uids, is_update) click to toggle source
# File lib/vmail/imap_client.rb, line 198
def fetch_envelopes(id_set, are_uids, is_update)
  results = reconnect_if_necessary do 
    if are_uids
      @imap.uid_fetch(id_set, ["FLAGS", "ENVELOPE", "RFC822.SIZE", "UID" ])
    else
      @imap.fetch(id_set, ["FLAGS", "ENVELOPE", "RFC822.SIZE", "UID" ])
    end
  end
  if results.nil?
    error = "Expected fetch results but got nil"
    log(error) && raise(error)
  end
  log "- extracting headers"
  new_message_rows = results.map {|x| extract_row_data(x) }
  log "- returning #{new_message_rows.size} new rows and caching result"  
  new_message_rows
end
fetch_row_text(id_set, are_uids=false, is_update=false) click to toggle source

id_set may be a range, array, or string

# File lib/vmail/imap_client.rb, line 182
def fetch_row_text(id_set, are_uids=false, is_update=false)
  log "Fetch_row_text: #{id_set.inspect}"
  if id_set.is_a?(String)
    id_set = id_set.split(',')
  end
  if id_set.to_a.empty?
    log "- empty set"
    return ""
  end
  new_message_rows = fetch_envelopes(id_set, are_uids, is_update)
  new_message_rows.map {|x| x[:row_text]}.join("\n")
rescue # Encoding::CompatibilityError (only in 1.9.2)
  log "Error in fetch_row_text:\n#{$!}\n#{$!.backtrace}"
  new_message_rows.map {|x| Iconv.conv('US-ASCII//TRANSLIT//IGNORE', 'UTF-8', x[:row_text])}.join("\n")
end
flag(uid_set, action, flg) click to toggle source

id_set is a string comming from the vim client action is -FLAGS or +FLAGS

# File lib/vmail/imap_client.rb, line 507
def flag(uid_set, action, flg)
  log "Flag #{uid_set} #{flg} #{action}"
  uid_set = uid_set.split(',').map(&:to_i)
  if flg == 'Deleted'
    log "Deleting uid_set: #{uid_set.inspect}"
    decrement_max_seqno(uid_set.size)
    # for delete, do in a separate thread because deletions are slow
    spawn_thread_if_tty do 
      unless @mailbox == mailbox_aliases['trash']
        log "@imap.uid_copy #{uid_set.inspect} to #{mailbox_aliases['trash']}"
        log @imap.uid_copy(uid_set, mailbox_aliases['trash'])
      end
      log "@imap.uid_store #{uid_set.inspect} #{action} [#{flg.to_sym}]"
      log @imap.uid_store(uid_set, action, [flg.to_sym])
      reload_mailbox
      clear_cached_message
    end
  elsif flg == 'spam' || flg == mailbox_aliases['spam'] 
    log "Marking as spam uid_set: #{uid_set.inspect}"
    decrement_max_seqno(uid_set.size)
    spawn_thread_if_tty do 
      log "@imap.uid_copy #{uid_set.inspect} to #{mailbox_aliases['spam']}"
      log @imap.uid_copy(uid_set, mailbox_aliases['spam']) 
      log "@imap.uid_store #{uid_set.inspect} #{action} [:Deleted]"
      log @imap.uid_store(uid_set, action, [:Deleted])
      reload_mailbox
      clear_cached_message
    end
  else
    log "Flagging uid_set: #{uid_set.inspect}"
    spawn_thread_if_tty do
      log "@imap.uid_store #{uid_set.inspect} #{action} [#{flg.to_sym}]"
      log @imap.uid_store(uid_set, action, [flg.to_sym])
    end
  end
end
format_flags(flags) click to toggle source

flags is an array like [:Flagged, :Seen]

# File lib/vmail/imap_client.rb, line 291
def format_flags(flags)
  # other flags like "Old" should be hidden here
  flags = flags.map {|flag| FLAGMAP[flag] || flag}
  flags.delete("Old")
  if flags.delete(:Seen).nil?
    flags << '+' # unread
  end
  flags.join('')
end
format_headers(hash) click to toggle source
# File lib/vmail/imap_client.rb, line 619
def format_headers(hash)
  lines = []
  hash.each_pair do |key, value|
    if value.is_a?(Array)
      value = value.join(", ")
    end
    lines << "#{key.gsub("_", '-')}: #{value}"
  end
  lines.join("\n")
end
format_parts_info(parts) click to toggle source
# File lib/vmail/imap_client.rb, line 498
def format_parts_info(parts)
  lines = parts.select {|part| part !~ %{text/plain}}
  if lines.size > 0
    "\n#{lines.join("\n")}"
  end
end
format_sent_message(mail) click to toggle source
# File lib/vmail/imap_client.rb, line 667
def format_sent_message(mail)
  formatter = Vmail::MessageFormatter.new(mail)
  message_text = Sent Message #{self.format_parts_info(formatter.list_parts)}#{format_headers(formatter.extract_headers)}#{formatter.process_body}
end
forward_template() click to toggle source
# File lib/vmail/imap_client.rb, line 647
def forward_template
  original_body = @current_message.split(/\n-{20,}\n/, 2)[1]
  formatter = Vmail::MessageFormatter.new(@current_mail)
  headers = formatter.extract_headers
  subject = headers['subject']
  if subject !~ /Fwd: /
    subject = "Fwd: #{subject}"
  end

  new_message_template(subject, false) + 
    "\n---------- Forwarded message ----------\n" +
    original_body + signature
end
get_highest_message_id() click to toggle source
# File lib/vmail/imap_client.rb, line 99
def get_highest_message_id
  # get highest message ID
  res = @imap.fetch([1,"*"], ["ENVELOPE"])
  if res 
    @num_messages = res[-1].seqno
    log "HIGHEST ID: #@num_messages"
  else
    @num_messages = 1
    log "NO HIGHEST ID: setting @num_messages to 1"
  end
end
get_mailbox_status() click to toggle source

not used for anything

# File lib/vmail/imap_client.rb, line 112
def get_mailbox_status
  return
  @status = @imap.status(@mailbox,  ["MESSAGES", "RECENT", "UNSEEN"])
  log "Mailbox status: #{@status.inspect}"
end
handle_error(error) click to toggle source
# File lib/vmail/imap_client.rb, line 799
def handle_error(error)
  log error
end
list_mailboxes() click to toggle source
# File lib/vmail/imap_client.rb, line 139
def list_mailboxes
  log 'loading mailboxes...'
  @mailboxes ||= (@imap.list("", "*") || []).
    select {|struct| struct.attr.none? {|a| a == :Noselect} }.
    map {|struct| struct.name}.uniq
  @mailboxes.delete("INBOX")
  @mailboxes.unshift("INBOX")
  log "Loaded mailboxes: #{@mailboxes.inspect}"
  @mailboxes = @mailboxes.map {|name| mailbox_aliases.invert[name] || name}
  @mailboxes.join("\n")
end
log(string) click to toggle source
# File lib/vmail/imap_client.rb, line 795
def log(string)
  @logger.debug string
end
mailbox_aliases() click to toggle source

do this just once

# File lib/vmail/imap_client.rb, line 152
def mailbox_aliases
  return @mailbox_aliases if @mailbox_aliases
  aliases = {"sent" => "Sent Mail",
             "all" => "All Mail",
             "starred" => "Starred",
             "important" => "Important",
             "drafts" => "Drafts",
             "spam" => "Spam",
             "trash" => "Trash"}
  @mailbox_aliases = {}
  aliases.each do |shortname, fullname|
    [ "[Gmail]", "[Google Mail" ].each do |prefix|
      if @mailboxes.include?( "#{prefix}/#{fullname}" )
        @mailbox_aliases[shortname] =  "#{prefix}/#{fullname}"
      end
    end
  end
  log "Setting aliases to #{@mailbox_aliases.inspect}"
  @mailbox_aliases
end
mailboxes() click to toggle source

called internally, not by vim client

# File lib/vmail/imap_client.rb, line 174
def mailboxes
  if @mailboxes.nil?
    list_mailboxes
  end
  @mailboxes
end
message_cache() click to toggle source

holds mail objects keyed by [mailbox, uid]

# File lib/vmail/imap_client.rb, line 33
def message_cache
  @message_cache ||= {}
  size = @message_cache.values.reduce(0) {|sum, x| sum + x[:size]}
  if size > 2_000_000 # TODO make this configurable
    log "Pruning message cache; message cache is consuming #{number_to_human_size size}"
    @message_cache.keys[0, @message_cache.size / 2].each {|k| @message_cache.delete(k)}
  end
  @message_cache
end
more_messages(message_id, limit=100) click to toggle source

gets 100 messages prior to id

# File lib/vmail/imap_client.rb, line 380
def more_messages(message_id, limit=100)
  log "More_messages: message_id #{message_id}"
  message_id = message_id.to_i
  if @all_search 
    x = [(message_id - limit), 0].max
    y = [message_id - 1, 0].max

    res = fetch_row_text((x..y))
    add_more_message_line(res, x)
  else # filter search query
    log "@start_index #@start_index"
    x = [(@start_index - limit), 0].max
    y = [@start_index - 1, 0].max
    @start_index = x
    res = fetch_row_text(@ids[x..y]) 
    add_more_message_line(res, @ids[x])
  end
end
move_to(uid_set, mailbox) click to toggle source
# File lib/vmail/imap_client.rb, line 544
def move_to(uid_set, mailbox)
  uid_set = uid_set.split(',').map(&:to_i)
  decrement_max_seqno(uid_set.size)
  log "Move #{uid_set.inspect} to #{mailbox}"
  if mailbox == 'all'
    log "Archiving messages"
  end
  if mailbox_aliases[mailbox]
    mailbox = mailbox_aliases[mailbox]
  end
  create_if_necessary mailbox
  log "Moving uid_set: #{uid_set.inspect} to #{mailbox}"
  spawn_thread_if_tty do 
    log @imap.uid_copy(uid_set, mailbox)
    log @imap.uid_store(uid_set, '+FLAGS', [:Deleted])
    reload_mailbox
    clear_cached_message
    log "Moved uid_set #{uid_set.inspect} to #{mailbox}"
  end
end
new_mail_from_input(text) click to toggle source
# File lib/vmail/imap_client.rb, line 701
def new_mail_from_input(text)
  require 'mail'
  mail = Mail.new
  raw_headers, raw_body = *text.split(/\n\s*\n/, 2)
  headers = {}
  raw_headers.split("\n").each do |line|
    key, value = *line.split(/:\s*/, 2)
    log [key, value].join(':')
    if %(from to cc bcc).include?(key)
      value = quote_addresses(value)
    end
    headers[key] = value
  end
  log "Delivering message with headers: #{headers.to_yaml}"
  mail.from = headers['from'] || @username
  mail.to = headers['to'] #.split(/,\s+/)
  mail.cc = headers['cc'] #&& headers['cc'].split(/,\s+/)
  mail.bcc = headers['bcc'] #&& headers['cc'].split(/,\s+/)
  mail.subject = headers['subject']
  mail.from ||= @username
  # attachments are added as a snippet of YAML after a blank line
  # after the headers, and followed by a blank line
  if (attachments = raw_body.split(/\n\s*\n/, 2)[0]) =~ /^attach(ment|ments)*:/
    files = YAML::load(attachments).values.flatten
    log "Attach: #{files}"
    files.each do |file|
      if File.directory?(file)
        Dir.glob("#{file}/*").each {|f| mail.add_file(f) if File.size?(f)}
      else
        mail.add_file(file) if File.size?(file)
      end
    end
    mail.text_part do
      body raw_body.split(/\n\s*\n/, 2)[1]
    end

  else
    mail.text_part do
      body raw_body
    end
  end
  mail
end
new_message_template(subject = nil, append_signature = true) click to toggle source
# File lib/vmail/imap_client.rb, line 611
def new_message_template(subject = nil, append_signature = true)
  headers = {'from' => "#{@name} <#{@username}>",
    'to' => nil,
    'subject' => subject
  }
  format_headers(headers) + (append_signature ? ("\n\n" + signature) : "\n\n")
end
number_to_human_size(number) click to toggle source

borrowed from ActionView/Helpers

# File lib/vmail/imap_client.rb, line 276
def number_to_human_size(number)
  if number.to_i < 1024
    "<1kb" # round up to 1kh
  else
    max_exp = UNITS.size - 1
    exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
    exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
    number  /= 1024 ** exponent
    unit = UNITS[exponent]
    "#{number}#{unit}"
  end
end
open() click to toggle source
# File lib/vmail/imap_client.rb, line 43
def open
  @imap = Net::IMAP.new(@imap_server, @imap_port, true, nil, false)
  log @imap.login(@username, @password)
  list_mailboxes # prefetch mailbox list
end
open_html_part() click to toggle source
# File lib/vmail/imap_client.rb, line 762
def open_html_part
  log "Open_html_part"
  log @current_mail.parts.inspect
  multipart = @current_mail.parts.detect {|part| part.multipart?}
  html_part = if multipart 
                multipart.parts.detect {|part| part.header["Content-Type"].to_s =~ /text\/html/}
              elsif ! @current_mail.parts.empty?
                @current_mail.parts.detect {|part| part.header["Content-Type"].to_s =~ /text\/html/}
              else
                @current_mail.body
              end
  return if html_part.nil?
  outfile = 'part.html'
  File.open(outfile, 'w') {|f| f.puts(html_part.decoded)}
  # client should handle opening the html file
  return outfile
end
prefetch_adjacent(index) click to toggle source

deprecated

# File lib/vmail/imap_client.rb, line 490
def prefetch_adjacent(index)
  Thread.new do 
    [index + 1, index - 1].each do |idx|
      fetch_and_cache(idx)
    end
  end
end
prime_connection() click to toggle source
# File lib/vmail/imap_client.rb, line 125
def prime_connection
  return if @ids.nil? || @ids.empty?
  reconnect_if_necessary(4) do 
    # this is just to prime the IMAP connection
    # It's necessary for some reason before update and deliver. 
    log "Priming connection"
    res = @imap.fetch(@ids[-1], ["ENVELOPE"])
    if res.nil?
      # just go ahead, just log
      log "Priming connection didn't work, connection seems broken, but still going ahead..."
    end
  end 
end
reconnect_if_necessary(timeout = 60, &block) click to toggle source
# File lib/vmail/imap_client.rb, line 803
def reconnect_if_necessary(timeout = 60, &block)
  # if this times out, we know the connection is stale while the user is
  # trying to update
  Timeout::timeout(timeout) do
    block.call
  end
rescue IOError, Errno::EADDRNOTAVAIL, Errno::ECONNRESET, Timeout::Error
  log "Error: #{$!}"
  log "Attempting to reconnect"
  close
  log(revive_connection)
  # hope this isn't an endless loop
  reconnect_if_necessary do 
    block.call
  end
rescue
  log "Error: #{$!}"
  raise
end
reload_mailbox() click to toggle source
# File lib/vmail/imap_client.rb, line 86
def reload_mailbox
  return unless STDIN.tty?
  select_mailbox(@mailbox, true)
end
reply_template(replyall=false) click to toggle source
# File lib/vmail/imap_client.rb, line 630
def reply_template(replyall=false)
  log "Sending reply template"
  if @current_mail.nil?
    log "- missing @current mail!"
    return nil
  end
  # user reply_template class
  reply_headers = Vmail::ReplyTemplate.new(@current_mail, @username, @name, replyall).reply_headers
  body = reply_headers.delete(:body)
  format_headers(reply_headers) + "\n\n\n" + body + signature
end
revive_connection() click to toggle source
# File lib/vmail/imap_client.rb, line 118
def revive_connection
  log "Reviving connection"
  open
  log "Reselecting mailbox #@mailbox"
  @imap.select(@mailbox)
end
save_attachments(dir) click to toggle source
# File lib/vmail/imap_client.rb, line 745
def save_attachments(dir)
  log "Save_attachments #{dir}"
  if !@current_mail
    log "Missing a current message"
  end
  return unless dir && @current_mail
  attachments = @current_mail.attachments
  `mkdir -p #{dir}`
  saved = attachments.map do |x|
    path = File.join(dir, x.filename)
    log "Saving #{path}"
    File.open(path, 'wb') {|f| f.puts x.decoded}
    path
  end
  "Saved:\n" + saved.map {|x| "- #{x}"}.join("\n")
end
search(query) click to toggle source
# File lib/vmail/imap_client.rb, line 301
def search(query)
  query = Vmail::Query.parse(query)
  @limit = query.shift.to_i
  # a limit of zero is effectively no limit
  if @limit == 0
    @limit = @num_messages
  end
  if query.size == 1 && query[0].downcase == 'all'
    # form a sequence range
    query.unshift [[@num_messages - @limit + 1 , 1].max, @num_messages].join(':')
    @all_search = true
  else # this is a special query search
    # set the target range to the whole set
    query.unshift "1:#@num_messages"
    @all_search = false
  end
  @query = query.map {|x| x.to_s.downcase}
  query_string = Vmail::Query.args2string(@query)
  log "Search query: #{@query} > #{query_string.inspect}"
  log "- @all_search #{@all_search}"
  @query = query
  @ids = reconnect_if_necessary(180) do # increase timeout to 3 minutes
    @imap.search(query_string)
  end
  # save ids in @ids, because filtered search relies on it
  fetch_ids = if @all_search
                @ids
              else #filtered search
                @start_index = [@ids.length - @limit, 0].max
                @ids[@start_index..-1]
              end
  self.max_seqno = @ids[-1]
  log "- search query got #{@ids.size} results; max seqno: #{self.max_seqno}" 
  clear_cached_message
  res = fetch_row_text(fetch_ids)
  if STDOUT.tty?
    add_more_message_line(res, fetch_ids[0])
  else
    # non interactive mode
    puts [@mailbox, res].join("\n")
  end
rescue
  log "ERROR:\n#{$!.inspect}\n#{$!.backtrace.join("\n")}"
end
select_mailbox(mailbox, force=false) click to toggle source
# File lib/vmail/imap_client.rb, line 66
def select_mailbox(mailbox, force=false)
  if mailbox_aliases[mailbox]
    mailbox = mailbox_aliases[mailbox]
  end
  if mailbox == @mailbox && !force
    return
  end
  log "Selecting mailbox #{mailbox.inspect}"
  reconnect_if_necessary(15) do 
    log @imap.select(mailbox)
  end
  log "Done"
  @mailbox = mailbox
  log "Getting mailbox status"
  get_mailbox_status
  log "Getting highest message id"
  get_highest_message_id
  return "OK"
end
show_message(uid, raw=false) click to toggle source
# File lib/vmail/imap_client.rb, line 415
def show_message(uid, raw=false)
  log "Show message: #{uid}"
  return @current_mail.to_s if raw 
  uid = uid.to_i
  if uid == @current_message_uid 
    return @current_message 
  end

  #prefetch_adjacent(index) # deprecated

  # TODO keep state in vim buffers, instead of on Vmail Ruby client
  # envelope_data[:row_text] = envelope_data[:row_text].gsub(/^\+ /, '  ').gsub(/^\*\+/, '* ') # mark as read in cache
  #seqno = envelope_data[:seqno]

  log "Showing message uid: #{uid}"
  data = if x = message_cache[[@mailbox, uid]]
           log "- message cache hit"
           x
         else 
           log "- fetching and storing to message_cache[[#{@mailbox}, #{uid}]]"
           fetch_and_cache(uid)
         end
  if data.nil?
    # retry, though this is a hack!
    log "- data is nil. retrying..."
    return show_message(uid, raw)
  end
  # make this more DRY later by directly using a ref to the hash
  mail = data[:mail]
  size = data[:size] 
  @current_message_uid = uid
  log "- setting @current_mail"
  @current_mail = mail # used later to show raw message or extract attachments if any
  @current_message = data[:message_text]
rescue
  log "Parsing error"
  "Error encountered parsing this message:\n#{$!}\n#{$!.backtrace.join("\n")}"
end
signature() click to toggle source
# File lib/vmail/imap_client.rb, line 642
def signature
  return '' unless @signature
  "\n\n#@signature"
end
smtp_settings() click to toggle source
# File lib/vmail/imap_client.rb, line 785
def smtp_settings
  [:smtp, {:address => "smtp.gmail.com",
  :port => 587,
  :domain => 'gmail.com',
  :user_name => @username,
  :password => @password,
  :authentication => 'plain',
  :enable_starttls_auto => true}]
end
spawn_thread_if_tty(&block) click to toggle source
# File lib/vmail/imap_client.rb, line 578
def spawn_thread_if_tty(&block) 
  if STDIN.tty?
    Thread.new do 
      reconnect_if_necessary(10, &block)
    end
  else
    block.call
  end
end
update() click to toggle source
# File lib/vmail/imap_client.rb, line 352
def update
  prime_connection
  old_num_messages = @num_messages
  # we need to re-select the mailbox to get the new highest id
  reload_mailbox
  update_query = @query.dup
  # set a new range filter
  # this may generate a negative rane, e.g., "19893:19992" but that seems harmless
  update_query[0] = "#{old_num_messages}:#{@num_messages}"
  ids = reconnect_if_necessary { 
    log "Search #update_query"
    @imap.search(Vmail::Query.args2string(update_query))
  }
  log "- got seqnos: #{ids.inspect}"
  log "- getting seqnos > #{self.max_seqno}"
  new_ids = ids.select {|seqno| seqno > self.max_seqno}
  @ids = @ids + new_ids
  log "- update: new uids: #{new_ids.inspect}"
  if !new_ids.empty?
    self.max_seqno = new_ids[-1]
    res = fetch_row_text(new_ids, false, true)
    res
  else
    ''
  end
end
window_width=(width) click to toggle source
# File lib/vmail/imap_client.rb, line 780
def window_width=(width)
  log "Setting window width to #{width}"
  @width = width.to_i
end
with_open() click to toggle source

expects a block, closes on finish

# File lib/vmail/imap_client.rb, line 50
def with_open
  @imap = Net::IMAP.new(@imap_server, @imap_port, true, nil, false)
  log @imap.login(@username, @password)
  yield self
  close
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.