]> git.cworth.org Git - sup/blob - lib/sup/mbox/loader.rb
moved responsibility for labels out of source and back into subclasses.
[sup] / lib / sup / mbox / loader.rb
1 require 'rmail'
2
3 module Redwood
4 module MBox
5
6 class Loader < Source
7   attr_reader :labels
8
9   def initialize uri_or_fp, start_offset=nil, usual=true, archived=false, id=nil
10     super
11
12     @mutex = Mutex.new
13     @labels = [:unread]
14     @labels << :inbox unless archived?
15
16     case uri_or_fp
17     when String
18       raise ArgumentError, "not an mbox uri" unless uri_or_fp =~ %r!mbox://!
19
20       fn = uri_or_fp.sub(%r!^mbox://!, "")
21       ## heuristic: use the filename as a label, unless the file
22       ## has a path that probably represents an inbox.
23       @labels << File.basename(fn).intern unless File.dirname(fn) =~ /\b(var|usr|spool)\b/
24       @f = File.open fn
25     else
26       @f = uri_or_fp
27     end
28   end
29
30   def start_offset; 0; end
31   def end_offset; File.size @f; end
32
33   def load_header offset
34     header = nil
35     @mutex.synchronize do
36       @f.seek offset
37       l = @f.gets
38       unless l =~ BREAK_RE
39         self.broken_msg = "offset mismatch in mbox file offset #{offset.inspect}: #{l.inspect}. Run 'sup-import --rebuild #{to_s}' to correct this." 
40         raise SourceError, self.broken_msg
41       end
42       header = MBox::read_header @f
43     end
44     header
45   end
46
47   def load_message offset
48     @mutex.synchronize do
49       @f.seek offset
50       begin
51         RMail::Mailbox::MBoxReader.new(@f).each_message do |input|
52           return RMail::Parser.read(input)
53         end
54       rescue RMail::Parser::Error => e
55         raise SourceError, "error parsing message with rmail: #{e.message}"
56       end
57     end
58   end
59
60   def raw_header offset
61     ret = ""
62     @mutex.synchronize do
63       @f.seek offset
64       until @f.eof? || (l = @f.gets) =~ /^$/
65         ret += l
66       end
67     end
68     ret
69   end
70
71   def raw_full_message offset
72     ret = ""
73     @mutex.synchronize do
74       @f.seek offset
75       @f.gets # skip mbox header
76       until @f.eof? || (l = @f.gets) =~ BREAK_RE
77         ret += l
78       end
79     end
80     ret
81   end
82
83   def next
84     returned_offset = nil
85     next_offset = cur_offset
86
87     @mutex.synchronize do
88       @f.seek cur_offset
89
90       ## cur_offset could be at one of two places here:
91
92       ## 1. before a \n and a mbox separator, if it was previously at
93       ##    EOF and a new message was added; or,
94       ## 2. at the beginning of an mbox separator (in all other
95       ##    cases).
96
97       l = @f.gets or raise "next while at EOF"
98       if l =~ /^\s*$/ # case 1
99         returned_offset = @f.tell
100         @f.gets # now we're at a BREAK_RE, so skip past it
101       else # case 2
102         returned_offset = cur_offset
103         ## we've already skipped past the BREAK_RE, to just go
104       end
105
106       while(line = @f.gets)
107         break if line =~ BREAK_RE
108         next_offset = @f.tell
109       end
110     end
111
112     self.cur_offset = next_offset
113     [returned_offset, labels]
114   end
115 end
116
117 Redwood::register_yaml(Loader, %w(uri cur_offset usual archived id))
118
119 end
120 end