class Redwood::MBox

Constants

BREAK_RE

Attributes

labels[R]

Public Class Methods

is_break_line?(l) click to toggle source
# File lib/sup/mbox.rb, line 178
def self.is_break_line? l
  l =~ BREAK_RE or return false
  time = $1
  begin
    ## hack -- make Time.parse fail when trying to substitute values from Time.now
    Time.parse time, Time.at(0)
    true
  rescue NoMethodError, ArgumentError
    warn "found invalid date in potential mbox split line, not splitting: #{l.inspect}"
    false
  end
end
new(uri_or_fp, usual=true, archived=false, id=nil, labels=nil) click to toggle source

uri_or_fp is horrific. need to refactor.

Calls superclass method
# File lib/sup/mbox.rb, line 15
def initialize uri_or_fp, usual=true, archived=false, id=nil, labels=nil
  @mutex = Mutex.new
  @labels = Set.new((labels || []) - LabelManager::RESERVED_LABELS)

  case uri_or_fp
  when String
    @expanded_uri = Source.expand_filesystem_uri(uri_or_fp)
    parts = @expanded_uri.match /^([a-zA-Z0-9]*:(\/\/)?)(.*)/
    if parts
      prefix = parts[1]
      @path = parts[3]
      uri = URI(prefix + URI.encode(@path, URI_ENCODE_CHARS))
    else
      uri = URI(URI.encode @expanded_uri, URI_ENCODE_CHARS)
      @path = uri.path
    end

    raise ArgumentError, "not an mbox uri" unless uri.scheme == "mbox"
    raise ArgumentError, "mbox URI ('#{uri}') cannot have a host: #{uri.host}" if uri.host
    raise ArgumentError, "mbox URI must have a path component" unless uri.path
    @f = nil
  else
    @f = uri_or_fp
    @path = uri_or_fp.path
    @expanded_uri = "mbox://#{URI.encode @path, URI_ENCODE_CHARS}"
  end

  super uri_or_fp, usual, archived, id
end
suggest_labels_for(path) click to toggle source
# File lib/sup/mbox.rb, line 48
def self.suggest_labels_for path
  ## heuristic: use the filename as a label, unless the file
  ## has a path that probably represents an inbox.
  if File.dirname(path) =~ /\b(var|usr|spool)\b/
    []
  else
    [File.basename(path).downcase.intern]
  end
end

Public Instance Methods

default_labels() click to toggle source
# File lib/sup/mbox.rb, line 140
def default_labels
  [:inbox, :unread]
end
each_raw_message_line(offset) { |l| ... } click to toggle source

apparently it's a million times faster to call this directly if we're just moving messages around on disk, than reading things into memory with raw_message.

# File lib/sup/mbox.rb, line 130
def each_raw_message_line offset
  @mutex.synchronize do
    ensure_open
    @f.seek offset
    until @f.eof? || MBox::is_break_line?(l = @f.gets)
      yield l
    end
  end
end
file_path() click to toggle source
# File lib/sup/mbox.rb, line 45
def file_path; @path end
first_new_message() click to toggle source

offset of first new message or nil

# File lib/sup/mbox.rb, line 174
def first_new_message
  next_offset(last_indexed_message || 0)
end
go_idle() click to toggle source
# File lib/sup/mbox.rb, line 63
def go_idle
  @mutex.synchronize do
    return if @f.nil? or @path.nil?
    @f.close
    @f = nil
  end
end
is_source_for?(uri;) click to toggle source
Calls superclass method
# File lib/sup/mbox.rb, line 46
def is_source_for? uri; super || (uri == @expanded_uri) end
last_indexed_message() click to toggle source

TODO optimize this by iterating over allterms list backwards or storing source_info negated

# File lib/sup/mbox.rb, line 169
def last_indexed_message
  benchmark(:mbox_read_index) { Index.instance.enum_for(:each_source_info, self.id).map(&:to_i).max }
end
load_header(offset) click to toggle source
# File lib/sup/mbox.rb, line 71
def load_header offset
  header = nil
  @mutex.synchronize do
    ensure_open
    @f.seek offset
    header = parse_raw_email_header @f
  end
  header
end
load_message(offset) click to toggle source
# File lib/sup/mbox.rb, line 81
def load_message offset
  @mutex.synchronize do
    ensure_open
    @f.seek offset
    begin
      ## don't use RMail::Mailbox::MBoxReader because it doesn't properly ignore
      ## "From" at the start of a message body line.
      string = ""
      until @f.eof? || MBox::is_break_line?(l = @f.gets)
        string << l
      end
      RMail::Parser.read string
    rescue RMail::Parser::Error => e
      raise FatalSourceError, "error parsing mbox file: #{e.message}"
    end
  end
end
next_offset(offset) click to toggle source
# File lib/sup/mbox.rb, line 157
def next_offset offset
  @mutex.synchronize do
    ensure_open
    @f.seek offset
    nil while line = @f.gets and not MBox::is_break_line? line
    offset = @f.tell
    offset != File.size(@f) ? offset : nil
  end
end
poll() { |:add, :info => offset, :labels => (labels + default_labels), :progress => (offset - first_offset).to_f/end_offset| ... } click to toggle source
# File lib/sup/mbox.rb, line 144
def poll
  first_offset = first_new_message
  offset = first_offset
  end_offset = File.size @f
  while offset and offset < end_offset
    yield :add,
      :info => offset,
      :labels => (labels + default_labels),
      :progress => (offset - first_offset).to_f/end_offset
    offset = next_offset offset
  end
end
raw_header(offset) click to toggle source
# File lib/sup/mbox.rb, line 99
def raw_header offset
  ret = ""
  @mutex.synchronize do
    ensure_open
    @f.seek offset
    until @f.eof? || (l = @f.gets) =~ /^\r*$/
      ret << l
    end
  end
  ret
end
raw_message(offset) click to toggle source
# File lib/sup/mbox.rb, line 111
def raw_message offset
  ret = ""
  each_raw_message_line(offset) { |l| ret << l }
  ret
end
store_message(date, from_email) { |f| ... } click to toggle source
# File lib/sup/mbox.rb, line 117
def store_message date, from_email, &block
  need_blank = File.exist?(@path) && !File.zero?(@path)
  File.open(@path, "ab") do |f|
    f.puts if need_blank
    f.puts "From #{from_email} #{date.asctime}"
    yield f
  end
end

Private Instance Methods

ensure_open() click to toggle source
# File lib/sup/mbox.rb, line 58
def ensure_open
  @f = File.open @path, 'rb' if @f.nil?
end