operator-copy.cc (5906B)
1 /* -*- Mode: c++; -*- */ 2 /* -------------------------------------------------------------------- 3 * Filename: 4 * operator-copy.cc 5 * 6 * Description: 7 * Implementation of the COPY 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 <string> 39 40 #include "depot.h" 41 #include "io.h" 42 #include "maildir.h" 43 #include "operators.h" 44 #include "recursivedescent.h" 45 #include "session.h" 46 #include "convert.h" 47 48 using namespace ::std; 49 using namespace Binc; 50 51 //---------------------------------------------------------------------- 52 CopyOperator::CopyOperator(void) 53 { 54 } 55 56 //---------------------------------------------------------------------- 57 CopyOperator::~CopyOperator(void) 58 { 59 } 60 61 //---------------------------------------------------------------------- 62 const string CopyOperator::getName(void) const 63 { 64 return "COPY"; 65 } 66 67 //---------------------------------------------------------------------- 68 int CopyOperator::getState(void) const 69 { 70 return Session::SELECTED; 71 } 72 73 //------------------------------------------------------------------------ 74 Operator::ProcessResult CopyOperator::process(Depot &depot, 75 Request &command) 76 { 77 Session &session = Session::getInstance(); 78 IO &logger = IOFactory::getInstance().get(2); 79 80 // Get the current mailbox 81 Mailbox *srcMailbox = depot.getSelected(); 82 83 // Get the destination mailbox 84 string dmailbox = command.getMailbox(); 85 Mailbox *destMailbox = depot.get(toCanonMailbox(dmailbox)); 86 if (destMailbox == 0) { 87 session.setResponseCode("TRYCREATE"); 88 session.setLastError("invalid mailbox " + toImapString(dmailbox)); 89 return NO; 90 } 91 92 unsigned int mode = Mailbox::SKIP_EXPUNGED; 93 mode |= command.getUidMode() ? Mailbox::UID_MODE : Mailbox::SQNR_MODE; 94 95 // Copy each message in the sequence set to the destination mailbox. 96 bool success = true; 97 Mailbox::iterator i = srcMailbox->begin(command.bset, mode); 98 for (; success && i != srcMailbox->end(); ++i) { 99 Message &source = *i; 100 101 if (srcMailbox->fastCopy(source, *destMailbox, 102 depot.mailboxToFilename(toCanonMailbox(dmailbox)))) 103 continue; 104 105 // Have the destination mailbox create a message for us. 106 Message *dest 107 = destMailbox->createMessage(depot.mailboxToFilename(toCanonMailbox(dmailbox)), 108 source.getInternalDate()); 109 if (!dest) { 110 session.setLastError(destMailbox->getLastError()); 111 success = false; 112 break; 113 } 114 115 // Set the flags and internal date. 116 dest->setStdFlag(source.getStdFlags()); 117 dest->setInternalDate(source.getInternalDate()); 118 119 // Reset the read position to the beginning of the source message 120 source.rewind(); 121 122 // Copy chunks from the source message over to the destination 123 // message. 124 string chunk; 125 do { 126 int readSize = source.readChunk(chunk); 127 128 if (readSize == 0) 129 break; 130 else if (readSize == -1) { 131 logger << "when reading from message " 132 << i.getSqnr() << "/" << source.getUID() 133 << " in \"" << srcMailbox->getName() << "\": " 134 << source.getLastError() << endl; 135 success = false; 136 } else if (!dest->appendChunk(chunk)) { 137 logger << "when writing to \"" 138 << dmailbox << "\": " 139 << dest->getLastError() << endl; 140 success = false; 141 } 142 } while (success); 143 144 dest->close(); 145 } 146 147 if (!success && !destMailbox->rollBackNewMessages()) { 148 session.setLastError("Failed to rollback after unsuccessful copy: " 149 + destMailbox->getLastError()); 150 return NO; 151 } 152 153 if (success) 154 if (!destMailbox->commitNewMessages(depot.mailboxToFilename(toCanonMailbox(dmailbox)))) { 155 session.setLastError("Failed to commit after successful copy: " 156 + destMailbox->getLastError()); 157 return NO; 158 } 159 160 if (!success) 161 session.setLastError("The transaction was unrolled. Please " 162 "contant your system administrator for " 163 "more information."); 164 165 return success ? OK : NO; 166 } 167 168 //------------------------------------------------------------------------ 169 Operator::ParseResult CopyOperator::parse(Request &c_in) const 170 { 171 Session &session = Session::getInstance(); 172 173 Operator::ParseResult res; 174 if ((res = expectSPACE()) != ACCEPT) { 175 session.setLastError("Expected SPACE after COPY"); 176 return res; 177 } 178 179 if ((res = expectSet(c_in.getSet())) != ACCEPT) { 180 session.setLastError("Expected sequence set after COPY SPACE"); 181 return res; 182 } 183 184 if ((res = expectSPACE()) != ACCEPT) { 185 session.setLastError("Expected SPACE after COPY SPACE set"); 186 return res; 187 } 188 189 string mailbox; 190 if ((res = expectMailbox(mailbox)) != ACCEPT) { 191 session.setLastError("Expected mailbox after COPY SPACE set SPACE"); 192 return res; 193 } 194 195 if ((res = expectCRLF()) != ACCEPT) { 196 session.setLastError("Expected CRLF after COPY SPACE set SPACE mailbox"); 197 return res; 198 } 199 200 c_in.setMailbox(mailbox); 201 c_in.setName("COPY"); 202 203 return ACCEPT; 204 }