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