bincimap

Log | Files | Refs | LICENSE

operator-list.cc (7575B)


      1 /* -*- Mode: c++; -*- */
      2 /*  --------------------------------------------------------------------
      3  *  Filename:
      4  *    operator-list.cc
      5  *  
      6  *  Description:
      7  *    Implementation of the LIST command.
      8  *
      9  *  Authors:
     10  *    Andreas Aardal Hanssen <andreas-binc curly bincimap spot org>
     11  *
     12  *  Bugs:
     13  *
     14  *  ChangeLog:
     15  *
     16  *  --------------------------------------------------------------------
     17  *  Copyright 2002-2005 Andreas Aardal Hanssen
     18  *
     19  *  This program is free software; you can redistribute it and/or modify
     20  *  it under the terms of the GNU General Public License as published by
     21  *  the Free Software Foundation; either version 2 of the License, or
     22  *  (at your option) any later version.
     23  *
     24  *  This program is distributed in the hope that it will be useful,
     25  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     26  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     27  *  GNU General Public License for more details.
     28  *
     29  *  You should have received a copy of the GNU General Public License
     30  *  along with this program; if not, write to the Free Software
     31  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
     32  *  --------------------------------------------------------------------
     33  */
     34 #ifdef HAVE_CONFIG_H
     35 #include <config.h>
     36 #endif
     37 
     38 #include <sys/types.h>
     39 #include <dirent.h>
     40 #include <sys/stat.h>
     41 
     42 #include <string>
     43 #include <iostream>
     44 
     45 #include "io.h"
     46 
     47 #include "mailbox.h"
     48 
     49 #include "regmatch.h"
     50 #include "convert.h"
     51 
     52 #include "recursivedescent.h"
     53 
     54 #include "session.h"
     55 #include "depot.h"
     56 #include "operators.h"
     57 
     58 using namespace ::std;
     59 using namespace Binc;
     60 
     61 namespace {
     62   const time_t LIST_CACHE_TIMEOUT = 10;
     63 }
     64 
     65 //----------------------------------------------------------------------
     66 ListOperator::ListOperator(void)
     67 {
     68   cacheTimeout = 0;
     69 }
     70 
     71 //----------------------------------------------------------------------
     72 ListOperator::~ListOperator(void)
     73 {
     74 }
     75 
     76 //----------------------------------------------------------------------
     77 const string ListOperator::getName(void) const
     78 {
     79   return "LIST";
     80 }
     81 
     82 //----------------------------------------------------------------------
     83 int ListOperator::getState(void) const
     84 {
     85   return Session::AUTHENTICATED | Session::SELECTED;
     86 }
     87 
     88 //------------------------------------------------------------------------
     89 Operator::ProcessResult ListOperator::process(Depot &depot,
     90 					      Request &command)
     91 {
     92   IO &com = IOFactory::getInstance().get(1);
     93   Session &session = Session::getInstance();
     94   const char delim = depot.getDelimiter();
     95 
     96   // special case: if the mailbox argument is empty, then give a
     97   // hard coded reply.
     98   string wildcard;
     99   if ((wildcard = command.getListMailbox()) == "") {
    100     com << "* LIST (\\Noselect) \"" << delim << "\" \"\"" << endl;
    101     return OK;
    102   }
    103 
    104   // remove leading or trailing delimiter in wildcard
    105   trim(wildcard, string(&delim, 1));
    106 
    107   // convert wildcard to regular expression
    108   string regex = toRegex(wildcard, depot.getDelimiter());
    109   string wildcardLower = regex;
    110   lowercase(wildcardLower);
    111   if (wildcardLower.substr(0, 6) == "^inbox")
    112     regex = "^[iI][nN][bB][oO][xX]" + regex.substr(6);
    113 
    114   // remove leading or trailing delimiter in reference
    115   string ref = command.getMailbox();
    116   trim(ref, string(&delim, 1));
    117   wildcardLower = ref;
    118   lowercase(wildcardLower);
    119   if (wildcardLower.substr(0, 5) == "inbox"
    120       && (wildcardLower.length() == 5 || wildcardLower[5] == delim))
    121     ref = "INBOX" + ref.substr(5);
    122 
    123   // a map from mailbox name to flags
    124   map<string, unsigned int> mailboxes;
    125 
    126   if (cacheTimeout == 0 || cacheTimeout < time(0) - LIST_CACHE_TIMEOUT
    127       || session.mailboxchanges) {
    128     session.mailboxchanges = false;
    129 
    130     // read through all entries in depository.
    131     for (Depot::iterator i = depot.begin("."); i != depot.end(); ++i) {
    132       const string path = *i;
    133       if (path == "")
    134 	continue;
    135 
    136       const string mpath = depot.filenameToMailbox(path);
    137       Mailbox *m = 0;
    138       
    139       // skip entries that are not identified as mailboxes
    140       if ((m = depot.get(mpath)) == 0)
    141 	continue;
    142       
    143       // convert file name to mailbox name. skip it if there is no
    144       // corresponding mailbox name.
    145       string tmp = toCanonMailbox(depot.filenameToMailbox(path));
    146       trim(tmp, string(&delim, 1));
    147       if (tmp == "") continue;
    148       else {
    149 	// inherit flags that were already set for this mailbox.
    150 	int flags = DIR_SELECT;
    151 	if (m->isMarked(path)) flags |= DIR_MARKED;
    152 	if (mailboxes.find(tmp) != mailboxes.end()) flags |= mailboxes[tmp];
    153 	mailboxes[tmp] = flags;
    154       }
    155 
    156       // now add all superior mailboxes with no flags set if not
    157       // added already.
    158       string::size_type pos = tmp.rfind(delim);
    159       while (pos != string::npos) {
    160 	tmp = tmp.substr(0, pos);
    161 	trim(tmp, string(&delim, 1));
    162 	
    163 	if (mailboxes.find(tmp) == mailboxes.end())
    164 	  mailboxes[tmp] = 0;
    165 	
    166 	pos = tmp.rfind(delim);
    167       }
    168     }
    169 
    170     // find leaf nodes O(N^2)
    171     map<string, unsigned int>::iterator i;
    172     for (i = mailboxes.begin(); i != mailboxes.end(); ++i) {
    173       string mailbox = i->first;
    174       mailbox += delim;
    175       
    176       bool leaf = true;
    177       map<string, unsigned int>::const_iterator j = mailboxes.begin();
    178       for (; j != mailboxes.end(); ++j) {
    179 	string::size_type pos = j->first.rfind(delim);
    180 	if (pos == string::npos) continue;
    181 	
    182 	string base = j->first.substr(0, pos + 1);
    183 	
    184 	if (mailbox == base) {
    185 	  leaf = false;
    186 	  break;
    187 	}
    188       }
    189 
    190       if (leaf) {
    191 	unsigned int flags = i->second;
    192 	flags |= DIR_LEAF;
    193 	i->second = flags;
    194       }
    195     }
    196 
    197     cache = mailboxes;
    198     cacheTimeout = time(0);
    199   } else {
    200     mailboxes = cache;
    201     cacheTimeout = time(0);
    202   }
    203 
    204   // finally, print all mailbox entries with flags.  
    205   map<string, unsigned int>::iterator i = mailboxes.begin();
    206   for (; i != mailboxes.end(); ++i) {
    207     if (ref == "" || (ref.length() <= i->first.length() && ref == i->first.substr(0, ref.length())))
    208       if (regexMatch(i->first.substr(ref.length()), regex) == 0) {
    209 	com << "* LIST (";
    210 	string sep = "";
    211 	
    212 	int flags = i->second;
    213 	bool noselect = false;
    214 
    215 	if (!(flags & DIR_SELECT)) {
    216 	  com << sep << "\\Noselect";
    217 	  sep = " ";
    218 	  noselect = true;
    219 	}
    220 	
    221 	if (!noselect) {
    222 	  if (flags & DIR_MARKED)
    223 	    com << sep << "\\Marked";
    224 	  else
    225 	    com << sep << "\\Unmarked";
    226 	  sep = " ";
    227 	}
    228 
    229 	if (flags & DIR_NOINFERIORS)
    230 	  com << sep << "\\Noinferiors";
    231 
    232 	com << ") \"" << depot.getDelimiter() << "\" "
    233 	    << toImapString(i->first) << endl;
    234       }
    235   }
    236 
    237   return OK;
    238 }
    239 
    240 //----------------------------------------------------------------------
    241 Operator::ParseResult ListOperator::parse(Request &c_in) const
    242 {
    243   Session &session = Session::getInstance();
    244 
    245   if (c_in.getUidMode())
    246     return REJECT;
    247 
    248   Operator::ParseResult res;
    249   if ((res = expectSPACE()) != ACCEPT) {
    250     session.setLastError("Expected SPACE after LIST");
    251     return res;
    252   }
    253 
    254   string mailbox;
    255   if ((res = expectMailbox(mailbox)) != ACCEPT) {
    256     session.setLastError("Expected mailbox after LIST SPACE");
    257     return res;
    258   }
    259 
    260   c_in.setMailbox(mailbox);
    261 
    262   if ((res = expectSPACE()) != ACCEPT) {
    263     session.setLastError("Expected SPACE after LIST SPACE mailbox");
    264     return res;
    265   }
    266 
    267   string listmailbox;
    268   if ((res = expectListMailbox(listmailbox)) != ACCEPT) {
    269     session.setLastError("Expected list_mailbox after LIST SPACE"
    270 			 " mailbox SPACE");
    271     return res;
    272   }
    273 
    274   if ((res = expectCRLF()) != ACCEPT) {
    275     session.setLastError("Expected CRLF after LIST SPACE mailbox"
    276 			 " SPACE list_mailbox");
    277     return res;
    278   }
    279 
    280   c_in.setListMailbox(listmailbox);
    281   c_in.setName("LIST");
    282   return ACCEPT;
    283 }