IMAPResult IMAPCommandDeleteAcl::ExecuteCommand(shared_ptr<HM::IMAPConnection> pConnection, shared_ptr<IMAPCommandArgument> pArgument) { if (!pConnection->IsAuthenticated()) return IMAPResult(IMAPResult::ResultNo, "Authenticate first"); if (!Configuration::Instance()->GetIMAPConfiguration()->GetUseIMAPACL()) return IMAPResult(IMAPResult::ResultBad, "ACL is not enabled."); shared_ptr<IMAPSimpleCommandParser> pParser = shared_ptr<IMAPSimpleCommandParser>(new IMAPSimpleCommandParser()); pParser->Parse(pArgument); if (pParser->ParamCount() != 2) return IMAPResult(IMAPResult::ResultBad, "DELETEACL command requires 2 parameter."); String sOriginalFolderName; String sFolderName; if (pParser->Word(1)->Clammerized()) { sFolderName = pArgument->Literal(0); sOriginalFolderName = sFolderName; } else { sOriginalFolderName = pParser->Word(1)->Value(); sFolderName = sOriginalFolderName; IMAPFolder::UnescapeFolderString(sFolderName); } String sIdentifier = pParser->Word(2)->Value(); shared_ptr<IMAPFolder> pFolder = pConnection->GetFolderByFullPath(sFolderName); if (!pFolder) return IMAPResult(IMAPResult::ResultBad, "Folder could not be found."); // Check if the user has access to delete ACL for this folder if (!pConnection->CheckPermission(pFolder, ACLPermission::PermissionAdminister)) return IMAPResult(IMAPResult::ResultNo, "ACL: DeleteACL permission denied"); // Assume identifier is a user and not a group. shared_ptr<const Account> pAccount = CacheContainer::Instance()->GetAccount(sIdentifier); if (!pAccount) { // Do something. We cannot give permissions to an object which does not exist. return IMAPResult(IMAPResult::ResultNo, "DeleteACL failed. Identifier not found"); } if (!pFolder->GetPermissions()->DeletePermissionsForAccount(pAccount->GetID())) return IMAPResult(IMAPResult::ResultNo, "DeleteACL failed"); String sResponse = pArgument->Tag() + _T(" OK DeleteACL complete\r\n"); pConnection->SendAsciiData(sResponse); return IMAPResult(); }
IMAPResult IMAPCommandSUBSCRIBE::ExecuteCommand(shared_ptr<HM::IMAPConnection> pConnection, shared_ptr<IMAPCommandArgument> pArgument) { if (!pConnection->IsAuthenticated()) return IMAPResult(IMAPResult::ResultNo, "Authenticate first"); shared_ptr<IMAPSimpleCommandParser> pParser = shared_ptr<IMAPSimpleCommandParser>(new IMAPSimpleCommandParser()); pParser->Parse(pArgument); if (pParser->ParamCount() < 1) return IMAPResult(IMAPResult::ResultBad, "Command requires at least 1 parameter."); // Fetch the folder name. String sFolderName = pParser->GetParamValue(pArgument, 0); if (sFolderName == Configuration::Instance()->GetIMAPConfiguration()->GetIMAPPublicFolderName()) { // Silently accept this. If we don't, some email clients (such as OE) will complain that it won't // be possible to subscribe to sub folders String sResponse = pArgument->Tag() + " OK Subscribe completed\r\n"; pConnection->SendAsciiData(sResponse); return IMAPResult(); } shared_ptr<IMAPFolder> pFolder = pConnection->GetFolderByFullPath(sFolderName); if (!pFolder) return IMAPResult(IMAPResult::ResultNo, "Folder could not be found."); // Check if ther user has access to see this folder. if (!pConnection->CheckPermission(pFolder, ACLPermission::PermissionLookup)) return IMAPResult(IMAPResult::ResultNo, "ACL: Lookup permission denied (required for SUBSCRIBE)."); if (!pFolder) return IMAPResult(IMAPResult::ResultNo, "That mailbox does not exist."); pFolder->SetIsSubscribed(true); PersistentIMAPFolder::SaveObject(pFolder); String sResponse = pArgument->Tag() + " OK Subscribe completed\r\n"; pConnection->SendAsciiData(sResponse); return IMAPResult(); }
IMAPResult IMAPCommandEXPUNGE::ExecuteCommand(shared_ptr<IMAPConnection> pConnection, shared_ptr<IMAPCommandArgument> pArgument) { if (!pConnection->IsAuthenticated()) return IMAPResult(IMAPResult::ResultNo, "Authenticate first"); if (pConnection->GetCurrentFolderReadOnly()) { return IMAPResult(IMAPResult::ResultNo, "Expunge command on read-only folder."); } // Iterate through mail boxes and delete messages marked for deletion. shared_ptr<IMAPFolder> pCurFolder = pConnection->GetCurrentFolder(); if (!pCurFolder) return IMAPResult(IMAPResult::ResultNo, "No folder selected."); if (!pConnection->CheckPermission(pCurFolder, ACLPermission::PermissionExpunge)) return IMAPResult(IMAPResult::ResultBad, "ACL: Expunge permission denied (Required for EXPUNGE command)."); std::vector<int> vecExpungedMessages = pCurFolder->Expunge(); std::vector<int>::iterator iterExpunged = vecExpungedMessages.begin(); String sResponse; while (iterExpunged != vecExpungedMessages.end()) { String sTemp; sTemp.Format(_T("* %d EXPUNGE\r\n"), (*iterExpunged)); sResponse += sTemp; iterExpunged++; } pConnection->SendAsciiData(sResponse); if (!vecExpungedMessages.empty()) { std::vector<__int64> affectedMessages; boost_foreach(__int64 messageIndex, vecExpungedMessages) { affectedMessages.push_back(messageIndex); }
IMAPResult IMAPCommandAppend::ExecuteCommand(shared_ptr<IMAPConnection> pConnection, shared_ptr<IMAPCommandArgument> pArgument) { if (!pConnection->IsAuthenticated()) return IMAPResult(IMAPResult::ResultNo, "Authenticate first"); m_sCurrentTag = pArgument->Tag(); // Reset these two so we don't re-use old values. m_sFlagsToSet = ""; m_sCreateTimeToSet = ""; shared_ptr<IMAPSimpleCommandParser> pParser = shared_ptr<IMAPSimpleCommandParser>(new IMAPSimpleCommandParser()); pParser->Parse(pArgument); if (pParser->WordCount() < 3) return IMAPResult(IMAPResult::ResultBad, "APPEND Command requires at least 2 parameter."); // Create a new mailbox String sFolderName = pParser->GetParamValue(pArgument, 0); if (!pParser->Word(1)->Clammerized()) IMAPFolder::UnescapeFolderString(sFolderName); if (pParser->ParantheziedWord()) m_sFlagsToSet = pParser->ParantheziedWord()->Value(); // last word. shared_ptr<IMAPSimpleWord> pWord = pParser->Word(pParser->WordCount()-1); if (!pWord || !pWord->Clammerized()) return IMAPResult(IMAPResult::ResultBad, "Missing literal"); AnsiString literalSize = pWord->Value(); m_lBytesLeftToReceive = atoi(literalSize); if (m_lBytesLeftToReceive == 0) return IMAPResult(IMAPResult::ResultBad, "Empty message not permitted."); // Add an extra two bytes since we expect a <newline> in the end. m_lBytesLeftToReceive += 2; shared_ptr<const Domain> domain = CacheContainer::Instance()->GetDomain(pConnection->GetAccount()->GetDomainID()); int maxMessageSizeKB = _GetMaxMessageSize(domain); if (maxMessageSizeKB > 0 && m_lBytesLeftToReceive / 1024 > maxMessageSizeKB) { String sMessage; sMessage.Format(_T("Message size exceeds fixed maximum message size. Size: %d KB, Max size: %d KB"), m_lBytesLeftToReceive / 1024, maxMessageSizeKB); return IMAPResult(IMAPResult::ResultNo, sMessage); } // Locate the parameter containing the date to set. // Can't use pParser->QuotedWord() since there may // be many quoted words in the command. for (int i = 2; i < pParser->WordCount(); i++) { shared_ptr<IMAPSimpleWord> pWord = pParser->Word(i); if (pWord->Quoted()) { m_sCreateTimeToSet = pWord->Value(); // date-day-fixed = (SP DIGIT) / 2DIGIT // ; Fixed-format version of date-day // If the date given starts with <space>number, we need // to Trim. Doesn't hurt to always do this. m_sCreateTimeToSet.TrimLeft(); } } m_pDestinationFolder = pConnection->GetFolderByFullPath(sFolderName); if (!m_pDestinationFolder) return IMAPResult(IMAPResult::ResultBad, "Folder could not be found."); if (!m_pDestinationFolder->IsPublicFolder()) { // Make sure that this message fits in the mailbox. shared_ptr<const Account> pAccount = CacheContainer::Instance()->GetAccount(pConnection->GetAccount()->GetID()); if (!pAccount) return IMAPResult(IMAPResult::ResultNo, "Account could not be fetched."); if (!pAccount->SpaceAvailable(m_lBytesLeftToReceive)) return IMAPResult(IMAPResult::ResultNo, "Your quota has been exceeded."); } if (!pConnection->CheckPermission(m_pDestinationFolder, ACLPermission::PermissionInsert)) return IMAPResult(IMAPResult::ResultBad, "ACL: Insert permission denied (Required for APPEND command)."); __int64 lFolderID = m_pDestinationFolder->GetID(); m_pCurrentMessage = shared_ptr<Message>(new Message); m_pCurrentMessage->SetAccountID(m_pDestinationFolder->GetAccountID()); m_pCurrentMessage->SetFolderID(lFolderID); // Construct a file name which we'll write the message to. // Should we connect this message to an account? Yes, if this is not a public folder. shared_ptr<const Account> pMessageOwner; if (!m_pDestinationFolder->IsPublicFolder()) pMessageOwner = pConnection->GetAccount(); m_sMessageFileName = PersistentMessage::GetFileName(pMessageOwner, m_pCurrentMessage); String sResponse = "+ Ready for literal data\r\n"; pConnection->SetReceiveBinary(true); pConnection->SendAsciiData(sResponse); return IMAPResult(); }
void IMAPCommandAppend::_Finish(shared_ptr<IMAPConnection> pConnection) { if (!m_pCurrentMessage) return; // Add this message to the folder. m_pCurrentMessage->SetSize(FileUtilities::FileSize(m_sMessageFileName)); m_pCurrentMessage->SetState(Message::Delivered); // Set message flags. bool bSeen = (m_sFlagsToSet.FindNoCase(_T("\\Seen")) >= 0); bool bDeleted = (m_sFlagsToSet.FindNoCase(_T("\\Deleted")) >= 0); bool bDraft = (m_sFlagsToSet.FindNoCase(_T("\\Draft")) >= 0); bool bAnswered = (m_sFlagsToSet.FindNoCase(_T("\\Answered")) >= 0); bool bFlagged = (m_sFlagsToSet.FindNoCase(_T("\\Flagged")) >= 0); if (bSeen) { // ACL: If user tries to set the Seen flag, check that he has permission to do so. if (!pConnection->CheckPermission(m_pDestinationFolder, ACLPermission::PermissionWriteSeen)) { // User does not have permission to set the Seen flag. bSeen = false; } } m_pCurrentMessage->SetFlagDeleted(bDeleted); m_pCurrentMessage->SetFlagSeen(bSeen); m_pCurrentMessage->SetFlagDraft(bDraft); m_pCurrentMessage->SetFlagAnswered(bAnswered); m_pCurrentMessage->SetFlagFlagged(bFlagged); m_pCurrentMessage->SetFlagRecent(true); // Set the create time if (!m_sCreateTimeToSet.IsEmpty()) { // Convert to internal format m_sCreateTimeToSet = Time::GetInternalDateFromIMAPInternalDate(m_sCreateTimeToSet); m_pCurrentMessage->SetCreateTime(m_sCreateTimeToSet); } PersistentMessage::SaveObject(m_pCurrentMessage); m_pDestinationFolder->SetFolderNeedsRefresh(); String sResponse; if (pConnection->GetCurrentFolder() && pConnection->GetCurrentFolder()->GetID() == m_pDestinationFolder->GetID()) { shared_ptr<Messages> messages = m_pDestinationFolder->GetMessages(); sResponse += IMAPNotificationClient::GenerateExistsString(messages->GetCount()); sResponse += IMAPNotificationClient::GenerateRecentString(messages->GetNoOfRecent()); } // Send the OK response to the client. sResponse += m_sCurrentTag + " OK APPEND completed\r\n"; pConnection->SendAsciiData(sResponse); // Notify the mailbox notifier that the mailbox contents have changed. shared_ptr<ChangeNotification> pNotification = shared_ptr<ChangeNotification>(new ChangeNotification(m_pDestinationFolder->GetAccountID(), m_pDestinationFolder->GetID(), ChangeNotification::NotificationMessageAdded)); Application::Instance()->GetNotificationServer()->SendNotification(pConnection->GetNotificationClient(), pNotification); m_pDestinationFolder.reset(); m_pCurrentMessage.reset(); }
IMAPResult IMAPCopy::DoAction(shared_ptr<IMAPConnection> pConnection, int messageIndex, shared_ptr<Message> pOldMessage, const shared_ptr<IMAPCommandArgument> pArgument) { if (!pArgument || !pOldMessage) return IMAPResult(IMAPResult::ResultBad, "Invalid parameters"); shared_ptr<IMAPSimpleCommandParser> pParser = shared_ptr<IMAPSimpleCommandParser>(new IMAPSimpleCommandParser()); pParser->Parse(pArgument); if (pParser->WordCount() <= 0) return IMAPResult(IMAPResult::ResultNo, "The command requires parameters."); String sFolderName; if (pParser->Word(0)->Clammerized()) sFolderName = pArgument->Literal(0); else { sFolderName = pParser->Word(0)->Value(); IMAPFolder::UnescapeFolderString(sFolderName); } shared_ptr<IMAPFolder> pFolder = pConnection->GetFolderByFullPath(sFolderName); if (!pFolder) return IMAPResult(IMAPResult::ResultBad, "The folder could not be found."); shared_ptr<const Account> pAccount = pConnection->GetAccount(); if (!pFolder->IsPublicFolder()) { if (!pAccount->SpaceAvailable(pOldMessage->GetSize())) return IMAPResult(IMAPResult::ResultNo, "Your quota has been exceeded."); } // Check if the user has permission to copy to this destination folder if (!pConnection->CheckPermission(pFolder, ACLPermission::PermissionInsert)) return IMAPResult(IMAPResult::ResultBad, "ACL: Insert permission denied (Required for COPY command)."); shared_ptr<Message> pNewMessage = PersistentMessage::CopyToIMAPFolder(pAccount, pOldMessage, pFolder); if (!pNewMessage) return IMAPResult(IMAPResult::ResultBad, "Failed to copy message"); // Check if the user has access to set the Seen flag, otherwise if (!pConnection->CheckPermission(pFolder, ACLPermission::PermissionWriteSeen)) pNewMessage->SetFlagSeen(false); if (!PersistentMessage::SaveObject(pNewMessage)) return IMAPResult(IMAPResult::ResultBad, "Failed to save copy of message."); pFolder->SetFolderNeedsRefresh(); // Set a delayed notification so that the any IMAP idle client is notified when this // command has been finished. shared_ptr<ChangeNotification> pNotification = shared_ptr<ChangeNotification>(new ChangeNotification(pFolder->GetAccountID(), pFolder->GetID(), ChangeNotification::NotificationMessageAdded)); pConnection->SetDelayedChangeNotification(pNotification); return IMAPResult(); }
IMAPResult IMAPStore::DoAction(shared_ptr<IMAPConnection> pConnection, int messageIndex, shared_ptr<Message> pMessage, const shared_ptr<IMAPCommandArgument> pArgument) { if (!pMessage || !pArgument) return IMAPResult(IMAPResult::ResultBad, "Invalid parameters"); if (pConnection->GetCurrentFolderReadOnly()) { return IMAPResult(IMAPResult::ResultNo, "Store command on read-only folder."); } bool bSilent = false; String sCommand = pArgument->Command(); if (sCommand.FindNoCase(_T("FLAGS.SILENT")) >= 0) bSilent = true; else bSilent = false; // Read flags from command. bool bSeen = (sCommand.FindNoCase(_T("\\Seen")) >= 0); bool bDeleted = (sCommand.FindNoCase(_T("\\Deleted")) >= 0); bool bDraft = (sCommand.FindNoCase(_T("\\Draft")) >= 0); bool bAnswered = (sCommand.FindNoCase(_T("\\Answered")) >= 0); bool bFlagged = (sCommand.FindNoCase(_T("\\Flagged")) >= 0); if (bSeen) { // ACL: If user tries to change the Seen flag, check that he has permission to do so. if (!pConnection->CheckPermission(pConnection->GetCurrentFolder(), ACLPermission::PermissionWriteSeen)) return IMAPResult(IMAPResult::ResultNo, "ACL: WriteSeen permission denied (Required for STORE command)."); } if (bDeleted) { if (!pConnection->CheckPermission(pConnection->GetCurrentFolder(), ACLPermission::PermissionWriteDeleted)) return IMAPResult(IMAPResult::ResultNo, "ACL: DeleteMessages permission denied (Required for STORE command)."); } if (bDraft || bAnswered || bFlagged) { if (!pConnection->CheckPermission(pConnection->GetCurrentFolder(), ACLPermission::PermissionWriteOthers)) return IMAPResult(IMAPResult::ResultNo, "ACL: WriteOthers permission denied (Required for STORE command)."); } if (sCommand.FindNoCase(_T("-FLAGS")) >= 0) { // Remove flags if (bSeen) pMessage->SetFlagSeen(false); if (bDeleted) pMessage->SetFlagDeleted(false); if (bDraft) pMessage->SetFlagDraft(false); if (bAnswered) pMessage->SetFlagAnswered(false); if (bFlagged) pMessage->SetFlagFlagged(false); } else if (sCommand.FindNoCase(_T("+FLAGS")) >= 0) { // Add flags if (bSeen) pMessage->SetFlagSeen(true); if (bDeleted) pMessage->SetFlagDeleted(true); if (bDraft) pMessage->SetFlagDraft(true); if (bAnswered) pMessage->SetFlagAnswered(true); if (bFlagged) pMessage->SetFlagFlagged(true); } else if (sCommand.FindNoCase(_T("FLAGS")) >= 0) { // Set flags pMessage->SetFlagSeen(bSeen); pMessage->SetFlagDeleted(bDeleted); pMessage->SetFlagDraft(bDraft); pMessage->SetFlagAnswered(bAnswered); pMessage->SetFlagFlagged(bFlagged); } Application::Instance()->GetFolderManager()->UpdateMessageFlags( (int) pConnection->GetCurrentFolder()->GetAccountID(), (int) pConnection->GetCurrentFolder()->GetID(), pMessage->GetID(), pMessage->GetFlags()); if (!bSilent) { pConnection->SendAsciiData(GetMessageFlags(pMessage, messageIndex)); } // BEGIN IMAP IDLE // Notify the mailbox notifier that the mailbox contents have changed. std::vector<__int64> effectedMessages; effectedMessages.push_back(pMessage->GetID()); shared_ptr<ChangeNotification> pNotification = shared_ptr<ChangeNotification>(new ChangeNotification(pConnection->GetCurrentFolder()->GetAccountID(), pConnection->GetCurrentFolder()->GetID(), ChangeNotification::NotificationMessageFlagsChanged, effectedMessages)); Application::Instance()->GetNotificationServer()->SendNotification(pConnection->GetNotificationClient(), pNotification); // END IMAP IDLE return IMAPResult(); }