M changelog.md => changelog.md +1 -0
@@ 14,6 14,7 @@
* `[bluetooth][settings]` Add Bluetooth settings to database.
* `[PowerManagement]` Added CPU load measurement.
* `[alarms]` Added new/edit alarm window
+* `[file indexer]` Support for deleting entries in File Indexer DB.
### Changed
M module-services/service-db/agents/file_indexer/FileIndexerAgent.cpp => module-services/service-db/agents/file_indexer/FileIndexerAgent.cpp +112 -15
@@ 7,6 7,7 @@
#include <log/log.hpp>
#include <module-sys/Service/Bus.hpp>
#include <purefs/filesystem_paths.hpp>
+#include <Application.hpp>
#include <memory>
FileIndexerAgent::FileIndexerAgent(sys::Service *parentService) : DatabaseAgent(parentService)
@@ 62,6 63,11 @@ void FileIndexerAgent::registerMessages()
parentService->connect(FileIndexer::Messages::SetPropertiesMessage(),
std::bind(&FileIndexerAgent::handleSetProperties, this, _1));
+ parentService->connect(FileIndexer::Messages::DeleteFileMessage(),
+ std::bind(&FileIndexerAgent::handleDeleteFile, this, _1));
+ parentService->connect(FileIndexer::Messages::DeleteAllFilesInDirMessage(),
+ std::bind(&FileIndexerAgent::handleDeleteAllFilesInDir, this, _1));
+
parentService->connect(FileIndexer::Messages::RegisterOnFileChange(),
std::bind(&FileIndexerAgent::handleRegisterOnFileChange, this, _1));
parentService->connect(FileIndexer::Messages::UnregisterOnFileChange(),
@@ 146,7 152,16 @@ auto FileIndexerAgent::dbGetFilesCount() -> unsigned int
if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
return 0;
}
- return (*retQuery)[0].getInt32();
+ return (*retQuery)[0].getUInt32();
+}
+
+auto FileIndexerAgent::dbGeMetadataCount() -> unsigned int
+{
+ auto retQuery = database->query(FileIndexer::Statements::getMetadataCount);
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
+ return 0;
+ }
+ return (*retQuery)[0].getUInt32();
}
auto FileIndexerAgent::dbListDir(std::unique_ptr<FileIndexer::ListDirData> listDir)
@@ 247,6 262,45 @@ auto FileIndexerAgent::dbSetRecord(std::unique_ptr<FileIndexer::FileRecord> reco
retRecord.file_id);
}
+auto FileIndexerAgent::dbDeleteFile(std::unique_ptr<FileIndexer::FileRecord> record) -> bool
+{
+ unsigned int file_id = FileIndexer::FILE_ID_NOT_EXISTS;
+ if (false == record->path.empty()) {
+ auto retQuery = database->query(FileIndexer::Statements::getFileIdByPath, record->path.c_str());
+ if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
+ return false;
+ }
+ else {
+ file_id = (*retQuery)[0].getUInt32();
+ }
+ }
+ else {
+ file_id = record->file_id;
+ }
+ return database->execute(FileIndexer::Statements::deleteFileById, file_id, file_id);
+}
+
+auto FileIndexerAgent::dbDeleteAllFilesInDir(std::unique_ptr<FileIndexer::FileRecord> record) -> bool
+{
+ bool retStatus = false;
+ if (false == record->directory.empty()) {
+ auto retQuery = database->query(FileIndexer::Statements::getFilesIdByDir, record->directory.c_str());
+ if (nullptr == retQuery || FileIndexer::ZERO_ROWS_FOUND == retQuery->getRowCount()) {
+ return false;
+ }
+ else {
+ do {
+ unsigned int file_id = (*retQuery)[0].getUInt32();
+ retStatus = database->execute(FileIndexer::Statements::deleteFileById, file_id, file_id);
+ } while (retQuery->nextRow() && retStatus);
+ return retStatus;
+ }
+ }
+ else {
+ return false;
+ }
+}
+
auto FileIndexerAgent::dbUpdateRecord(std::unique_ptr<FileIndexer::FileRecord> record) -> bool
{
return database->execute(FileIndexer::Statements::updateFileInfo,
@@ 267,7 321,7 @@ auto FileIndexerAgent::handleGetRecord(sys::Message *req) -> sys::MessagePointer
return std::make_shared<FileIndexer::Messages::GetRecordResponseMessage>(
std::make_unique<FileIndexer::FileRecord>(msg->dbRecord));
}
- return std::make_shared<sys::ResponseMessage>();
+ return app::msgHandled();
}
auto FileIndexerAgent::handleSetRecord(sys::Message *req) -> sys::MessagePointer
@@ 285,13 339,56 @@ auto FileIndexerAgent::handleSetRecord(sys::Message *req) -> sys::MessagePointer
dbSetRecord(std::make_unique<FileIndexer::FileRecord>(record));
for (auto recipient : fileChangeRecipents[record.directory]) {
- auto notifeMsg = std::make_shared<FileIndexer::Messages::FileChanged>(
+ auto notifyMsg = std::make_shared<FileIndexer::Messages::DirectoryContentChangedMessage>(
std::make_unique<std::string>(record.directory));
- sys::Bus::SendUnicast(std::move(notifeMsg), recipient, parentService);
+ sys::Bus::SendUnicast(std::move(notifyMsg), recipient, parentService);
}
}
}
- return std::make_shared<sys::ResponseMessage>();
+ return app::msgHandled();
+}
+
+auto FileIndexerAgent::handleDeleteFile(sys::Message *req) -> sys::MessagePointer
+{
+ if (auto msg = dynamic_cast<FileIndexer::Messages::DeleteFileMessage *>(req)) {
+ auto recordPtr = std::move(msg->record);
+ FileIndexer::FileRecord record = *recordPtr;
+ msg->dbRecord = dbGetRecord(std::make_unique<FileIndexer::FileRecord>(record));
+
+ auto deleteMsg = std::make_shared<FileIndexer::Messages::FileDeletedMessage>(std::move(recordPtr));
+
+ sys::Bus::SendUnicast(std::move(deleteMsg), msg->sender, parentService);
+ dbDeleteFile(std::make_unique<FileIndexer::FileRecord>(record));
+
+ for (auto recipient : fileChangeRecipents[record.directory]) {
+ auto notifyMsg = std::make_shared<FileIndexer::Messages::DirectoryContentChangedMessage>(
+ std::make_unique<std::string>(record.directory));
+ sys::Bus::SendUnicast(std::move(notifyMsg), recipient, parentService);
+ }
+ }
+ return app::msgHandled();
+}
+
+auto FileIndexerAgent::handleDeleteAllFilesInDir(sys::Message *req) -> sys::MessagePointer
+{
+ if (auto msg = dynamic_cast<FileIndexer::Messages::DeleteAllFilesInDirMessage *>(req)) {
+ auto recordPtr = std::move(msg->record);
+ FileIndexer::FileRecord record = *recordPtr;
+ msg->dbRecord = dbGetRecord(std::make_unique<FileIndexer::FileRecord>(record));
+
+ auto deleteMsg =
+ std::make_shared<FileIndexer::Messages::AllFilesInDirDeletedDeletedMessage>(std::move(recordPtr));
+
+ sys::Bus::SendUnicast(std::move(deleteMsg), msg->sender, parentService);
+ dbDeleteAllFilesInDir(std::make_unique<FileIndexer::FileRecord>(record));
+
+ for (auto recipient : fileChangeRecipents[record.directory]) {
+ auto notifyMsg = std::make_shared<FileIndexer::Messages::DirectoryContentChangedMessage>(
+ std::make_unique<std::string>(record.directory));
+ sys::Bus::SendUnicast(std::move(notifyMsg), recipient, parentService);
+ }
+ }
+ return app::msgHandled();
}
auto FileIndexerAgent::dbGetProperty(std::unique_ptr<FileIndexer::FileMetadata> metaData) -> FileIndexer::FileMetadata
@@ 306,7 403,7 @@ auto FileIndexerAgent::dbGetProperty(std::unique_ptr<FileIndexer::FileMetadata>
return *metaData;
}
FileIndexer::FileMetadata retMetaData;
- retMetaData.file_id = (*retQuery)[0].getInt32();
+ retMetaData.file_id = (*retQuery)[0].getUInt32();
retMetaData.properties.clear();
retMetaData.properties.emplace((*retQuery)[1].getString(), (*retQuery)[2].getString());
return retMetaData;
@@ 323,7 420,7 @@ auto FileIndexerAgent::dbGetAllProperties(std::unique_ptr<FileIndexer::FileMetad
return retMetaData;
}
if (retQuery->getRowCount() > 0) {
- retMetaData.file_id = (*retQuery)[0].getInt32();
+ retMetaData.file_id = (*retQuery)[0].getUInt32();
retMetaData.properties.clear();
do {
retMetaData.properties.emplace((*retQuery)[1].getString(), (*retQuery)[2].getString());
@@ 342,7 439,7 @@ auto FileIndexerAgent::dbSetProperty(std::unique_ptr<FileIndexer::FileMetadata>
if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
return false;
}
- fileId = (*retQuery)[0].getInt32();
+ fileId = (*retQuery)[0].getUInt32();
}
else if (metaData->file_id != FileIndexer::FILE_ID_NOT_EXISTS) {
retQuery = database->query(FileIndexer::Statements::getFileInfoById, metaData->file_id);
@@ 373,7 470,7 @@ auto FileIndexerAgent::dbSetProperties(std::unique_ptr<FileIndexer::FileMetadata
if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
return false;
}
- fileId = (*retQuery)[0].getInt32();
+ fileId = (*retQuery)[0].getUInt32();
}
else if (metaData->file_id != FileIndexer::FILE_ID_NOT_EXISTS) {
retQuery = database->query(FileIndexer::Statements::getFileInfoById, metaData->file_id);
@@ 419,7 516,7 @@ auto FileIndexerAgent::handleGetProperty(sys::Message *req) -> sys::MessagePoint
return std::make_shared<FileIndexer::Messages::GetPropertyResponseMessage>(
std::make_unique<FileIndexer::FileMetadata>(msg->dbMetaData));
}
- return std::make_shared<sys::ResponseMessage>();
+ return app::msgHandled();
}
auto FileIndexerAgent::handleGetAllProperties(sys::Message *req) -> sys::MessagePointer
@@ 431,7 528,7 @@ auto FileIndexerAgent::handleGetAllProperties(sys::Message *req) -> sys::Message
return std::make_shared<FileIndexer::Messages::GetPropertyResponseMessage>(
std::make_unique<FileIndexer::FileMetadata>(msg->dbMetaData));
}
- return std::make_shared<sys::ResponseMessage>();
+ return app::msgHandled();
}
auto FileIndexerAgent::handleSetProperty(sys::Message *req) -> sys::MessagePointer
@@ 454,7 551,7 @@ auto FileIndexerAgent::handleSetProperty(sys::Message *req) -> sys::MessagePoint
dbSetProperty(std::make_unique<FileIndexer::FileMetadata>(metaData));
}
}
- return std::make_shared<sys::ResponseMessage>();
+ return app::msgHandled();
}
auto FileIndexerAgent::handleSetProperties(sys::Message *req) -> sys::MessagePointer
@@ 469,10 566,10 @@ auto FileIndexerAgent::handleSetProperties(sys::Message *req) -> sys::MessagePoi
dbSetProperties(std::make_unique<FileIndexer::FileMetadata>(metaData));
for (auto recipient : fileChangeRecipents[metaData.directory]) {
- auto notifyMsg =
- std::make_shared<FileIndexer::Messages::FileChanged>(std::make_unique<std::string>(metaData.directory));
+ auto notifyMsg = std::make_shared<FileIndexer::Messages::DirectoryContentChangedMessage>(
+ std::make_unique<std::string>(metaData.directory));
sys::Bus::SendUnicast(std::move(notifyMsg), recipient, parentService);
}
}
- return std::make_shared<sys::ResponseMessage>();
+ return app::msgHandled();
}
M module-services/service-db/agents/file_indexer/FileIndexerAgent.hpp => module-services/service-db/agents/file_indexer/FileIndexerAgent.hpp +5 -0
@@ 28,6 28,8 @@ class FileIndexerAgent : public DatabaseAgent
auto handleSetProperties(sys::Message *req) -> sys::MessagePointer;
auto handleGetRecord(sys::Message *req) -> sys::MessagePointer;
auto handleSetRecord(sys::Message *req) -> sys::MessagePointer;
+ auto handleDeleteFile(sys::Message *req) -> sys::MessagePointer;
+ auto handleDeleteAllFilesInDir(sys::Message *req) -> sys::MessagePointer;
using MapOfRecipentsToBeNotified = std::map<std::string, std::set<std::string>>;
MapOfRecipentsToBeNotified fileChangeRecipents;
@@ 44,8 46,11 @@ class FileIndexerAgent : public DatabaseAgent
auto dbGetAllProperties(std::unique_ptr<FileIndexer::FileMetadata> metaData) -> FileIndexer::FileMetadata;
auto dbGetRecord(std::unique_ptr<FileIndexer::FileRecord> record) -> FileIndexer::FileRecord;
auto dbSetRecord(std::unique_ptr<FileIndexer::FileRecord> record) -> bool;
+ auto dbDeleteFile(std::unique_ptr<FileIndexer::FileRecord> record) -> bool;
+ auto dbDeleteAllFilesInDir(std::unique_ptr<FileIndexer::FileRecord> record) -> bool;
auto dbUpdateRecord(std::unique_ptr<FileIndexer::FileRecord> record) -> bool;
auto getDbInitString() -> const std::string override;
auto getDbFilePath() -> const std::string override;
auto dbGetFilesCount() -> unsigned int;
+ auto dbGeMetadataCount() -> unsigned int;
};
M module-services/service-db/agents/file_indexer/FileIndexer_queries.hpp => module-services/service-db/agents/file_indexer/FileIndexer_queries.hpp +23 -1
@@ 10,6 10,10 @@ namespace FileIndexer::Statements
SELECT COUNT(FT.file_id) AS FILE_PATH_EXISTS FROM file_tab AS FT;
)sql";
+ constexpr auto getMetadataCount = R"sql(
+ SELECT COUNT(MT.file_id) AS FILE_PATH_EXISTS FROM metadata_tab AS MT;
+ )sql";
+
constexpr auto checkFileExists = R"sql(
SELECT COUNT(size) AS FILE_PATH_EXISTS FROM file_tab AS FT
WHERE FT.path = '%q'
@@ 17,7 21,7 @@ namespace FileIndexer::Statements
)sql";
constexpr auto getFileIdByPath = R"sql(
- SELECT FT.file_id file_tab AS FT
+ SELECT FT.file_id FROM file_tab AS FT
WHERE FT.path = '%q'
COLLATE NOCASE;
)sql";
@@ 61,6 65,24 @@ namespace FileIndexer::Statements
WHERE file_id = '%lu' ;
)sql";
+ constexpr auto deleteFileById = R"sql(
+ DELETE FROM file_tab AS FT
+ WHERE FT.file_id = '%lu';
+ DELETE FROM metadata_tab AS MT
+ WHERE MT.file_id = '%lu';
+ )sql";
+
+ constexpr auto deleteAllFileInfoInDir = R"sql(
+ DELETE FROM file_tab AS FT
+ WHERE FT.directory = '%q';
+ )sql";
+
+ constexpr auto getFilesIdByDir = R"sql(
+ SELECT file_id FROM file_tab AS FT
+ WHERE FT.directory = '%q'
+ COLLATE NOCASE;
+ )sql";
+
constexpr auto getPropertyValue = R"sql(
SELECT MT.file_id, MT.property, MT.value
FROM metadata_tab AS MT, file_tab AS FT
M module-services/service-db/service-db/FileIndexerMessages.hpp => module-services/service-db/service-db/FileIndexerMessages.hpp +44 -3
@@ 109,11 109,11 @@ namespace FileIndexer
std::unique_ptr<std::string> directory;
};
- class FileChanged : public FileIndexerMessage
+ class DirectoryContentChangedMessage : public FileIndexerMessage
{
public:
- FileChanged() = default;
- explicit FileChanged(std::unique_ptr<std::string> dir) : directory(std::move(dir))
+ DirectoryContentChangedMessage() = default;
+ explicit DirectoryContentChangedMessage(std::unique_ptr<std::string> dir) : directory(std::move(dir))
{}
std::unique_ptr<std::string> directory;
@@ 194,6 194,47 @@ namespace FileIndexer
std::unique_ptr<FileRecord> record;
};
+ class DeleteRecordMessage : public RecordMessage
+ {
+ public:
+ DeleteRecordMessage() = default;
+ DeleteRecordMessage(std::unique_ptr<FileRecord> record) : RecordMessage(std::move(record))
+ {}
+ };
+
+ class DeleteFileMessage : public RecordMessage
+ {
+ public:
+ DeleteFileMessage() = default;
+ DeleteFileMessage(std::unique_ptr<FileRecord> record) : RecordMessage(std::move(record))
+ {}
+ };
+
+ class DeleteAllFilesInDirMessage : public RecordMessage
+ {
+ public:
+ DeleteAllFilesInDirMessage() = default;
+ DeleteAllFilesInDirMessage(std::unique_ptr<FileRecord> record) : RecordMessage(std::move(record))
+ {}
+ };
+
+ class FileDeletedMessage : public RecordMessage
+ {
+ public:
+ FileDeletedMessage() = default;
+ explicit FileDeletedMessage(std::unique_ptr<FileRecord> record) : RecordMessage(std::move(record))
+ {}
+ };
+
+ class AllFilesInDirDeletedDeletedMessage : public RecordMessage
+ {
+ public:
+ AllFilesInDirDeletedDeletedMessage() = default;
+ explicit AllFilesInDirDeletedDeletedMessage(std::unique_ptr<FileRecord> record)
+ : RecordMessage(std::move(record))
+ {}
+ };
+
/// Property manipulation
class PropertyMessage : public FileIndexerMessage
{
M module-services/service-db/test/CMakeLists.txt => module-services/service-db/test/CMakeLists.txt +1 -0
@@ 14,5 14,6 @@ add_catch2_executable(
module-audio
service-cellular
module-cellular
+ iosyscalls
)
M module-services/service-db/test/test-service-db-file_indexer.cpp => module-services/service-db/test/test-service-db-file_indexer.cpp +89 -0
@@ 223,4 223,93 @@ TEST_CASE("FileIndexer")
}
REQUIRE(newPropertyValueFound == newPropertyValue);
}
+
+ SECTION("DeleteFile /DeleteAllFilesInDir")
+ {
+ FileIndexer::ListDirData inputData{};
+ FileIndexer::FileRecord record{};
+ std::cout << "DeleteFile /DeleteAllFilesInDir" << std::endl << std::flush;
+
+ // SetRecord - add files
+ auto recordPtr = std::make_unique<FileIndexer::FileRecord>(record);
+ std::unique_ptr<FileIndexerTest> fiAgentTest{new FileIndexerTest(&file_indexer_client_service)};
+ record.directory = "wav";
+ record.path = "wav/file1.wav";
+
+ recordPtr = std::make_unique<FileIndexer::FileRecord>(record);
+ auto setMsg = std::make_shared<FileIndexer::Messages::SetRecordMessage>(std::move(recordPtr));
+ setMsg->sender = serviceName;
+ sys::MessagePointer recMsg = fiAgentTest->handleSetRecordTest(setMsg.get());
+
+ record.path = "wav/file2.wav";
+ recordPtr = std::make_unique<FileIndexer::FileRecord>(record);
+ setMsg = std::make_shared<FileIndexer::Messages::SetRecordMessage>(std::move(recordPtr));
+ setMsg->sender = serviceName;
+ recMsg = fiAgentTest->handleSetRecordTest(setMsg.get());
+
+ record.path = "wav/file3.wav";
+ recordPtr = std::make_unique<FileIndexer::FileRecord>(record);
+ setMsg = std::make_shared<FileIndexer::Messages::SetRecordMessage>(std::move(recordPtr));
+ setMsg->sender = serviceName;
+ recMsg = fiAgentTest->handleSetRecordTest(setMsg.get());
+
+ std::cout << "Get listDir" << std::endl << std::flush;
+ inputData.directory = "wav";
+ inputData.list_offset = 0;
+ inputData.list_limit = 10;
+ auto inputDataPtr = std::make_unique<FileIndexer::ListDirData>(inputData);
+ auto getListDirMsg = std::make_shared<FileIndexer::Messages::GetListDirMessage>(std::move(inputDataPtr));
+ recMsg = fiAgentTest->handleListDirTest(getListDirMsg.get());
+ auto msg = dynamic_cast<FileIndexer::Messages::GetListDirResponseMessage *>(recMsg.get());
+ auto fileCount = msg->getCount();
+ auto fileList = msg->getResults();
+ LOG_INFO("Total number of files in %s = %d", inputData.directory.c_str(), fileCount);
+ LOG_INFO("Limit= %d Offset= %d", inputData.list_limit, inputData.list_offset);
+ for (auto fileRec : fileList) {
+ LOG_INFO("file id= %d file path= %s size = %d mime= %d mtime = %d directory= %s ftype= %d",
+ fileRec.file_id,
+ fileRec.path.c_str(),
+ fileRec.size,
+ fileRec.mime_type,
+ fileRec.mtime,
+ fileRec.directory.c_str(),
+ fileRec.file_type);
+ }
+
+ std::cout << "DeleteFile" << std::endl << std::flush;
+ record.path = "wav/file1.wav";
+ record.directory = "wav";
+ recordPtr = std::make_unique<FileIndexer::FileRecord>(record);
+ auto delMsg = std::make_shared<FileIndexer::Messages::DeleteFileMessage>(std::move(recordPtr));
+ delMsg->sender = serviceName;
+ auto delRecMsg = fiAgentTest->handleDeleteFileTest(delMsg.get());
+
+ inputData.directory = "wav";
+ inputData.list_offset = 0;
+ inputData.list_limit = 10;
+ inputDataPtr = std::make_unique<FileIndexer::ListDirData>(inputData);
+ getListDirMsg = std::make_shared<FileIndexer::Messages::GetListDirMessage>(std::move(inputDataPtr));
+ recMsg = fiAgentTest->handleListDirTest(getListDirMsg.get());
+ msg = dynamic_cast<FileIndexer::Messages::GetListDirResponseMessage *>(recMsg.get());
+ auto fileCountAfterDel = msg->getCount();
+ REQUIRE(fileCountAfterDel == fileCount - 1);
+
+ std::cout << "DeleteAllFilesInDir" << std::endl << std::flush;
+ record.directory = "wav";
+ recordPtr = std::make_unique<FileIndexer::FileRecord>(record);
+ auto delAllMsg = std::make_shared<FileIndexer::Messages::DeleteAllFilesInDirMessage>(std::move(recordPtr));
+ delAllMsg->sender = serviceName;
+ delRecMsg = fiAgentTest->handleDeleteAllFilesInDirTest(delAllMsg.get());
+
+ inputData.directory = "wav";
+ inputData.list_offset = 0;
+ inputData.list_limit = 10;
+ inputDataPtr = std::make_unique<FileIndexer::ListDirData>(inputData);
+ getListDirMsg = std::make_shared<FileIndexer::Messages::GetListDirMessage>(std::move(inputDataPtr));
+ recMsg = fiAgentTest->handleListDirTest(getListDirMsg.get());
+ msg = dynamic_cast<FileIndexer::Messages::GetListDirResponseMessage *>(recMsg.get());
+ fileCountAfterDel = msg->getCount();
+ REQUIRE(fileCountAfterDel == 0);
+ std::cout << "DeleteFile /DeleteAllFilesInDir finished" << std::endl << std::flush;
+ }
}
M module-services/service-db/test/test-service-db-file_indexer.hpp => module-services/service-db/test/test-service-db-file_indexer.hpp +10 -0
@@ 48,6 48,16 @@ class FileIndexerTest : public FileIndexerAgent
return FileIndexerAgent::handleUnregisterOnFileChange(req);
}
+ auto handleDeleteFileTest(FileIndexer::Messages::DeleteFileMessage *req) -> sys::MessagePointer
+ {
+ return FileIndexerAgent::handleDeleteFile(req);
+ }
+
+ auto handleDeleteAllFilesInDirTest(FileIndexer::Messages::DeleteAllFilesInDirMessage *req) -> sys::MessagePointer
+ {
+ return FileIndexerAgent::handleDeleteAllFilesInDir(req);
+ }
+
unsigned int getNumberOfRecipients(std::string directory)
{
unsigned int recipientCount = 0;