"Fossies" - the Fresh Open Source Software Archive

Member "imapfilter-2.8.2/src/mailbox.lua" (26 Dec 2023, 32428 Bytes) of package /linux/privat/imapfilter-2.8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Lua source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "mailbox.lua": 2.8.0_vs_2.8.1.

    1 -- The Mailbox class represents a mailbox that resides in an IMAP account.
    2 
    3 Mailbox = {}
    4 
    5 Mailbox._mt = {}
    6 setmetatable(Mailbox, Mailbox._mt)
    7 
    8 
    9 Mailbox._mt.__call = function (self, account, mailbox)
   10     local object = {}
   11 
   12     object._type = 'mailbox'
   13     object._account = account
   14     object._mailbox = mailbox
   15     object._string =  account._account.username .. '@' ..
   16                       account._account.server .. '/' .. mailbox
   17 
   18     for key, value in pairs(Mailbox) do
   19         if type(value) == 'function' then object[key] = value end
   20     end
   21 
   22     object._mt = {}
   23     object._mt.__index = object._attach_message
   24     setmetatable(object, object._mt)
   25 
   26     return object
   27 end
   28 
   29 
   30 function Mailbox._check_connection(self)
   31     if not self._account._account.session then
   32         self._account._login_user(self._account)
   33     end
   34 end
   35 
   36 function Mailbox._check_result(self, request, result)
   37     if result == nil then
   38         self._account._account.session = nil
   39         self._account._account.selected = nil
   40         self._account._account.readonly = nil
   41         error(request .. ' request to ' .. self._account._string .. ' failed', 0)
   42     end
   43 end
   44 
   45 
   46 function Mailbox._attach_message(self, uid)
   47     self[uid] = Message(self._account, self, uid)
   48     return self[uid]
   49 end
   50 
   51 function Mailbox._detach_message(self, uid)
   52     self[uid] = nil
   53 end
   54 
   55 
   56 function Mailbox._cached_select(self)
   57     if self._account._account.selected == nil or
   58         self._account._account.selected ~= self._mailbox then
   59 
   60         self._check_connection(self)
   61         local r, readonly = ifcore.select(self._account._account.session, self._mailbox)
   62         self._check_result(self, 'select', r)
   63         if r == false then return false end
   64 
   65         self._account._account.selected = self._mailbox
   66         self._account._account.readonly = readonly
   67     end
   68     return true
   69 end
   70 
   71 function Mailbox._cached_close(self)
   72     self._check_connection(self)
   73     if self._account._account.selected == nil then
   74         return
   75     end
   76     local r = ifcore.close(self._account._account.session)
   77     self._check_result(self, 'close', r)
   78     if r == false then
   79         return false
   80     end
   81 
   82     self._account._account.selected = nil
   83     self._account._account.readonly = nil
   84 
   85     return true
   86 end
   87 
   88 
   89 function Mailbox._send_query(self, criteria, messages)
   90     _check_optional(criteria, { 'string', 'table' })
   91     _check_optional(messages, 'table')
   92 
   93     if self._cached_select(self) ~= true then return {} end
   94 
   95     local mesgs
   96     if messages == nil then
   97         mesgs = nil
   98     else
   99         mesgs = _extract_messages(self, messages)
  100     end
  101     if mesgs == nil or #mesgs == 0 or
  102        options.limit > 0 and #mesgs > options.limit then
  103         mesgs = 'ALL'
  104     else
  105         mesgs = _make_range(mesgs)
  106         mesgs = 'UID ' .. table.concat(mesgs, ',')
  107     end
  108 
  109     local query
  110     if criteria == nil then
  111         query = mesgs
  112     elseif type(criteria) == 'string' then
  113         query = mesgs .. ' ' .. criteria
  114     else
  115         query = _make_query(criteria, mesgs)
  116     end
  117 
  118     if type(options.charset) == 'string' then
  119         charset = options.charset
  120     else
  121         charset = ''
  122     end
  123 
  124     self._check_connection(self)
  125     local r, results = ifcore.search(self._account._account.session, query,
  126                                      charset)
  127     self._check_result(self, 'search', r)
  128     if r == false then return false end
  129 
  130     if options.close == true then self._cached_close(self) end
  131     if results == nil then return {} end
  132 
  133     local t = {}
  134     for n in string.gmatch(results, '%d+') do
  135         table.insert(t, { self, tonumber(n) })
  136     end
  137 
  138     return t
  139 end
  140 
  141 
  142 function Mailbox._flag_messages(self, mode, flags, messages)
  143     if not messages or #messages == 0 then return end
  144     if self._cached_select(self) ~= true then return end
  145     if self._account._account.readonly == true then return end
  146 
  147     local f = ''
  148     if #flags ~= 0 then f = table.concat(flags, ' ') end
  149 
  150     local r = false
  151     local m = _make_range(messages)
  152     local n = #m
  153     local l = n
  154     if options.limit > 0 then l = options.limit end
  155     for i = 1, n, l do
  156         j = i + l - 1
  157         if n < j then j = n end
  158         self._check_connection(self)
  159         r = ifcore.store(self._account._account.session, table.concat(m, ',',
  160                          i, j), mode, f)
  161         self._check_result(self, 'store', r)
  162         if r == false then break end
  163     end
  164 
  165     if options.close == true then self._cached_close(self) end
  166 
  167     return r
  168 end
  169 
  170 
  171 function Mailbox._copy_messages(self, dest, messages)
  172     if not messages or #messages == 0 then return end
  173 
  174     local r = false
  175     if self._account._account.session == dest._account._account.session then
  176         if self._cached_select(self) ~= true then return end
  177 
  178         local m = _make_range(messages)
  179         local n = #m
  180         local l = n
  181         if options.limit > 0 then l = options.limit end
  182         for i = 1, n, l do
  183             j = i + l - 1
  184             if n < j then j = n end
  185             self._check_connection(self)
  186             r = ifcore.copy(self._account._account.session,
  187                             table.concat(m, ',', i, j), dest._mailbox)
  188             self._check_result(self, 'copy', r)
  189             if r == false then break end
  190         end
  191 
  192         if options.close == true then self._cached_close(self) end
  193     else
  194         local fast = self._fetch_fast(self, messages)
  195         local mesgs = self._fetch_message(self, messages)
  196 
  197         for i in pairs(fast) do
  198             for k, v in ipairs(fast[i]['flags']) do
  199                 if string.lower(v) == '\\recent' then
  200                     table.remove(fast[i]['flags'], k)
  201                 end
  202             end
  203 
  204             self._check_connection(dest)
  205             r = ifcore.append(dest._account._account.session, dest._mailbox,
  206                               mesgs[i], table.concat(fast[i]['flags'], ' '),
  207                               fast[i]['date'])
  208             self._check_result(dest, 'append', r)
  209             if r == false then break end
  210         end
  211     end
  212 
  213     return r
  214 end
  215 
  216 
  217 function Mailbox._fetch_fast(self, messages)
  218     if not messages or #messages == 0 then return end
  219     if self._cached_select(self) ~= true then return end
  220 
  221     local results = {}
  222     for _, m in ipairs(messages) do
  223         self._check_connection(self)
  224         local r, flags, date, size =
  225             ifcore.fetchfast(self._account._account.session, tostring(m))
  226         self._check_result(self, 'fetchfast', r)
  227         if r == false then break end
  228 
  229         if flags ~= nil and date ~= nil and size ~= nil  then
  230             local f = {}
  231             for s in string.gmatch(flags, '%S+') do
  232                 table.insert(f, s)
  233             end
  234             results[m] = {}
  235             results[m]['flags'] = f
  236             results[m]['date'] = date
  237             results[m]['size'] = size
  238         end
  239     end
  240 
  241     if options.close == true then self._cached_close(self) end
  242 
  243     return results
  244 end
  245 
  246 function Mailbox._fetch_flags(self, messages)
  247     if not messages or #messages == 0 then return end
  248     if self._cached_select(self) ~= true then return end
  249 
  250     local results = {}
  251     for _, m in ipairs(messages) do
  252         self._check_connection(self)
  253         local r, flags = ifcore.fetchflags(self._account._account.session,
  254                                            tostring(m))
  255         self._check_result(self, 'fetchfast', r)
  256         if r == false then break end
  257 
  258         if flags ~= nil then
  259             local f = {}
  260             for s in string.gmatch(flags, '%S+') do
  261                 table.insert(f, s)
  262             end
  263             results[m] = f
  264         end
  265     end
  266 
  267     if options.close == true then self._cached_close(self) end
  268 
  269     return results
  270 end
  271 
  272 function Mailbox._fetch_date(self, messages)
  273     if not messages or #messages == 0 then return end
  274     if self._cached_select(self) ~= true then return end
  275 
  276     local results = {}
  277     for _, m in ipairs(messages) do
  278         if options.cache == true and
  279             self[m]._date then
  280             results[m] = self[m]._date
  281         else
  282             self._check_connection(self)
  283             local r, date = ifcore.fetchdate(self._account._account.session,
  284                                              tostring(m))
  285             self._check_result(self, 'fetchdate', r)
  286             if r == false then break end
  287 
  288             if date ~= nil then
  289                 results[m] = date
  290                 if options.cache == true then self[m]._date = date end
  291             end
  292         end
  293     end
  294 
  295     if options.close == true then self._cached_close(self) end
  296 
  297     return results
  298 end
  299 
  300 function Mailbox._fetch_size(self, messages)
  301     if not messages or #messages == 0 then return end
  302     if self._cached_select(self) ~= true then return end
  303 
  304     local results = {}
  305     for _, m in ipairs(messages) do
  306         if options.cache == true and
  307             self[m]._size then
  308             results[m] = self[m]._size
  309         else
  310             self._check_connection(self)
  311             local r, size = ifcore.fetchsize(self._account._account.session,
  312                                              tostring(m))
  313             self._check_result(self, 'fetchsize', r)
  314             if r == false then break end
  315 
  316             if size ~= nil then
  317                 results[m] = tonumber(size)
  318                 if options.cache == true then self[m]._size = tonumber(size) end
  319             end
  320         end
  321     end
  322 
  323     if options.close == true then self._cached_close(self) end
  324 
  325     return results
  326 end
  327 
  328 function Mailbox._fetch_header(self, messages)
  329     if not messages or #messages == 0 then return end
  330     if self._cached_select(self) ~= true then return end
  331 
  332     local results = {}
  333     for _, m in ipairs(messages) do
  334         if options.cache == true and
  335             self[m]._header then
  336             results[m] = self[m]._header
  337         else
  338             self._check_connection(self)
  339             local r, header = ifcore.fetchheader(self._account._account.session,
  340                                                  tostring(m))
  341             self._check_result(self, 'fetchheader', r)
  342             if r == false then break end
  343 
  344             if header ~= nil then
  345                 results[m] = header
  346                 if options.cache == true then self[m]._header = header end
  347             end
  348         end
  349     end
  350 
  351     if options.close == true then self._cached_close(self) end
  352 
  353     return results
  354 end
  355 
  356 function Mailbox._fetch_body(self, messages)
  357     if not messages or #messages == 0 then return end
  358     if self._cached_select(self) ~= true then return end
  359 
  360     local results = {}
  361     for _, m in ipairs(messages) do
  362         if options.cache == true and
  363             self[m]._body then
  364             results[m] = self[m]._body
  365         else
  366             self._check_connection(self)
  367             local r, body = ifcore.fetchbody(self._account._account.session,
  368                                              tostring(m))
  369             self._check_result(self, 'fetchbody', r)
  370             if r == false then break end
  371 
  372             if body ~= nil then
  373                 results[m] = body
  374                 if options.cache == true then self[m]._body = body end
  375             end
  376         end
  377     end
  378 
  379     if options.close == true then self._cached_close(self) end
  380 
  381     return results
  382 end
  383 
  384 function Mailbox._fetch_message(self, messages)
  385     if not messages or #messages == 0 then return end
  386 
  387     local header = self._fetch_header(self, messages)
  388     local body = self._fetch_body(self, messages)
  389 
  390     local results = {}
  391     for _, m in ipairs(messages) do
  392         if header[m] == nil then
  393             results[m] = nil
  394         elseif body[m] == nil then
  395             results[m] = header[m]
  396         else
  397             results[m] = header[m] .. body[m]
  398         end
  399     end
  400 
  401     return results
  402 end
  403 
  404 
  405 function Mailbox._fetch_fields(self, fields, messages)
  406     if not messages or #messages == 0 then return end
  407     if self._cached_select(self) ~= true then return end
  408 
  409     local results = {}
  410     for _, m in ipairs(messages) do
  411         results[m] = ''
  412         for _, f in ipairs(fields) do
  413             if options.cache == true and
  414                 self[m]._fields[f] then
  415                 results[m] = results[m] .. self[m]._fields[f]
  416             else
  417                 self._check_connection(self)
  418                 local r, field =
  419                     ifcore.fetchfields(self._account._account.session,
  420                                        tostring(m), f)
  421 
  422                 self._check_result(self, 'fetchfields', r)
  423                 if r == false then break end
  424 
  425                 if field ~= nil then
  426                     field = string.gsub(field, '\r\n\r\n$', '\n')
  427                     results[m] = results[m] .. field
  428                     if options.cache == true then self[m]._fields[f] = field end
  429                 end
  430             end
  431         end
  432         results[m] = string.gsub(results[m], '\n$', '')
  433     end
  434 
  435     if options.close == true then self._cached_close(self) end
  436 
  437     return results
  438 end
  439 
  440 function Mailbox._fetch_structure(self, messages)
  441     if not messages or #messages == 0 then return end
  442     if self._cached_select(self) ~= true then return end
  443 
  444     local results = {}
  445     for _, m in ipairs(messages) do
  446         if options.cache == true and
  447             self[m]._structure then
  448             results[m] = self[m]._structure
  449         else
  450             self._check_connection(self)
  451             local r, structure =
  452                 ifcore.fetchstructure(self._account._account.session,
  453                                       tostring(m))
  454             self._check_result(self, 'fetchstructure', r)
  455             if r == false then break end
  456 
  457             if structure ~= nil then
  458                 local parsed = _parse_structure({ ['s'] = structure,
  459                                                 ['i'] = 1 })
  460                 results[m] = parsed
  461                 if options.cache == true then self[m]._structure = parsed end
  462             end
  463         end
  464     end
  465 
  466     if options.close == true then self._cached_close(self) end
  467 
  468     return results
  469 end
  470 
  471 function Mailbox._fetch_parts(self, parts, message)
  472     if self._cached_select(self) ~= true then return end
  473 
  474     local results = {}
  475     for _, part in ipairs(parts) do
  476         results[part] = ''
  477         if options.cache == true and
  478             self[message]._parts[part] then
  479             results[part] = self[message]._parts[part]
  480         else
  481             self._check_connection(self)
  482             local r, bodypart = ifcore.fetchpart(self._account._account.session,
  483                                                  tostring(message), part)
  484             self._check_result(self, 'fetchpart', r)
  485             if r == false then break end
  486 
  487             if bodypart ~= nil then
  488                 results[part] = bodypart
  489                 self[message]._parts[part] = bodypart
  490             end
  491         end
  492     end
  493 
  494     if options.close == true then self._cached_close(self) end
  495 
  496     return results
  497 end
  498 
  499 
  500 function Mailbox.check_status(self)
  501     self._check_connection(self)
  502     if self._account._account.selected == self._mailbox then
  503         self._cached_close(self)
  504     end
  505     local r, exist, recent, unseen, uidnext =
  506         ifcore.status(self._account._account.session, self._mailbox)
  507     self._check_result(self, 'status', r)
  508     if r == false then return -1, -1, -1, -1 end
  509 
  510     if options.info == true then
  511         print(exist .. ' messages, ' .. recent .. ' recent, ' .. unseen ..
  512               ' unseen, in ' .. self._string .. '.')
  513     end
  514 
  515     return exist, recent, unseen, uidnext
  516 end
  517 
  518 
  519 function Mailbox.send_query(self, criteria, messages)
  520     return Set(self._send_query(self, criteria, messages))
  521 end
  522 
  523 function Mailbox.select_all(self)
  524     return self.send_query(self)
  525 end
  526 
  527 
  528 function Mailbox.add_flags(self, flags, messages)
  529     _check_required(flags, 'table')
  530     _check_required(messages, 'table')
  531 
  532     local mesgs = _extract_messages(self, messages)
  533     local r = self._flag_messages(self, 'add', flags, mesgs)
  534     if options.info == true and r == true then
  535         print(#mesgs .. ' messages flagged in ' .. self._string .. '.')
  536     end
  537 
  538     return r
  539 end
  540 
  541 function Mailbox.remove_flags(self, flags, messages)
  542     _check_required(flags, 'table')
  543     _check_required(messages, 'table')
  544 
  545     local mesgs = _extract_messages(self, messages)
  546     local r = self._flag_messages(self, 'remove', flags, mesgs)
  547     if options.info == true and r == true then
  548         print(#mesgs .. ' messages flagged in ' .. self._string .. '.')
  549     end
  550 
  551     return r
  552 end
  553 
  554 function Mailbox.replace_flags(self, flags, messages)
  555     _check_required(flags, 'table')
  556     _check_required(messages, 'table')
  557 
  558     local mesgs = _extract_messages(self, messages)
  559     local r = self._flag_messages(self, 'replace', flags, mesgs)
  560     if options.info == true and r == true then
  561         print(#mesgs .. ' messages flagged in ' .. self._string .. '.')
  562     end
  563 
  564     return r
  565 end
  566 
  567 
  568 function Mailbox.mark_answered(self, messages)
  569     _check_required(messages, 'table')
  570 
  571     local mesgs = _extract_messages(self, messages)
  572     local r = self._flag_messages(self, 'add', { '\\Answered' }, mesgs)
  573     if options.info == true and r == true then
  574         print(#mesgs .. ' messages marked answered in ' .. self._string .. '.')
  575     end
  576 
  577     return r
  578 end
  579 
  580 
  581 function Mailbox.mark_deleted(self, messages)
  582     _check_required(messages, 'table')
  583 
  584     local mesgs = _extract_messages(self, messages)
  585     local r = self._flag_messages(self, 'add', { '\\Deleted' }, mesgs)
  586     if options.info == true and r == true then
  587         print(#mesgs .. ' messages marked deleted in ' .. self._string .. '.')
  588     end
  589 
  590     return r
  591 end
  592 
  593 function Mailbox.mark_draft(self, messages)
  594     _check_required(messages, 'table')
  595 
  596     local mesgs = _extract_messages(self, messages)
  597     local r = self._flag_messages(self, 'add', { '\\Draft' }, mesgs)
  598     if options.info == true and r == true then
  599         print(#mesgs .. ' messages marked draft in ' .. self._string .. '.')
  600     end
  601 
  602     return r
  603 end
  604 
  605 function Mailbox.mark_flagged(self, messages)
  606     _check_required(messages, 'table')
  607 
  608     local mesgs = _extract_messages(self, messages)
  609     local r = self._flag_messages(self, 'add', { '\\Flagged' }, mesgs)
  610     if options.info == true and r == true then
  611         print(#mesgs .. ' messages marked flagged in ' .. self._string .. '.')
  612     end
  613 
  614     return r
  615 end
  616 
  617 function Mailbox.mark_seen(self, messages)
  618     _check_required(messages, 'table')
  619 
  620     local mesgs = _extract_messages(self, messages)
  621     local r = self._flag_messages(self, 'add', { '\\Seen' }, mesgs)
  622     if options.info == true and r == true then
  623         print(#mesgs .. ' messages marked seen in ' .. self._string .. '.')
  624     end
  625 
  626     return r
  627 end
  628 
  629 function Mailbox.unmark_answered(self, messages)
  630     _check_required(messages, 'table')
  631 
  632     local mesgs = _extract_messages(self, messages)
  633     local r = self._flag_messages(self, 'remove', { '\\Answered' }, mesgs)
  634     if options.info == true and r == true then
  635         print(#mesgs .. ' messages unmarked answered in ' .. self._string ..
  636               '.')
  637     end
  638 
  639     return r
  640 end
  641 
  642 function Mailbox.unmark_deleted(self, messages)
  643     _check_required(messages, 'table')
  644 
  645     local mesgs = _extract_messages(self, messages)
  646     local r = self._flag_messages(self, 'remove', { '\\Deleted' }, mesgs)
  647     if options.info == true and r == true then
  648         print(#mesgs .. ' messages unmarked deleted in ' .. self._string .. '.')
  649     end
  650 
  651     return r
  652 end
  653 
  654 function Mailbox.unmark_draft(self, messages)
  655     _check_required(messages, 'table')
  656 
  657     local mesgs = _extract_messages(self, messages)
  658     local r = self._flag_messages(self, 'remove', { '\\Draft' }, mesgs)
  659     if options.info == true and r == true then
  660         print(#mesgs .. ' messages unmarked draft in ' .. self._string .. '.')
  661     end
  662 
  663     return r
  664 end
  665 
  666 function Mailbox.unmark_flagged(self, messages)
  667     _check_required(messages, 'table')
  668 
  669     local mesgs = _extract_messages(self, messages)
  670     local r = self._flag_messages(self, 'remove', { '\\Flagged' }, mesgs)
  671     if options.info == true and r == true then
  672         print(#mesgs .. ' messages unmarked flagged in ' .. self._string .. '.')
  673     end
  674 
  675     return r
  676 end
  677 
  678 function Mailbox.unmark_seen(self, messages)
  679     _check_required(messages, 'table')
  680 
  681     local mesgs = _extract_messages(self, messages)
  682     local r = self._flag_messages(self, 'remove', { '\\Seen' }, mesgs)
  683     if options.info == true and r == true then
  684         print(#mesgs .. ' messages unmarked seen in ' .. self._string .. '.')
  685     end
  686 
  687     return r
  688 end
  689 
  690 
  691 function Mailbox.delete_messages(self, messages)
  692     _check_required(messages, 'table')
  693 
  694     local mesgs = _extract_messages(self, messages)
  695     local r = self._flag_messages(self, 'add', { '\\Deleted' }, mesgs)
  696     if options.info == true and r == true then
  697         print(#mesgs .. ' messages deleted in ' .. self._string .. '.')
  698     end
  699 
  700     return r
  701 end
  702 
  703 
  704 function Mailbox.copy_messages(self, dest, messages)
  705     _check_required(dest, 'table')
  706     _check_required(messages, 'table')
  707 
  708     local mesgs = _extract_messages(self, messages)
  709     local r = self._copy_messages(self, dest, mesgs)
  710     if options.info == true and r == true then
  711         print(#mesgs .. ' messages copied from ' .. self._string .. ' to ' ..
  712               dest._string .. '.')
  713     end
  714 
  715     return r
  716 end
  717 
  718 
  719 function Mailbox.move_messages(self, dest, messages)
  720     _check_required(dest, 'table')
  721     _check_required(messages, 'table')
  722 
  723     local mesgs = _extract_messages(self, messages)
  724     local rc = self._copy_messages(self, dest, mesgs)
  725     local rf = false
  726     if rc == true then
  727         rf = self._flag_messages(self, 'add', { '\\Deleted' }, mesgs)
  728     end
  729     if options.info == true and rc == true and rf == true then
  730         print(#mesgs .. ' messages moved from ' .. self._string .. ' to ' ..
  731               dest._string .. '.')
  732     end
  733 
  734     return rc == true and rf == true
  735 end
  736 
  737 
  738 function Mailbox.fetch_flags(self, messages)
  739     _check_required(messages, 'table')
  740     return self._fetch_flags(self, _extract_messages(self, messages))
  741 end
  742 
  743 function Mailbox.fetch_date(self, messages)
  744     _check_required(messages, 'table')
  745     return self._fetch_date(self, _extract_messages(self, messages))
  746 end
  747 
  748 function Mailbox.fetch_size(self, messages)
  749     _check_required(messages, 'table')
  750     return self._fetch_size(self, _extract_messages(self, messages))
  751 end
  752 
  753 function Mailbox.fetch_header(self, messages)
  754     _check_required(messages, 'table')
  755     return self._fetch_header(self, _extract_messages(self, messages))
  756 end
  757 
  758 function Mailbox.fetch_body(self, messages)
  759     _check_required(messages, 'table')
  760     return self._fetch_body(self, _extract_messages(self, messages))
  761 end
  762 
  763 function Mailbox.fetch_message(self, messages)
  764     _check_required(messages, 'table')
  765     return self._fetch_message(self, _extract_messages(self, messages))
  766 end
  767 
  768 function Mailbox.fetch_fields(self, fields, messages)
  769     _check_required(fields, 'table')
  770     _check_required(messages, 'table')
  771     return self._fetch_fields(self, fields, _extract_messages(self, messages))
  772 end
  773 
  774 function Mailbox.fetch_structure(self, messages)
  775     _check_required(messages, 'table')
  776     return self._fetch_structure(self, _extract_messages(self, messages))
  777 end
  778 
  779 function Mailbox.fetch_parts(self, parts, message)
  780     _check_required(parts, 'table')
  781     _check_required(message, 'number')
  782     return self._fetch_parts(self, parts, message)
  783 end
  784 
  785 
  786 function Mailbox.append_message(self, message, flags, date)
  787     _check_required(message, 'string')
  788     _check_optional(flags, { 'string', 'table' })
  789     _check_optional(date, 'string')
  790 
  791     if type(flags) == 'table' then flags = table.concat(flags, ' ') end
  792     self._check_connection(self)
  793     r = ifcore.append(self._account._account.session, self._mailbox, message,
  794                       flags, date)
  795     self._check_result(self, 'append', r)
  796     if r == false then return false end
  797 
  798     if options.info == true and r == true then
  799         print('Appended message of ' .. #message .. ' octets to ' ..
  800               self._string .. '.')
  801     end
  802 
  803     return true
  804 end
  805 
  806 
  807 function Mailbox.is_answered(self, messages)
  808     return self.send_query(self, 'ANSWERED', messages)
  809 end
  810 
  811 function Mailbox.is_deleted(self, messages)
  812     return self.send_query(self, 'DELETED', messages)
  813 end
  814 
  815 function Mailbox.is_draft(self, messages)
  816     return self.send_query(self, 'DRAFT', messages)
  817 end
  818 
  819 function Mailbox.is_flagged(self, messages)
  820     return self.send_query(self, 'FLAGGED', messages)
  821 end
  822 
  823 function Mailbox.is_new(self, messages)
  824     return self.send_query(self, 'NEW', messages)
  825 end
  826 
  827 function Mailbox.is_old(self, messages)
  828     return self.send_query(self, 'OLD', messages)
  829 end
  830 
  831 function Mailbox.is_recent(self, messages)
  832     return self.send_query(self, 'RECENT', messages)
  833 end
  834 
  835 function Mailbox.is_seen(self, messages)
  836     return self.send_query(self, 'SEEN', messages)
  837 end
  838 
  839 function Mailbox.is_unanswered(self, messages)
  840     return self.send_query(self, 'UNANSWERED', messages)
  841 end
  842 
  843 function Mailbox.is_undeleted(self, messages)
  844     return self.send_query(self, 'UNDELETED', messages)
  845 end
  846 
  847 function Mailbox.is_undraft(self, messages)
  848     return self.send_query(self, 'UNDRAFT', messages)
  849 end
  850 
  851 function Mailbox.is_unflagged(self, messages)
  852     return self.send_query(self, 'UNFLAGGED', messages)
  853 end
  854 
  855 function Mailbox.is_unseen(self, messages)
  856     return self.send_query(self, 'UNSEEN', messages)
  857 end
  858 
  859 
  860 function Mailbox.has_keyword(self, flag, message)
  861     _check_required(flag, 'string')
  862     return self.send_query(self, 'KEYWORD ' .. flag, messages)
  863 end
  864 
  865 Mailbox.has_flag = Mailbox.has_keyword
  866 
  867 function Mailbox.has_unkeyword(self, flag, message)
  868     _check_required(flag, 'string')
  869     return self.send_query(self, 'UNKEYWORD ' .. flag, messages)
  870 end
  871 
  872 
  873 function Mailbox.is_larger(self, size, messages)
  874     _check_required(size, 'number')
  875     _check_optional(messages, 'table')
  876     return self.send_query(self, 'LARGER ' .. tostring(size), messages)
  877 end
  878 
  879 function Mailbox.is_smaller(self, size, messages)
  880     _check_required(size, 'number')
  881     _check_optional(messages, 'table')
  882     return self.send_query(self, 'SMALLER ' .. tostring(size), messages)
  883 end
  884 
  885 
  886 function Mailbox.arrived_on(self, date, messages)
  887     _check_required(date, 'string')
  888     return self.send_query(self, 'ON ' .. date, messages)
  889 end
  890 
  891 function Mailbox.arrived_before(self, date, messages)
  892     _check_required(date, 'string')
  893     return self.send_query(self, 'BEFORE ' .. date, messages)
  894 end
  895 
  896 function Mailbox.arrived_since(self, date, messages)
  897     _check_required(date, 'string')
  898     return self.send_query(self, 'SINCE ' .. date, messages)
  899 end
  900 
  901 function Mailbox.sent_on(self, date, messages)
  902     _check_required(date, 'string')
  903     return self.send_query(self, 'SENTON ' .. date, messages)
  904 end
  905 
  906 function Mailbox.sent_before(self, date, messages)
  907     _check_required(date, 'string')
  908     return self.send_query(self, 'SENTBEFORE ' .. date, messages)
  909 end
  910 
  911 function Mailbox.sent_since(self, date, messages)
  912     _check_required(date, 'string')
  913     return self.send_query(self, 'SENTSINCE ' .. date, messages)
  914 end
  915 
  916 function Mailbox.is_newer(self, days, messages)
  917     _check_required(days, 'number')
  918     return self.send_query(self, 'SINCE ' .. form_date(days), messages)
  919 end
  920 
  921 function Mailbox.is_older(self, days, messages)
  922     _check_required(days, 'number')
  923     return self.send_query(self, 'BEFORE ' .. form_date(days), messages)
  924 end
  925 
  926 
  927 function Mailbox.contain_field(self, field, string, messages)
  928     _check_required(field, 'string')
  929     _check_required(string, 'string')
  930     return self.send_query(self, 'HEADER ' .. field .. ' "' .. string .. '"',
  931                            messages)
  932 end
  933 
  934 function Mailbox.contain_bcc(self, string, messages)
  935     _check_required(string, 'string')
  936     return self.send_query(self, 'BCC "' .. string .. '"', messages)
  937 end
  938 
  939 function Mailbox.contain_cc(self, string, messages)
  940     _check_required(string, 'string')
  941     return self.send_query(self, 'CC "' .. string .. '"', messages)
  942 end
  943 
  944 function Mailbox.contain_from(self, string, messages)
  945     _check_required(string, 'string')
  946     return self.send_query(self, 'FROM "' .. string .. '"', messages)
  947 end
  948 
  949 function Mailbox.contain_subject(self, string, messages)
  950     _check_required(string, 'string')
  951     return self.send_query(self, 'SUBJECT "' .. string .. '"', messages)
  952 end
  953 
  954 function Mailbox.contain_to(self, string, messages)
  955     _check_required(string, 'string')
  956     return self.send_query(self, 'TO "' .. string .. '"', messages)
  957 end
  958 
  959 function Mailbox.contain_header(self, string, messages)
  960     _check_required(string, 'string')
  961     return self.send_query(self, 'TEXT "' .. string .. '" NOT BODY "' ..
  962         string .. '"', messages)
  963 end
  964 
  965 function Mailbox.contain_body(self, string, messages)
  966     _check_required(string, 'string')
  967     return self.send_query(self, 'BODY "' .. string .. '"', messages)
  968 end
  969 
  970 function Mailbox.contain_message(self, string, messages)
  971     _check_required(string, 'string')
  972     return self.send_query(self, 'TEXT "' .. string .. '"', messages)
  973 end
  974 
  975 
  976 function Mailbox.match_field(self, field, pattern, messages)
  977     _check_required(field, 'string')
  978     _check_required(pattern, 'string')
  979 
  980     if not messages then messages = self._send_query(self) end
  981     local mesgs = _extract_messages(self, messages)
  982     local fields = self._fetch_fields(self, { field }, mesgs)
  983     if #mesgs == 0 or fields == nil then return Set({}) end
  984     local results = {}
  985     for m, f in pairs(fields) do
  986         if regex_search(pattern, (string.gsub(f, '^[^:]*: ?(.*)$', '%1'))) then
  987             table.insert(results, {self, m})
  988         end
  989     end
  990 
  991     return Set(results)
  992 end
  993 
  994 function Mailbox.match_bcc(self, pattern, messages)
  995     _check_required(pattern, 'string')
  996     return self.match_field(self, 'Bcc', pattern, messages)
  997 end
  998 
  999 function Mailbox.match_cc(self, pattern, messages)
 1000     _check_required(pattern, 'string')
 1001     return self.match_field(self, 'Cc', pattern, messages)
 1002 end
 1003 
 1004 function Mailbox.match_from(self, pattern, messages)
 1005     _check_required(pattern, 'string')
 1006     return self.match_field(self, 'From', pattern, messages)
 1007 end
 1008 
 1009 function Mailbox.match_subject(self, pattern, messages)
 1010     _check_required(pattern, 'string')
 1011     return self.match_field(self, 'Subject', pattern, messages)
 1012 end
 1013 
 1014 function Mailbox.match_to(self, pattern, messages)
 1015     _check_required(pattern, 'string')
 1016     return self.match_field(self, 'To', pattern, messages)
 1017 end
 1018 
 1019 function Mailbox.match_header(self, pattern, messages)
 1020     _check_required(pattern, 'string')
 1021 
 1022     if not messages then messages = self._send_query(self) end
 1023     local mesgs = _extract_messages(self, messages)
 1024     local header = self._fetch_header(self, mesgs)
 1025     if #mesgs == 0 or header == nil then return Set({}) end
 1026     local results = {}
 1027     for m, h in pairs(header) do
 1028         if regex_search(pattern, h) then table.insert(results, {self, m}) end
 1029     end
 1030 
 1031     return Set(results)
 1032 end
 1033 
 1034 function Mailbox.match_body(self, pattern, messages)
 1035     _check_required(pattern, 'string')
 1036 
 1037     if not messages then messages = self._send_query(self) end
 1038     local mesgs = _extract_messages(self, messages)
 1039     local body = self._fetch_body(self, mesgs)
 1040     if #mesgs == 0 or body == nil then return Set({}) end
 1041     local results = {}
 1042     for m, b in pairs(body) do
 1043         if regex_search(pattern, b) then table.insert(results, {self, m}) end
 1044     end
 1045 
 1046     return Set(results)
 1047 end
 1048 
 1049 function Mailbox.match_message(self, pattern, messages)
 1050     _check_required(pattern, 'string')
 1051 
 1052     if not messages then messages = self._send_query(self) end
 1053     local mesgs = _extract_messages(self, messages)
 1054     local full = self._fetch_message(self, mesgs)
 1055     if #mesgs == 0 or full == nil then return Set({}) end
 1056     local results = {}
 1057     for m, f in pairs(full) do
 1058         if regex_search(pattern, f) then table.insert(results, {self, m}) end
 1059     end
 1060 
 1061     return Set(results)
 1062 end
 1063 
 1064 
 1065 function Mailbox.enter_idle(self)
 1066     if self._cached_select(self) ~= true then return false end
 1067 
 1068     self._check_connection(self)
 1069     local r, event = ifcore.idle(self._account._account.session)
 1070     self._check_result(self, 'idle', r)
 1071     if r == false then return false end
 1072 
 1073     if options.close == true then self._cached_close(self) end
 1074 
 1075     if type(event) == 'string' then
 1076         return true, string.upper(event)
 1077     else
 1078         return true
 1079     end
 1080 end
 1081 
 1082 
 1083 Mailbox.open = _cached_select
 1084 Mailbox.close = _cached_close
 1085 
 1086 Mailbox._mt.__index = function () end
 1087 Mailbox._mt.__newindex = function () end