Class: Redwood::Maildir

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

Overview

Maildir doesn’t provide an ordered unique id, which is what Sup requires to be really useful. So we must maintain, in memory, a mapping between Sup “ids” (timestamps, essentially) and the pathnames on disk.

Constant Summary collapse

SCAN_INTERVAL =

seconds

30

Instance Attribute Summary

Attributes inherited from Source

#cur_offset, #id

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Source

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

Constructor Details

#initialize(uri, last_date = nil, usual = true, archived = false, id = nil, labels = []) ⇒ Maildir

Returns a new instance of Maildir.

Raises:

  • (ArgumentError)


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/sup/maildir.rb', line 16

def initialize uri, last_date=nil, usual=true, archived=false, id=nil, labels=[]
  super uri, last_date, usual, archived, id
  uri = URI(Source.expand_filesystem_uri(uri))

  raise ArgumentError, "not a maildir URI" unless uri.scheme == "maildir"
  raise ArgumentError, "maildir URI cannot have a host: #{uri.host}" if uri.host
  raise ArgumentError, "mbox URI must have a path component" unless uri.path

  @dir = uri.path
  @labels = (labels || []).freeze
  @ids = []
  @ids_to_fns = {}
  @last_scan = nil
  @mutex = Mutex.new
end

Class Method Details

.suggest_labels_for(path) ⇒ Object



33
# File 'lib/sup/maildir.rb', line 33

def self.suggest_labels_for path; [] end

Instance Method Details

#checkObject



36
37
38
39
40
41
# File 'lib/sup/maildir.rb', line 36

def check
  scan_mailbox
  return unless start_offset

  start = @ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}." # couldn't find the most recent email
end

#draft?(msg) ⇒ Boolean

Returns:

  • (Boolean)


129
# File 'lib/sup/maildir.rb', line 129

def draft? msg; maildir_data(msg)[2].include? "D"; end

#eachObject



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/sup/maildir.rb', line 104

def each
  scan_mailbox
  return unless start_offset

  start = @ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}." # couldn't find the most recent email

  start.upto(@ids.length - 1) do |i|         
    id = @ids[i]
    self.cur_offset = id
    yield id, @labels + (seen?(id) ? [] : [:unread]) + (trashed?(id) ? [:deleted] : []) + (flagged?(id) ? [:starred] : [])
  end
end

#each_raw_message_line(id) ⇒ Object



43
44
45
46
47
48
49
50
# File 'lib/sup/maildir.rb', line 43

def each_raw_message_line id
  scan_mailbox
  with_file_for(id) do |f|
    until f.eof?
      yield f.gets
    end
  end
end

#end_offsetObject



122
123
124
125
# File 'lib/sup/maildir.rb', line 122

def end_offset
  scan_mailbox
  @ids.last
end

#file_pathObject



32
# File 'lib/sup/maildir.rb', line 32

def file_path; @dir end

#flagged?(msg) ⇒ Boolean

Returns:

  • (Boolean)


130
# File 'lib/sup/maildir.rb', line 130

def flagged? msg; maildir_data(msg)[2].include? "F"; end

#is_source_for?(uri) ⇒ Boolean

Returns:

  • (Boolean)


34
# File 'lib/sup/maildir.rb', line 34

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

#load_header(id) ⇒ Object



52
53
54
55
# File 'lib/sup/maildir.rb', line 52

def load_header id
  scan_mailbox
  with_file_for(id) { |f| MBox::read_header f }
end

#load_message(id) ⇒ Object



57
58
59
60
# File 'lib/sup/maildir.rb', line 57

def load_message id
  scan_mailbox
  with_file_for(id) { |f| RMail::Parser.read f }
end

#mark_draft(msg) ⇒ Object



136
# File 'lib/sup/maildir.rb', line 136

def mark_draft msg; maildir_mark_file msg, "D" unless draft? msg; end

#mark_flagged(msg) ⇒ Object



137
# File 'lib/sup/maildir.rb', line 137

def mark_flagged msg; maildir_mark_file msg, "F" unless flagged? msg; end

#mark_passed(msg) ⇒ Object



138
# File 'lib/sup/maildir.rb', line 138

def mark_passed msg; maildir_mark_file msg, "P" unless passed? msg; end

#mark_replied(msg) ⇒ Object



139
# File 'lib/sup/maildir.rb', line 139

def mark_replied msg; maildir_mark_file msg, "R" unless replied? msg; end

#mark_seen(msg) ⇒ Object



140
# File 'lib/sup/maildir.rb', line 140

def mark_seen msg; maildir_mark_file msg, "S" unless seen? msg; end

#mark_trashed(msg) ⇒ Object



141
# File 'lib/sup/maildir.rb', line 141

def mark_trashed msg; maildir_mark_file msg, "T" unless trashed? msg; end

#passed?(msg) ⇒ Boolean

Returns:

  • (Boolean)


131
# File 'lib/sup/maildir.rb', line 131

def passed? msg; maildir_data(msg)[2].include? "P"; end

#pct_doneObject



127
# File 'lib/sup/maildir.rb', line 127

def pct_done; 100.0 * (@ids.index(cur_offset) || 0).to_f / (@ids.length - 1).to_f; end

#raw_header(id) ⇒ Object



62
63
64
65
66
67
68
69
70
71
# File 'lib/sup/maildir.rb', line 62

def raw_header id
  scan_mailbox
  ret = ""
  with_file_for(id) do |f|
    until f.eof? || (l = f.gets) =~ /^$/
      ret += l
    end
  end
  ret
end

#raw_message(id) ⇒ Object



73
74
75
76
# File 'lib/sup/maildir.rb', line 73

def raw_message id
  scan_mailbox
  with_file_for(id) { |f| f.readlines.join }
end

#replied?(msg) ⇒ Boolean

Returns:

  • (Boolean)


132
# File 'lib/sup/maildir.rb', line 132

def replied? msg; maildir_data(msg)[2].include? "R"; end

#scan_mailboxObject

Raises:



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/sup/maildir.rb', line 78

def scan_mailbox
  return if @last_scan && (Time.now - @last_scan) < SCAN_INTERVAL

  cdir = File.join(@dir, 'cur')
  ndir = File.join(@dir, 'new')
  
  raise FatalSourceError, "#{cdir} not a directory" unless File.directory? cdir
  raise FatalSourceError, "#{ndir} not a directory" unless File.directory? ndir

  begin
    @ids, @ids_to_fns = @mutex.synchronize do
      ids, ids_to_fns = [], {}
      (Dir[File.join(cdir, "*")] + Dir[File.join(ndir, "*")]).map do |fn|
        id = make_id fn
        ids << id
        ids_to_fns[id] = fn
      end
      [ids.sort, ids_to_fns]
    end
  rescue SystemCallError, IOError => e
    raise FatalSourceError, "Problem scanning Maildir directories: #{e.message}."
  end
  
  @last_scan = Time.now
end

#seen?(msg) ⇒ Boolean

Returns:

  • (Boolean)


133
# File 'lib/sup/maildir.rb', line 133

def seen? msg; maildir_data(msg)[2].include? "S"; end

#start_offsetObject



117
118
119
120
# File 'lib/sup/maildir.rb', line 117

def start_offset
  scan_mailbox
  @ids.first
end

#trashed?(msg) ⇒ Boolean

Returns:

  • (Boolean)


134
# File 'lib/sup/maildir.rb', line 134

def trashed? msg; maildir_data(msg)[2].include? "T"; end

#uriObject

remind me never to use inheritance again.



15
# File 'lib/sup/maildir.rb', line 15

yaml_properties :uri, :cur_offset, :usual, :archived, :id, :labels