Class: Redwood::MBox::Loader

Inherits:
Source show all
Defined in:
lib/sup/mbox/loader.rb

Direct Known Subclasses

SentLoader

Instance Attribute Summary

Attributes inherited from Source

#cur_offset, #id, #uri

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Source

#==, #done?, #each, #reset!, #seek_to!, #to_s, #usual

Constructor Details

#initialize(uri_or_fp, start_offset = nil, usual = true, archived = false, id = nil, labels = []) ⇒ Loader

uri_or_fp is horrific. need to refactor.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/sup/mbox/loader.rb', line 11

def initialize uri_or_fp, start_offset=nil, usual=true, archived=false, id=nil, labels=[]
  @mutex = Mutex.new
  @labels = ((labels || []) - LabelManager::RESERVED_LABELS).uniq.freeze

  case uri_or_fp
  when String
    uri = URI(Source.expand_filesystem_uri(uri_or_fp))
    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 = File.open uri.path
    @path = uri.path
  else
    @f = uri_or_fp
    @path = uri_or_fp.path
  end

  super uri_or_fp, start_offset, usual, archived, id
end

Class Method Details

.suggest_labels_for(path) ⇒ Object



34
35
36
37
38
39
40
41
42
# File 'lib/sup/mbox/loader.rb', line 34

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).intern]
  end
end

Instance Method Details

#checkObject



44
45
46
47
48
# File 'lib/sup/mbox/loader.rb', line 44

def check
  if (cur_offset ||= start_offset) > end_offset
    raise OutOfSyncSourceError, "mbox file is smaller than last recorded message offset. Messages have probably been deleted by another client."
  end
end

#each_raw_message_line(offset) ⇒ Object

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.

i hoped never to have to move shit around on disk but sup-sync-back has to do it.



106
107
108
109
110
111
112
113
114
# File 'lib/sup/mbox/loader.rb', line 106

def each_raw_message_line offset
  @mutex.synchronize do
    @f.seek offset
    yield @f.gets
    until @f.eof? || (l = @f.gets) =~ BREAK_RE
      yield l
    end
  end
end

#end_offsetObject



51
# File 'lib/sup/mbox/loader.rb', line 51

def end_offset; File.size @f; end

#file_pathObject



31
# File 'lib/sup/mbox/loader.rb', line 31

def file_path; @path end

#is_source_for?(uri) ⇒ Boolean

Returns:

  • (Boolean)


32
# File 'lib/sup/mbox/loader.rb', line 32

def is_source_for? uri; super || (self.uri.is_a?(String) && (URI(Source.expand_filesystem_uri(uri)) == URI(Source.expand_filesystem_uri(self.uri)))) end

#load_header(offset) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/sup/mbox/loader.rb', line 53

def load_header offset
  header = nil
  @mutex.synchronize do
    @f.seek offset
    l = @f.gets
    unless l =~ BREAK_RE
      raise OutOfSyncSourceError, "mismatch in mbox file offset #{offset.inspect}: #{l.inspect}." 
    end
    header = MBox::read_header @f
  end
  header
end

#load_message(offset) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/sup/mbox/loader.rb', line 66

def load_message offset
  @mutex.synchronize do
    @f.seek offset
    begin
      RMail::Mailbox::MBoxReader.new(@f).each_message do |input|
        m = RMail::Parser.read(input)
        if m.body && m.body.is_a?(String)
          m.body.gsub!(/^>From /, "From ")
        end
        return m
      end
    rescue RMail::Parser::Error => e
      raise FatalSourceError, "error parsing mbox file: #{e.message}"
    end
  end
end

#nextObject



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/sup/mbox/loader.rb', line 116

def next
  returned_offset = nil
  next_offset = cur_offset

  begin
    @mutex.synchronize do
      @f.seek cur_offset

      ## cur_offset could be at one of two places here:

      ## 1. before a \n and a mbox separator, if it was previously at
      ##    EOF and a new message was added; or,
      ## 2. at the beginning of an mbox separator (in all other
      ##    cases).

      l = @f.gets or raise "next while at EOF"
      if l =~ /^\s*$/ # case 1
        returned_offset = @f.tell
        @f.gets # now we're at a BREAK_RE, so skip past it
      else # case 2
        returned_offset = cur_offset
        ## we've already skipped past the BREAK_RE, so just go
      end

      while(line = @f.gets)
        break if line =~ BREAK_RE
        next_offset = @f.tell
      end
    end
  rescue SystemCallError, IOError => e
    raise FatalSourceError, "Error reading #{@f.path}: #{e.message}"
  end

  self.cur_offset = next_offset
  [returned_offset, (@labels + [:unread]).uniq]
end

#raw_header(offset) ⇒ Object



83
84
85
86
87
88
89
90
91
92
# File 'lib/sup/mbox/loader.rb', line 83

def raw_header offset
  ret = ""
  @mutex.synchronize do
    @f.seek offset
    until @f.eof? || (l = @f.gets) =~ /^$/
      ret += l
    end
  end
  ret
end

#raw_message(offset) ⇒ Object



94
95
96
97
98
# File 'lib/sup/mbox/loader.rb', line 94

def raw_message offset
  ret = ""
  each_raw_message_line(offset) { |l| ret += l }
  ret
end

#start_offsetObject



50
# File 'lib/sup/mbox/loader.rb', line 50

def start_offset; 0; end