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 }