Class: Redwood::IMAP

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

Constant Summary collapse

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, #usual

Constructor Details

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

Returns a new instance of IMAP.

Raises:

  • (ArgumentError)


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

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

#passwordObject

Returns the value of attribute password.



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

def password
  @password
end

#usernameObject

Returns the value of attribute username.



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

def username
  @username
end

Class Method Details

.suggest_labels_for(path) ⇒ Object



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

def self.suggest_labels_for path
  if path =~ /inbox/i
    [path.intern]
  else
    []
  end
end

Instance Method Details

#==(o) ⇒ Object

is this necessary? TODO: remove maybe



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

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

#checkObject



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

def check; end

#connectObject



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

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

#eachObject



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/sup/imap.rb', line 147

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

#each_raw_message_line(id) ⇒ Object



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

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

#end_offsetObject



180
181
182
183
# File 'lib/sup/imap.rb', line 180

def end_offset
  unsynchronized_scan_mailbox
  @ids.last
end

#hostObject



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

def host; @parsed_uri.host; end

#load_header(id) ⇒ Object



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

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

#load_message(id) ⇒ Object



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

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

#mailboxObject



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

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

#pct_doneObject



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

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

#portObject



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

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

#raw_header(id) ⇒ Object



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

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

#raw_message(id) ⇒ Object



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

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

#scan_mailboxObject



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/sup/imap.rb', line 127

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

  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
end

#ssl?Boolean

Returns:

  • (Boolean)


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

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

#start_offsetObject



174
175
176
177
# File 'lib/sup/imap.rb', line 174

def start_offset
  unsynchronized_scan_mailbox
  @ids.first
end