Class: Redwood::IMAP

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

Constant Summary

SCAN_INTERVAL =

seconds

60
RECOVERABLE_ERRORS =

upon these errors we'll try to rereconnect a few times

[ Errno::EPIPE, Errno::ETIMEDOUT, OpenSSL::SSL::SSLError ]

Instance Attribute Summary (collapse)

Attributes inherited from Source

#cur_offset, #id, #uri

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods inherited from Source

#done?, #file_path, #is_source_for?, #reset!, #seek_to!, #to_s

Constructor Details

- (IMAP) initialize(uri, username, password, last_idate = nil, usual = true, archived = false, id = nil, labels = [])

Returns a new instance of IMAP

Raises:

  • (ArgumentError)


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/sup/imap.rb', line 59

def initialize uri, username, password, last_idate=nil, usual=true, archived=false, id=nil, labels=[]
  raise ArgumentError, "username and password must be specified" unless username && password
  raise ArgumentError, "not an imap uri" unless uri =~ %r!imaps?://!

  super uri, last_idate, usual, archived, id

  @parsed_uri = URI(uri)
  @username = username
  @password = password
  @imap = nil
  @imap_state = {}
  @ids = []
  @last_scan = nil
  @labels = ((labels || []) - LabelManager::RESERVED_LABELS).uniq.freeze
  @say_id = nil
  @mutex = Mutex.new
end

Instance Attribute Details

- (Object) password

Returns the value of attribute password



55
56
57
# File 'lib/sup/imap.rb', line 55

def password
  @password
end

- (Object) username

Returns the value of attribute username



55
56
57
# File 'lib/sup/imap.rb', line 55

def username
  @username
end

Class Method Details

+ (Object) suggest_labels_for(path)



77
78
79
# File 'lib/sup/imap.rb', line 77

def self.suggest_labels_for path
  path =~ /([^\/]*inbox[^\/]*)/i ? [$1.downcase.intern] : []
end

Instance Method Details

- (Object) ==(o)

is this necessary? TODO: remove maybe



93
# File 'lib/sup/imap.rb', line 93

def == o; o.is_a?(IMAP) && o.uri == self.uri && o.username == self.username; end

- (Object) check



89
# File 'lib/sup/imap.rb', line 89

def check; end

- (Object) connect



136
137
138
139
# File 'lib/sup/imap.rb', line 136

def connect
  return if @imap
  safely { } # do nothing!
end

- (Object) each



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/sup/imap.rb', line 164

def each
  return unless start_offset

  ids = 
    @mutex.synchronize do
      unsynchronized_scan_mailbox
      @ids
    end

  start = ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}."

  start.upto(ids.length - 1) do |i|
    id = ids[i]
    state = @mutex.synchronize { @imap_state[id] } or next
    self.cur_offset = id 
    labels = { :Flagged => :starred,
               :Deleted => :deleted
             }.inject(@labels) do |cur, (imap, sup)|
      cur + (state[:flags].include?(imap) ? [sup] : [])
    end

    labels += [:unread] unless state[:flags].include?(:Seen)

    yield id, labels
  end
end

- (Object) each_raw_message_line(id)



103
104
105
# File 'lib/sup/imap.rb', line 103

def each_raw_message_line id
  StringIO.new(raw_message(id)).each { |l| yield l }
end

- (Object) end_offset



197
198
199
200
# File 'lib/sup/imap.rb', line 197

def end_offset
  unsynchronized_scan_mailbox
  @ids.last + 1
end

- (Object) expunge



129
130
131
132
133
# File 'lib/sup/imap.rb', line 129

def expunge
  @imap.expunge
  unsynchronized_scan_mailbox true
  true
end

- (Object) host



81
# File 'lib/sup/imap.rb', line 81

def host; @parsed_uri.host; end

- (Object) load_header(id)



95
96
97
# File 'lib/sup/imap.rb', line 95

def load_header id
  MBox::read_header StringIO.new(raw_header(id))
end

- (Object) load_message(id)



99
100
101
# File 'lib/sup/imap.rb', line 99

def load_message id
  RMail::Parser.read raw_message(id)
end

- (Object) mailbox



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

def mailbox
  x = @parsed_uri.path[1..-1]
  (x.nil? || x.empty?) ? 'INBOX' : CGI.unescape(x)
end

- (Object) mark_as_deleted(ids)



120
121
122
123
124
125
126
# File 'lib/sup/imap.rb', line 120

def mark_as_deleted ids
  ids = [ids].flatten # accept single arguments
  unsynchronized_scan_mailbox
  imap_ids = ids.map { |i| @imap_state[i] && @imap_state[i][:id] }.compact
  return if imap_ids.empty?
  @imap.store imap_ids, "+FLAGS", [:Deleted]
end

- (Object) pct_done



203
# File 'lib/sup/imap.rb', line 203

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

- (Object) port



82
# File 'lib/sup/imap.rb', line 82

def port; @parsed_uri.port || (ssl? ? 993 : 143); end

- (Object) raw_header(id)



107
108
109
110
111
# File 'lib/sup/imap.rb', line 107

def raw_header id
  unsynchronized_scan_mailbox
  header, flags = get_imap_fields id, 'RFC822.HEADER'
  header.gsub(/\r\n/, "\n")
end

- (Object) raw_message(id)



114
115
116
117
# File 'lib/sup/imap.rb', line 114

def raw_message id
  unsynchronized_scan_mailbox
  get_imap_fields(id, 'RFC822').first.gsub(/\r\n/, "\n")
end

- (Object) scan_mailbox(force = false)



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/sup/imap.rb', line 142

def scan_mailbox force=false
  return if !force && @last_scan && (Time.now - @last_scan) < SCAN_INTERVAL
  last_id = safely do
    @imap.examine mailbox
    @imap.responses["EXISTS"].last
  end
  @last_scan = Time.now

  @ids = [] if force
  return if last_id == @ids.length

  range = (@ids.length + 1) .. last_id
  Redwood::log "fetching IMAP headers #{range}"
  fetch(range, ['RFC822.SIZE', 'INTERNALDATE', 'FLAGS']).each do |v|
    id = make_id v
    @ids << id
    @imap_state[id] = { :id => v.seqno, :flags => v.attr["FLAGS"] }
  end
  Redwood::log "done fetching IMAP headers"
end

- (Boolean) ssl?

Returns:

  • (Boolean)


87
# File 'lib/sup/imap.rb', line 87

def ssl?; @parsed_uri.scheme == 'imaps' end

- (Object) start_offset



191
192
193
194
# File 'lib/sup/imap.rb', line 191

def start_offset
  unsynchronized_scan_mailbox
  @ids.first
end