Class: Larch::IMAP::Mailbox
- Inherits:
-
Object
- Object
- Larch::IMAP::Mailbox
- Defined in:
- lib/larch/imap/mailbox.rb
Overview
Represents an IMAP mailbox.
Constant Summary collapse
- FETCH_BLOCK_SIZE =
Maximum number of message headers to fetch with a single IMAP command.
1024- REGEX_MESSAGE_ID =
Regex to capture a Message-Id header.
/message-id\s*:\s*(\S+)/i- SCAN_INTERVAL =
Minimum time (in seconds) allowed between mailbox scans.
60
Instance Attribute Summary collapse
-
#attr ⇒ Object
readonly
Returns the value of attribute attr.
-
#db_mailbox ⇒ Object
readonly
Returns the value of attribute db_mailbox.
-
#delim ⇒ Object
readonly
Returns the value of attribute delim.
-
#flags ⇒ Object
readonly
Returns the value of attribute flags.
-
#imap ⇒ Object
readonly
Returns the value of attribute imap.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#perm_flags ⇒ Object
readonly
Returns the value of attribute perm_flags.
-
#state ⇒ Object
readonly
Returns the value of attribute state.
-
#subscribed ⇒ Object
readonly
Returns the value of attribute subscribed.
Instance Method Summary collapse
-
#append(message) ⇒ Object
(also: #<<)
Appends the specified Larch::IMAP::Message to this mailbox if it doesn’t already exist.
-
#delete_message(guid) ⇒ Object
Deletes the message in this mailbox with the specified guid.
-
#each_db_message ⇒ Object
Iterates through messages in this mailbox, yielding a Larch::Database::Message object for each to the provided block.
-
#each_guid ⇒ Object
Iterates through messages in this mailbox, yielding the Larch message guid of each to the provided block.
-
#each_mailbox ⇒ Object
Iterates through mailboxes that are first-level children of this mailbox, yielding a Larch::IMAP::Mailbox object for each to the provided block.
-
#expunge ⇒ Object
Expunges this mailbox, permanently removing all messages with the Deleted flag.
-
#fetch(guid, peek = false) ⇒ Object
(also: #[])
Returns a Larch::IMAP::Message struct representing the message with the specified Larch guid, or
nilif the specified guid was not found in this mailbox. -
#fetch_db_message(guid) ⇒ Object
Returns a Larch::Database::Message object representing the message with the specified Larch guid, or
nilif the specified guide was not found in this mailbox. -
#has_guid?(guid) ⇒ Boolean
Returns
trueif a message with the specified Larch guid exists in this mailbox,falseotherwise. -
#initialize(imap, name, delim, subscribed, *attr) ⇒ Mailbox
constructor
A new instance of Mailbox.
-
#length ⇒ Object
(also: #size)
Gets the number of messages in this mailbox.
-
#mailboxes ⇒ Object
Returns an Array of Larch::IMAP::Mailbox objects representing mailboxes that are first-level children of this mailbox.
-
#peek(guid) ⇒ Object
Same as fetch, but doesn’t mark the message as seen.
-
#reset ⇒ Object
Resets the mailbox state.
-
#scan ⇒ Object
Fetches message headers from this mailbox.
-
#set_flags(guid, flags, merge = false) ⇒ Object
Sets the IMAP flags for the message specified by guid.
-
#subscribe(force = false) ⇒ Object
Subscribes to this mailbox.
-
#subscribed? ⇒ Boolean
Returns
trueif this mailbox is subscribed,falseotherwise. -
#unsubscribe(force = false) ⇒ Object
Unsubscribes from this mailbox.
Constructor Details
#initialize(imap, name, delim, subscribed, *attr) ⇒ Mailbox
Returns a new instance of Mailbox.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 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 57 58 59 60 61 62 63 |
# File 'lib/larch/imap/mailbox.rb', line 16 def initialize(imap, name, delim, subscribed, *attr) raise ArgumentError, "must provide a Larch::IMAP instance" unless imap.is_a?(Larch::IMAP) @attr = attr.flatten @delim = delim @flags = [] @imap = imap @last_scan = nil @name = name @name_utf7 = Net::IMAP.encode_utf7(@name) @perm_flags = [] @subscribed = subscribed # Valid mailbox states are :closed (no mailbox open), :examined (mailbox # open and read-only), or :selected (mailbox open and read-write). @state = :closed # Create/update this mailbox in the database. mb_data = { :name => @name, :delim => @delim, :attr => @attr.map{|a| a.to_s }.join(','), :subscribed => @subscribed ? 1 : 0 } @db_mailbox = imap.db_account.mailboxes_dataset.filter(:name => @name).first if @db_mailbox @db_mailbox.update(mb_data) else @db_mailbox = Database::Mailbox.create(mb_data) imap.db_account.add_mailbox(@db_mailbox) end # Create private convenience methods (debug, info, warn, etc.) to make # logging easier. Logger::LEVELS.each_key do |level| next if Mailbox.private_method_defined?(level) Mailbox.class_eval do define_method(level) do |msg| Larch.log.log(level, "#{@imap.options[:log_label]} #{@name}: #{msg}") end private level end end end |
Instance Attribute Details
#attr ⇒ Object (readonly)
Returns the value of attribute attr.
5 6 7 |
# File 'lib/larch/imap/mailbox.rb', line 5 def attr @attr end |
#db_mailbox ⇒ Object (readonly)
Returns the value of attribute db_mailbox.
5 6 7 |
# File 'lib/larch/imap/mailbox.rb', line 5 def db_mailbox @db_mailbox end |
#delim ⇒ Object (readonly)
Returns the value of attribute delim.
5 6 7 |
# File 'lib/larch/imap/mailbox.rb', line 5 def delim @delim end |
#flags ⇒ Object (readonly)
Returns the value of attribute flags.
5 6 7 |
# File 'lib/larch/imap/mailbox.rb', line 5 def flags @flags end |
#imap ⇒ Object (readonly)
Returns the value of attribute imap.
5 6 7 |
# File 'lib/larch/imap/mailbox.rb', line 5 def imap @imap end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
5 6 7 |
# File 'lib/larch/imap/mailbox.rb', line 5 def name @name end |
#perm_flags ⇒ Object (readonly)
Returns the value of attribute perm_flags.
5 6 7 |
# File 'lib/larch/imap/mailbox.rb', line 5 def perm_flags @perm_flags end |
#state ⇒ Object (readonly)
Returns the value of attribute state.
5 6 7 |
# File 'lib/larch/imap/mailbox.rb', line 5 def state @state end |
#subscribed ⇒ Object (readonly)
Returns the value of attribute subscribed.
5 6 7 |
# File 'lib/larch/imap/mailbox.rb', line 5 def subscribed @subscribed end |
Instance Method Details
#append(message) ⇒ Object Also known as: <<
Appends the specified Larch::IMAP::Message to this mailbox if it doesn’t already exist. Returns true if the message was appended successfully, false if the message already exists in the mailbox.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/larch/imap/mailbox.rb', line 68 def append() raise ArgumentError, "must provide a Larch::IMAP::Message object" unless .is_a?(Larch::IMAP::Message) return false if has_guid?(.guid) @imap.safely do unless imap_select(!!@imap.[:create_mailbox]) raise Larch::IMAP::Error, "mailbox cannot contain messages: #{@name}" end debug "appending message: #{message.guid}" @imap.conn.append(@name_utf7, .rfc822, get_supported_flags(.flags), .internaldate) unless @imap.[:dry_run] end true end |
#delete_message(guid) ⇒ Object
Deletes the message in this mailbox with the specified guid. Returns true on success, false on failure.
87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/larch/imap/mailbox.rb', line 87 def (guid) if @imap.quirks[:gmail] return false unless = (guid) debug "moving message to Gmail trash: #{guid}" @imap.safely { @imap.conn.uid_copy(.uid, '[Gmail]/Trash') } && set_flags(guid, [:Deleted], true) else set_flags(guid, [:Deleted], true) end end |
#each_db_message ⇒ Object
Iterates through messages in this mailbox, yielding a Larch::Database::Message object for each to the provided block.
102 103 104 105 |
# File 'lib/larch/imap/mailbox.rb', line 102 def # :yields: db_message scan @db_mailbox..all {|| yield } end |
#each_guid ⇒ Object
Iterates through messages in this mailbox, yielding the Larch message guid of each to the provided block.
109 110 111 |
# File 'lib/larch/imap/mailbox.rb', line 109 def each_guid # :yields: guid {|| yield .guid } end |
#each_mailbox ⇒ Object
Iterates through mailboxes that are first-level children of this mailbox, yielding a Larch::IMAP::Mailbox object for each to the provided block.
115 116 117 |
# File 'lib/larch/imap/mailbox.rb', line 115 def each_mailbox # :yields: mailbox mailboxes.each {|mb| yield mb } end |
#expunge ⇒ Object
Expunges this mailbox, permanently removing all messages with the Deleted flag.
121 122 123 124 125 126 127 128 129 130 |
# File 'lib/larch/imap/mailbox.rb', line 121 def expunge return false unless imap_select @imap.safely do debug "expunging deleted messages" @last_scan = nil @imap.conn.expunge unless @imap.[:dry_run] end end |
#fetch(guid, peek = false) ⇒ Object Also known as: []
Returns a Larch::IMAP::Message struct representing the message with the specified Larch guid, or nil if the specified guid was not found in this mailbox.
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/larch/imap/mailbox.rb', line 135 def fetch(guid, peek = false) scan unless = (guid) warning "message not found in local db: #{guid}" return nil end debug "#{peek ? 'peeking at' : 'fetching'} message: #{guid}" imap_uid_fetch([.uid], [(peek ? 'BODY.PEEK[]' : 'BODY[]'), 'FLAGS', 'INTERNALDATE', 'ENVELOPE']) do |fetch_data| data = fetch_data.first check_response_fields(data, 'BODY[]', 'FLAGS', 'INTERNALDATE', 'ENVELOPE') return Message.new(guid, data.attr['ENVELOPE'], data.attr['BODY[]'], data.attr['FLAGS'], Time.parse(data.attr['INTERNALDATE'])) end warning "message not found on server: #{guid}" return nil end |
#fetch_db_message(guid) ⇒ Object
Returns a Larch::Database::Message object representing the message with the specified Larch guid, or nil if the specified guide was not found in this mailbox.
161 162 163 164 |
# File 'lib/larch/imap/mailbox.rb', line 161 def (guid) scan @db_mailbox..filter(:guid => guid).first end |
#has_guid?(guid) ⇒ Boolean
Returns true if a message with the specified Larch guid exists in this mailbox, false otherwise.
168 169 170 171 |
# File 'lib/larch/imap/mailbox.rb', line 168 def has_guid?(guid) scan @db_mailbox..filter(:guid => guid).count > 0 end |
#length ⇒ Object Also known as: size
Gets the number of messages in this mailbox.
174 175 176 177 |
# File 'lib/larch/imap/mailbox.rb', line 174 def length scan @db_mailbox..count end |
#mailboxes ⇒ Object
Returns an Array of Larch::IMAP::Mailbox objects representing mailboxes that are first-level children of this mailbox.
182 183 184 185 186 187 188 189 190 |
# File 'lib/larch/imap/mailbox.rb', line 182 def mailboxes return [] if @attr.include?(:Noinferiors) all = @imap.safely{ @imap.conn.list('', "#{@name_utf7}#{@delim}%") } || [] subscribed = @imap.safely{ @imap.conn.lsub('', "#{@name_utf7}#{@delim}%") } || [] all.map{|mb| Mailbox.new(@imap, mb.name, mb.delim, subscribed.any?{|s| s.name == mb.name}, mb.attr) } end |
#peek(guid) ⇒ Object
Same as fetch, but doesn’t mark the message as seen.
193 194 195 |
# File 'lib/larch/imap/mailbox.rb', line 193 def peek(guid) fetch(guid, true) end |
#reset ⇒ Object
Resets the mailbox state.
198 199 200 |
# File 'lib/larch/imap/mailbox.rb', line 198 def reset @state = :closed end |
#scan ⇒ Object
Fetches message headers from this mailbox.
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/larch/imap/mailbox.rb', line 203 def scan now = Time.now.to_i return if @last_scan && (now - @last_scan) < SCAN_INTERVAL first_scan = @last_scan.nil? @last_scan = now # Compare the mailbox's current status with its last known status. begin return unless status = imap_status('MESSAGES', 'UIDNEXT', 'UIDVALIDITY') rescue Error => e return if @imap.[:create_mailbox] raise end flag_range = nil full_range = nil if @db_mailbox.uidvalidity && @db_mailbox.uidnext && status['UIDVALIDITY'] == @db_mailbox.uidvalidity # The UIDVALIDITY is the same as what we saw last time we scanned this # mailbox, which means that all the existing messages in the database are # still valid. We only need to request headers for new messages. # # If this is the first scan of this mailbox during this Larch session, # then we'll also update the flags of all messages in the mailbox. flag_range = 1...@db_mailbox.uidnext if first_scan full_range = @db_mailbox.uidnext...status['UIDNEXT'] else # The UIDVALIDITY has changed or this is the first time we've scanned this # mailbox (ever). Either way, all existing messages in the database are no # longer valid, so we have to throw them out and re-request everything. @db_mailbox. full_range = 1...status['UIDNEXT'] end @db_mailbox.update(:uidvalidity => status['UIDVALIDITY']) need_flag_scan = flag_range && flag_range.max && flag_range.min && flag_range.max - flag_range.min >= 0 need_full_scan = full_range && full_range.max && full_range.min && full_range.max - full_range.min >= 0 return unless need_flag_scan || need_full_scan fetch_flags(flag_range) if need_flag_scan if need_full_scan fetch_headers(full_range, { :progress_start => @db_mailbox..count + 1, :progress_total => status['MESSAGES'] }) end @db_mailbox.update(:uidnext => status['UIDNEXT']) return end |
#set_flags(guid, flags, merge = false) ⇒ Object
Sets the IMAP flags for the message specified by guid. flags should be an array of symbols for standard flags, strings for custom flags.
If merge is true, the specified flags will be merged with the message’s existing flags. Otherwise, all existing flags will be cleared and replaced with the specified flags.
Note that the :Recent flag cannot be manually set or removed.
Returns true on success, false on failure.
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/larch/imap/mailbox.rb', line 274 def set_flags(guid, flags, merge = false) raise ArgumentError, "flags must be an Array" unless flags.is_a?(Array) return false unless = (guid) merged_flags = merge ? (.flags + flags).uniq : flags supported_flags = get_supported_flags(merged_flags) return true if .flags == supported_flags return false if !imap_select @imap.safely { @imap.conn.uid_store(.uid, 'FLAGS.SILENT', supported_flags) } unless @imap.[:dry_run] true end |
#subscribe(force = false) ⇒ Object
Subscribes to this mailbox.
291 292 293 294 295 296 297 298 299 |
# File 'lib/larch/imap/mailbox.rb', line 291 def subscribe(force = false) return false if subscribed? && !force @imap.safely { @imap.conn.subscribe(@name_utf7) } unless @imap.[:dry_run] @subscribed = true @db_mailbox.update(:subscribed => 1) true end |
#subscribed? ⇒ Boolean
Returns true if this mailbox is subscribed, false otherwise.
302 303 304 |
# File 'lib/larch/imap/mailbox.rb', line 302 def subscribed? @subscribed end |
#unsubscribe(force = false) ⇒ Object
Unsubscribes from this mailbox.
307 308 309 310 311 312 313 314 315 |
# File 'lib/larch/imap/mailbox.rb', line 307 def unsubscribe(force = false) return false unless subscribed? || force @imap.safely { @imap.conn.unsubscribe(@name_utf7) } unless @imap.[:dry_run] @subscribed = false @db_mailbox.update(:subscribed => 0) true end |