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