IMAPResult IMAPCommandCREATE::ConfirmPossibleToCreate(std::shared_ptr<HM::IMAPConnection> pConnection, const std::vector<String> &vecNewPath, bool bIsPublicFolder) { if (bIsPublicFolder) { std::shared_ptr<IMAPFolders> pFolders = pConnection->GetPublicFolders(); std::vector<String> vecTempPath = vecNewPath; vecTempPath.erase(vecTempPath.end()-1); std::shared_ptr<IMAPFolder> pParentFolder = IMAPFolderUtilities::GetTopMostExistingFolder(pFolders, vecTempPath); // Check if the user has permission to create a folder in the parent folder if (pParentFolder) { if (!pConnection->CheckPermission(pParentFolder, ACLPermission::PermissionCreate)) return IMAPResult(IMAPResult::ResultNo, "ACL: Create permission denied (Required for CREATE command)."); } // Check if the user is trying to create a new root public folder, such as Public folders/Test if (bIsPublicFolder && !pParentFolder) return IMAPResult(IMAPResult::ResultNo, "ACL: Root public folders can only be created using administration tools."); } return IMAPResult(); }
IMAPResult IMAPCommandSUBSCRIBE::ExecuteCommand(std::shared_ptr<HM::IMAPConnection> pConnection, std::shared_ptr<IMAPCommandArgument> pArgument) { if (!pConnection->IsAuthenticated()) return IMAPResult(IMAPResult::ResultNo, "Authenticate first"); std::shared_ptr<IMAPSimpleCommandParser> pParser = std::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(); } std::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(); }
TError TContainerHolder::GetLocked(TScopedLock &holder_lock, const std::shared_ptr<TClient> client, const std::string &name, const bool checkPerm, std::shared_ptr<TContainer> &c, TNestedScopedLock &l) { std::string absoluteName; TError error; // resolve name if (client) { error = client->ResolveRelativeName(name, absoluteName, !checkPerm); if (error) return error; } else { absoluteName = name; } // get container error = Get(absoluteName, c); if (error) return error; // lock container l = TNestedScopedLock(*c, holder_lock); // make sure it's still alive if (!c->IsValid()) return TError(EError::ContainerDoesNotExist, "container doesn't exist"); // check permissions if (client && checkPerm) { error = c->CheckPermission(client->GetCred()); if (error) return error; } return TError::Success(); }
IMAPResult IMAPCommandAppend::ExecuteCommand(std::shared_ptr<IMAPConnection> pConnection, std::shared_ptr<IMAPCommandArgument> pArgument) { if (!pConnection->IsAuthenticated()) return IMAPResult(IMAPResult::ResultNo, "Authenticate first"); current_tag_ = pArgument->Tag(); // Reset these two so we don't re-use old values. flags_to_set_ = ""; create_time_to_set_ = ""; std::shared_ptr<IMAPSimpleCommandParser> pParser = std::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()) flags_to_set_ = pParser->ParantheziedWord()->Value(); // last word. std::shared_ptr<IMAPSimpleWord> pWord = pParser->Word(pParser->WordCount()-1); if (!pWord || !pWord->Clammerized()) return IMAPResult(IMAPResult::ResultBad, "Missing literal"); AnsiString literalSize = pWord->Value(); bytes_left_to_receive_ = atoi(literalSize); if (bytes_left_to_receive_ == 0) return IMAPResult(IMAPResult::ResultBad, "Empty message not permitted."); // Add an extra two bytes since we expect a <newline> in the end. bytes_left_to_receive_ += 2; std::shared_ptr<const Domain> domain = CacheContainer::Instance()->GetDomain(pConnection->GetAccount()->GetDomainID()); int maxMessageSizeKB = GetMaxMessageSize_(domain); if (maxMessageSizeKB > 0 && bytes_left_to_receive_ / 1024 > maxMessageSizeKB) { String sMessage; sMessage.Format(_T("Message size exceeds fixed maximum message size. Size: %d KB, Max size: %d KB"), bytes_left_to_receive_ / 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++) { std::shared_ptr<IMAPSimpleWord> pWord = pParser->Word(i); if (pWord->Quoted()) { create_time_to_set_ = 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. create_time_to_set_.TrimLeft(); } } destination_folder_ = pConnection->GetFolderByFullPath(sFolderName); if (!destination_folder_) return IMAPResult(IMAPResult::ResultBad, "Folder could not be found."); if (!destination_folder_->IsPublicFolder()) { // Make sure that this message fits in the mailbox. std::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(bytes_left_to_receive_)) return IMAPResult(IMAPResult::ResultNo, "Your quota has been exceeded."); } if (!pConnection->CheckPermission(destination_folder_, ACLPermission::PermissionInsert)) return IMAPResult(IMAPResult::ResultBad, "ACL: Insert permission denied (Required for APPEND command)."); __int64 lFolderID = destination_folder_->GetID(); current_message_ = std::shared_ptr<Message>(new Message); current_message_->SetAccountID(destination_folder_->GetAccountID()); current_message_->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. std::shared_ptr<const Account> pMessageOwner; if (!destination_folder_->IsPublicFolder()) pMessageOwner = pConnection->GetAccount(); message_file_name_ = PersistentMessage::GetFileName(pMessageOwner, current_message_); String sResponse = "+ Ready for literal data\r\n"; pConnection->SetReceiveBinary(true); pConnection->SendAsciiData(sResponse); return IMAPResult(); }
void IMAPCommandAppend::Finish_(std::shared_ptr<IMAPConnection> pConnection) { if (!current_message_) return; // Add this message to the folder. current_message_->SetSize(FileUtilities::FileSize(message_file_name_)); current_message_->SetState(Message::Delivered); // Set message flags. bool bSeen = (flags_to_set_.FindNoCase(_T("\\Seen")) >= 0); bool bDeleted = (flags_to_set_.FindNoCase(_T("\\Deleted")) >= 0); bool bDraft = (flags_to_set_.FindNoCase(_T("\\Draft")) >= 0); bool bAnswered = (flags_to_set_.FindNoCase(_T("\\Answered")) >= 0); bool bFlagged = (flags_to_set_.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(destination_folder_, ACLPermission::PermissionWriteSeen)) { // User does not have permission to set the Seen flag. bSeen = false; } } current_message_->SetFlagDeleted(bDeleted); current_message_->SetFlagSeen(bSeen); current_message_->SetFlagDraft(bDraft); current_message_->SetFlagAnswered(bAnswered); current_message_->SetFlagFlagged(bFlagged); current_message_->SetFlagRecent(true); // Set the create time if (!create_time_to_set_.IsEmpty()) { // Convert to internal format create_time_to_set_ = Time::GetInternalDateFromIMAPInternalDate(create_time_to_set_); current_message_->SetCreateTime(create_time_to_set_); } PersistentMessage::SaveObject(current_message_); destination_folder_->SetFolderNeedsRefresh(); String sResponse; if (pConnection->GetCurrentFolder() && pConnection->GetCurrentFolder()->GetID() == destination_folder_->GetID()) { std::shared_ptr<Messages> messages = destination_folder_->GetMessages(); sResponse += IMAPNotificationClient::GenerateExistsString(messages->GetCount()); sResponse += IMAPNotificationClient::GenerateRecentString(messages->GetNoOfRecent()); } // Send the OK response to the client. sResponse += current_tag_ + " OK APPEND completed\r\n"; pConnection->SendAsciiData(sResponse); // Notify the mailbox notifier that the mailbox contents have changed. std::shared_ptr<ChangeNotification> pNotification = std::shared_ptr<ChangeNotification>(new ChangeNotification(destination_folder_->GetAccountID(), destination_folder_->GetID(), ChangeNotification::NotificationMessageAdded)); Application::Instance()->GetNotificationServer()->SendNotification(pConnection->GetNotificationClient(), pNotification); destination_folder_.reset(); current_message_.reset(); }
IMAPResult IMAPCommandEXPUNGE::ExecuteCommand(std::shared_ptr<IMAPConnection> pConnection, std::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. std::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<__int64> expunged_messages_uid; std::vector<__int64> expunged_messages_index; std::function<bool(int, std::shared_ptr<Message>)> filter = [&expunged_messages_index, &expunged_messages_uid](int index, std::shared_ptr<Message> message) { if (message->GetFlagDeleted()) { expunged_messages_index.push_back(index); expunged_messages_uid.push_back(message->GetID()); return true; } return false; }; auto messages = MessagesContainer::Instance()->GetMessages(pCurFolder->GetAccountID(), pCurFolder->GetID()); messages->DeleteMessages(filter); auto iterExpunged = expunged_messages_index.begin(); String sResponse; while (iterExpunged != expunged_messages_index.end()) { String sTemp; sTemp.Format(_T("* %d EXPUNGE\r\n"), (*iterExpunged)); sResponse += sTemp; iterExpunged++; } pConnection->SendAsciiData(sResponse); if (!expunged_messages_uid.empty()) { auto recent_messages = pConnection->GetRecentMessages(); for (__int64 messageUid : expunged_messages_uid) { auto recent_messages_it = recent_messages.find(messageUid); if (recent_messages_it != recent_messages.end()) recent_messages.erase(recent_messages_it); } // Messages have been expunged // Notify the mailbox notifier that the mailbox contents have changed. std::shared_ptr<ChangeNotification> pNotification = std::shared_ptr<ChangeNotification>(new ChangeNotification(pCurFolder->GetAccountID(), pCurFolder->GetID(), ChangeNotification::NotificationMessageDeleted, expunged_messages_index)); Application::Instance()->GetNotificationServer()->SendNotification(pConnection->GetNotificationClient(), pNotification); } // We're done. sResponse = pArgument->Tag() + " OK EXPUNGE Completed\r\n"; pConnection->SendAsciiData(sResponse); return IMAPResult(); }
IMAPResult IMAPStore::DoAction(std::shared_ptr<IMAPConnection> pConnection, int messageIndex, std::shared_ptr<Message> pMessage, const std::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); } bool result = Application::Instance()->GetFolderManager()->UpdateMessageFlags( (int) pConnection->GetCurrentFolder()->GetAccountID(), (int) pConnection->GetCurrentFolder()->GetID(), pMessage->GetID(), pMessage->GetFlags()); if (!result) { return IMAPResult(IMAPResult::ResultNo, "Unable to store message flags."); } 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()); std::shared_ptr<ChangeNotification> pNotification = std::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(); }