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();
   }
Beispiel #3
0
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();
   }