Class: Redwood::MBox::Loader

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

Direct Known Subclasses

SentLoader

Instance Attribute Summary collapse

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

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.



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

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

Instance Attribute Details

#labelsObject

Returns the value of attribute labels



9
10
11
# File 'lib/sup/mbox/loader.rb', line 9

def labels
  @labels
end

Class Method Details

.suggest_labels_for(path) ⇒ Object



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

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

Instance Method Details

#checkObject



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

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.



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

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



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

def end_offset; File.size @f; end

#file_pathObject



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

def file_path; @path end

#is_source_for?(uri) ⇒ Boolean



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

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



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

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



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

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



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
152
# File 'lib/sup/mbox/loader.rb', line 117

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, (self.labels + [:unread]).uniq]
end

#raw_header(offset) ⇒ Object



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

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

#raw_message(offset) ⇒ Object



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

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

#start_offsetObject



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

def start_offset; 0; end