bincimap

Log | Files | Refs | LICENSE

operator-lsub.cc (7309B)


      1 /* -*- Mode: c++; -*- */
      2 /*  --------------------------------------------------------------------
      3  *  Filename:
      4  *    bincimapd-lsub.cc
      5  *  
      6  *  Description:
      7  *    Implementation of the LSUB 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 <algorithm>
     39 #include <string>
     40 #include <vector>
     41 #include <iostream>
     42 
     43 #include "recursivedescent.h"
     44 
     45 #include "io.h"
     46 #include "mailbox.h"
     47 #include "convert.h"
     48 #include "regmatch.h"
     49 
     50 #include "session.h"
     51 #include "depot.h"
     52 #include "operators.h"
     53 
     54 namespace {
     55   const int DIR_SELECT      = 0x01;
     56   const int DIR_MARKED      = 0x02;
     57   const int DIR_NOINFERIORS = 0x04;
     58 }
     59 
     60 using namespace ::std;
     61 using namespace Binc;
     62 
     63 //----------------------------------------------------------------------
     64 LsubOperator::LsubOperator(void)
     65 {
     66 }
     67 
     68 //----------------------------------------------------------------------
     69 LsubOperator::~LsubOperator(void)
     70 {
     71 }
     72 
     73 //----------------------------------------------------------------------
     74 const string LsubOperator::getName(void) const
     75 {
     76   return "LSUB";
     77 }
     78 
     79 //----------------------------------------------------------------------
     80 int LsubOperator::getState(void) const
     81 {
     82   return Session::AUTHENTICATED | Session::SELECTED;
     83 }
     84 
     85 //------------------------------------------------------------------------
     86 Operator::ProcessResult LsubOperator::process(Depot &depot,
     87 					      Request &command)
     88 {
     89   Session &session = Session::getInstance();
     90   IO &com = IOFactory::getInstance().get(1);
     91   const char delim = depot.getDelimiter();
     92 
     93   // remove leading or trailing delimiter in wildcard
     94   string wildcard = command.getListMailbox();
     95   trim(wildcard, string(&delim, 1));
     96 
     97   // convert wildcard to regular expression
     98   string regex = toRegex(wildcard, depot.getDelimiter());
     99   string wildcardLower = regex;
    100   lowercase(wildcardLower);
    101   if (wildcardLower.substr(0, 6) == "^inbox")
    102     regex = "^[iI][nN][bB][oO][xX]" + regex.substr(6);
    103 
    104   // remove leading or trailing delimiter in reference
    105   string ref = command.getMailbox();
    106   trim(ref, string(&delim, 1));
    107   wildcardLower = ref;
    108   lowercase(wildcardLower);
    109   if (wildcardLower.substr(0, 5) == "inbox"
    110       && (wildcardLower.length() == 5 || wildcardLower[5] == delim))
    111     ref = "INBOX" + ref.substr(5);
    112 
    113   // a multimap from mailbox name to flags
    114   multimap<string, int> mailboxes;
    115 
    116   // read through all entries in depository.
    117   for (Depot::iterator i = depot.begin("."); i != depot.end(); ++i) {
    118     const string path = *i;
    119     if (path == "")
    120       continue;
    121 
    122     const string mpath = depot.filenameToMailbox(path);
    123     Mailbox *m = 0;
    124 
    125     // skip entries that are not identified as mailboxes
    126     if ((m = depot.get(mpath)) == 0)
    127       continue;
    128 
    129     // convert file name to mailbox name. skip it if there is no
    130     // corresponding mailbox name.
    131     string tmp = toCanonMailbox(depot.filenameToMailbox(path));
    132     trim(tmp, string(&delim, 1));
    133     if (tmp == "")
    134       continue;
    135     else {
    136       int flags = DIR_SELECT;
    137       multimap<string, int>::iterator mi = mailboxes.find(tmp);
    138       if (mi != mailboxes.end()) {
    139 	flags |= mi->second;
    140 	mailboxes.erase(mi);
    141       }
    142 
    143       mailboxes.insert(make_pair(tmp, flags));
    144     }
    145 
    146     // now add all superior mailboxes with no flags set if not
    147     // added already.
    148     string::size_type pos = tmp.rfind(delim);
    149     while (pos != string::npos) {
    150       tmp = tmp.substr(0, pos);
    151       trim(tmp, string(&delim, 1));
    152 
    153       multimap<string, int>::iterator mi = mailboxes.find(tmp);
    154       if (mi == mailboxes.end())
    155 	mailboxes.insert(make_pair(tmp, 0));
    156 
    157       pos = tmp.rfind(delim);
    158     }
    159   }
    160 
    161   // find leaf nodes O(N^2)
    162   map<string, int>::iterator i;
    163   for (i = mailboxes.begin(); i != mailboxes.end(); ++i) {
    164     string mailbox = i->first;
    165     mailbox += delim;
    166 
    167     bool leaf = true;
    168     map<string, int>::const_iterator j;
    169     for (j = mailboxes.begin(); j != mailboxes.end(); ++j) {
    170       string::size_type pos = j->first.rfind(delim);
    171       if (pos == string::npos) continue;
    172 
    173       string base = j->first.substr(0, pos + 1);
    174 
    175       if (mailbox == base) {
    176 	leaf = false;
    177 	break;
    178       }
    179     }
    180   }
    181 
    182   session.loadSubscribes();
    183 
    184   vector<string> &subscribed = session.subscribed;
    185   sort(subscribed.begin(), subscribed.end());
    186 
    187   // finally, print all mailbox entries with flags.  
    188   for (vector<string>::const_iterator j = subscribed.begin();
    189        j != subscribed.end(); ++j) {
    190     if (ref == "" || (ref.length() <= (*j).length() && ref == (*j).substr(0, ref.length())))
    191       if (regexMatch((*j).substr(ref.length()), regex) == 0) {
    192 
    193 	int flags = 0;
    194     
    195 	for (i = mailboxes.begin(); i != mailboxes.end(); ++i) {
    196 	  if (i->first == *j) {
    197 	    flags = i->second;
    198 	    break;
    199 	  }
    200 	}
    201 
    202 	com << "* LSUB (";
    203 	string sep = "";
    204 
    205 	bool noselect = false;
    206 	if (!(flags & DIR_SELECT)) {
    207 	  com << sep << "\\Noselect";
    208 	  sep = " ";
    209 	  noselect = true;
    210 	}
    211 	
    212 	if (!noselect) {
    213 	  if (flags & DIR_MARKED)
    214 	    com << sep << "\\Marked";
    215 	  else
    216 	    com << sep << "\\Unmarked";
    217 	  sep = " ";
    218 	}
    219 
    220 	if (flags & DIR_NOINFERIORS)
    221 	  com << sep << "\\Noinferiors";
    222     
    223 	com << ") \"" << depot.getDelimiter() << "\" "
    224 	    << toImapString(*j) << endl;
    225       }
    226   }
    227     
    228   return OK;
    229 }
    230 
    231 //----------------------------------------------------------------------
    232 Operator::ParseResult LsubOperator::parse(Request &c_in) const
    233 {
    234   Session &session = Session::getInstance();
    235 
    236   if (c_in.getUidMode())
    237     return REJECT;
    238 
    239   Operator::ParseResult res;
    240   if ((res = expectSPACE()) != ACCEPT) {
    241     session.setLastError("Expected SPACE after LSUB");
    242     return ERROR;
    243   }
    244 
    245   string mailbox;
    246   if ((res = expectMailbox(mailbox)) != ACCEPT) {
    247     session.setLastError("Expected mailbox after LSUB SPACE");
    248     return ERROR;
    249   }
    250 
    251   c_in.setMailbox(mailbox);
    252 
    253   if ((res = expectSPACE()) != ACCEPT) {
    254     session.setLastError("Expected SPACE after LSUB SPACE mailbox");
    255     return ERROR;
    256   }
    257 
    258   string listmailbox;
    259   if ((res = expectListMailbox(listmailbox)) != ACCEPT) {
    260     session.setLastError("Expected list_mailbox after LSUB SPACE"
    261 			 " mailbox SPACE");
    262     return ERROR;
    263   }
    264 
    265   if ((res = expectCRLF()) != ACCEPT) {
    266     session.setLastError("Expected CRLF after LSUB SPACE"
    267 			 " mailbox SPACE list_mailbox");
    268     return ERROR;
    269   }
    270 
    271   c_in.setListMailbox(listmailbox);
    272   c_in.setName("LSUB");
    273   return ACCEPT;
    274 }