Class: Redwood::Maildir

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

Constant Summary collapse

MYHOSTNAME =
Socket.gethostname

Instance Attribute Summary

Attributes inherited from Source

#id

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SerializeLabelsNicely

#after_unmarshal!, #before_marshal

Methods inherited from Source

#==, #go_idle, parse_raw_email_header, #read?, #to_s, #usual

Constructor Details

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

Returns a new instance of Maildir.

Raises:

  • (ArgumentError)


11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/sup/maildir.rb', line 11

def initialize uri, usual=true, archived=false, id=nil, labels=[]
  super uri, usual, archived, id
  @expanded_uri = Source.expand_filesystem_uri(uri)
  uri = URI(@expanded_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, "maildir URI must have a path component" unless uri.path

  @dir = uri.path
  @labels = Set.new(labels || [])
  @mutex = Mutex.new
  @mtimes = { 'cur' => Time.at(0), 'new' => Time.at(0) }
end

Class Method Details

.suggest_labels_for(path) ⇒ Object



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

def self.suggest_labels_for path; [] end

Instance Method Details

#draft?(id) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#each_raw_message_line(id) ⇒ Object



58
59
60
61
62
63
64
# File 'lib/sup/maildir.rb', line 58

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

#file_pathObject



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

def file_path; @dir end

#flagged?(id) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#is_source_for?(uri) ⇒ Boolean

Returns:

  • (Boolean)


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

def is_source_for? uri; super || (uri == @expanded_uri); end

#load_header(id) ⇒ Object



66
67
68
# File 'lib/sup/maildir.rb', line 66

def load_header id
  with_file_for(id) { |f| parse_raw_email_header f }
end

#load_message(id) ⇒ Object



70
71
72
# File 'lib/sup/maildir.rb', line 70

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

#maildir_labels(id) ⇒ Object



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

def maildir_labels id
  (seen?(id) ? [] : [:unread]) +
    (trashed?(id) ?  [:deleted] : []) +
    (flagged?(id) ? [:starred] : [])
end

#mark_draft(id) ⇒ Object



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

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

#mark_flagged(id) ⇒ Object



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

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

#mark_passed(id) ⇒ Object



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

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

#mark_replied(id) ⇒ Object



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

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

#mark_seen(id) ⇒ Object



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

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

#mark_trashed(id) ⇒ Object



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

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

#passed?(id) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#pollObject

XXX use less memory



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/sup/maildir.rb', line 89

def poll
  @mtimes.each do |d,prev_mtime|
    subdir = File.join @dir, d
    debug "polling maildir #{subdir}"
    raise FatalSourceError, "#{subdir} not a directory" unless File.directory? subdir
    mtime = File.mtime subdir
    next if prev_mtime >= mtime
    @mtimes[d] = mtime

    old_ids = benchmark(:maildir_read_index) { Enumerator.new(Index.instance, :each_source_info, self.id, "#{d}/").to_a }
    new_ids = benchmark(:maildir_read_dir) { Dir.glob("#{subdir}/*").map { |x| File.basename x }.sort }
    added = new_ids - old_ids
    deleted = old_ids - new_ids
    debug "#{old_ids.size} in index, #{new_ids.size} in filesystem"
    debug "#{added.size} added, #{deleted.size} deleted"

    added.each_with_index do |id,i|
      yield :add,
        :info => File.join(d,id),
        :labels => @labels + maildir_labels(id) + [:inbox],
        :progress => i.to_f/(added.size+deleted.size)
    end

    deleted.each_with_index do |id,i|
      yield :delete,
        :info => File.join(d,id),
        :progress => (i.to_f+added.size)/(added.size+deleted.size)
    end
  end
  nil
end

#raw_header(id) ⇒ Object



74
75
76
77
78
79
80
81
82
# File 'lib/sup/maildir.rb', line 74

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

#raw_message(id) ⇒ Object



84
85
86
# File 'lib/sup/maildir.rb', line 84

def raw_message id
  with_file_for(id) { |f| f.read }
end

#replied?(id) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#seen?(id) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#store_message(date, from_email, &block) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/sup/maildir.rb', line 30

def store_message date, from_email, &block
  stored = false
  new_fn = new_maildir_basefn + ':2,S'
  Dir.chdir(@dir) do |d|
    tmp_path = File.join(@dir, 'tmp', new_fn)
    new_path = File.join(@dir, 'new', new_fn)
    begin
      sleep 2 if File.stat(tmp_path)

      File.stat(tmp_path)
    rescue Errno::ENOENT #this is what we want.
      begin
        File.open(tmp_path, 'wb') do |f|
          yield f #provide a writable interface for the caller
          f.fsync
        end

        File.link tmp_path, new_path
        stored = true
      ensure
        File.unlink tmp_path if File.exists? tmp_path
      end
    end #rescue Errno...
  end #Dir.chdir

  stored
end

#trashed?(id) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#uriObject

remind me never to use inheritance again.



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

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

#valid?(id) ⇒ Boolean

Returns:

  • (Boolean)


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

def valid? id
  File.exists? File.join(@dir, id)
end