diff --git a/src/agents/store/search.c b/src/agents/store/search.c
index a307b08..55ad6df 100644
--- a/src/agents/store/search.c
+++ b/src/agents/store/search.c
@@ -62,14 +62,13 @@ SearchDocument(StoreClient *client, StoreObject *document,
ccode = ConnWriteStr(client->conn, MSG4224CANTREAD);
break;
}
- /* FIXME
- if (BongoStreamSearchRfc822Header(ReadSourceFile, (void *)f, info->data.mail.headerSize,
+ if (BongoStreamSearchRfc822Header(ReadSourceFile, (void *)f,
+ document->size,
NULL, query->query))
{
- ccode = ReportHit(client->conn, info);
+ ccode = ReportHit(client->conn, document);
break;
}
- */
if (query->type == STORE_SEARCH_HEADERS) {
break;
}
@@ -132,33 +131,16 @@ search_body:
}
case STORE_SEARCH_HEADER: {
- /* 'subject' and 'from' fields are special cases because they are already in utf-8 in the info structure */
- /* FIXME
- if (info->data.mail.subject && XplStrCaseCmp(query->header, "subject") == 0) {
- if (BongoStrCaseStr(info->data.mail.subject, query->query)) {
- ccode = ReportHit(client->conn, info);
- }
- break;
- }
-
- if (XplStrCaseCmp(query->header, "from") == 0) {
-
- if (info->data.mail.from && BongoStrCaseStr(info->data.mail.from, query->query)) {
- ccode = ReportHit(client->conn, info);
- }
- break;
- }
-
if (XplFSeek64(f, 0, SEEK_SET)) {
ccode = ConnWriteStr(client->conn, MSG4224CANTREAD);
break;
}
- if (BongoStreamSearchRfc822Header(ReadSourceFile, (void *)f, info->data.mail.headerSize,
+ if (BongoStreamSearchRfc822Header(ReadSourceFile, (void *)f,
+ document->size,
query->header, query->query))
{
- ccode = ReportHit(client->conn, info);
+ ccode = ReportHit(client->conn, document);
}
- */
break;
}
@@ -167,6 +149,52 @@ search_body:
return(ccode);
}
+static int
+LoadCollectionDocuments(StoreClient *client, uint64_t collectionGuid,
+ GArray *documents)
+{
+ static const char query[] =
+ "SELECT guid, collection_guid, imap_uid, filename, type, flags, size, "
+ "time_created, time_modified FROM storeobject "
+ "WHERE collection_guid=?1 ORDER BY imap_uid, guid;";
+ MsgSQLStatement statement;
+ MsgSQLStatement *prepared;
+ int status;
+
+ memset(&statement, 0, sizeof(statement));
+ if (MsgSQLBeginTransaction(client->storedb)) return -1;
+ prepared = MsgSQLPrepare(client->storedb, query, &statement);
+ if (!prepared) goto abort;
+ MsgSQLBindInt64(prepared, 1, collectionGuid);
+ while ((status = MsgSQLResults(client->storedb, prepared)) > 0) {
+ StoreObject document;
+
+ memset(&document, 0, sizeof(document));
+ document.guid = MsgSQLResultInt64(prepared, 0);
+ document.collection_guid = MsgSQLResultInt64(prepared, 1);
+ document.imap_uid = MsgSQLResultInt(prepared, 2);
+ MsgSQLResultText(prepared, 3, document.filename, MAX_FILE_NAME);
+ document.filename[MAX_FILE_NAME] = '\0';
+ document.type = MsgSQLResultInt(prepared, 4);
+ document.flags = MsgSQLResultInt(prepared, 5);
+ document.size = MsgSQLResultInt64(prepared, 6);
+ document.time_created = MsgSQLResultInt(prepared, 7);
+ document.time_modified = MsgSQLResultInt(prepared, 8);
+ document.version = 1;
+ if (!STORE_IS_FOLDER(document.type))
+ g_array_append_val(documents, document);
+ }
+ if (status < 0) goto abort;
+ MsgSQLFinalize(&statement);
+ if (MsgSQLCommitTransaction(client->storedb)) return -1;
+ return 0;
+
+abort:
+ MsgSQLFinalize(&statement);
+ MsgSQLAbortTransaction(client->storedb);
+ return -1;
+}
+
CCode
@@ -209,39 +237,32 @@ StoreCommandSEARCH(StoreClient *client, uint64_t guid, StoreSearchInfo *query)
}
// we're searching a collection - so search each document in it
- /* FIXME
- stmt = DStoreListColl(client->handle, guid, -1, -1);
- if (!stmt) {
+ GArray *documents = g_array_new(FALSE, FALSE, sizeof(StoreObject));
+ if (!documents || LoadCollectionDocuments(client, guid, documents) != 0) {
+ if (documents) g_array_free(documents, TRUE);
return ConnWriteStr(client->conn, MSG5005DBLIBERR);
- goto finish;
}
-
- while (-1 != ccode) {
- switch (DStoreInfoStmtStep(client->handle, stmt, &child)) {
- case 0:
- ccode = ConnWriteStr(client->conn, MSG1000OK);
- goto finish;
- case -1:
- ccode = ConnWriteStr(client->conn, MSG5005DBLIBERR);
- goto finish;
- case 1:
- if (!STORE_IS_FOLDER(child.type)) {
- FindPathToDocument(client, child.collection, child.guid, path, sizeof(path));
- f = fopen(path, "rb");
- if (!f) {
- ccode = ConnWriteStr(client->conn, MSG4224CANTREAD);
- goto finish;
- }
- ccode = SearchDocument(client, &child, query, f);
- fclose(f);
- }
- break;
+ for (guint index = 0; index < documents->len; index++) {
+ StoreObject *child = &g_array_index(documents, StoreObject, index);
+
+ if (StoreObjectCheckAuthorization(client, child, STORE_PRIV_READ))
+ continue;
+ FindPathToDocument(client, child->collection_guid, child->guid, path,
+ sizeof(path));
+ f = fopen(path, "rb");
+ if (!f) {
+ g_array_free(documents, TRUE);
+ return ConnWriteStr(client->conn, MSG4224CANTREAD);
+ }
+ ccode = SearchDocument(client, child, query, f);
+ fclose(f);
+ if (ccode == -1) {
+ g_array_free(documents, TRUE);
+ return -1;
}
}
- */
+ g_array_free(documents, TRUE);
ccode = ConnWriteStr(client->conn, MSG1000OK);
-
-//finish:
-
+
return ccode;
}
diff --git a/src/libs/streamio/CMakeLists.txt b/src/libs/streamio/CMakeLists.txt
index a3ac0eb..2529d96 100644
--- a/src/libs/streamio/CMakeLists.txt
+++ b/src/libs/streamio/CMakeLists.txt
@@ -4,3 +4,20 @@ add_library(bongostreamio SHARED
bongostream.c)
install(TARGETS bongostreamio DESTINATION ${LIB_INSTALL_DIR})
+
+if(BUILD_TESTING)
+ add_executable(streamio-search-test tests/search-test.c)
+ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+ target_link_options(streamio-search-test PRIVATE "LINKER:--no-as-needed")
+ endif()
+ target_link_libraries(streamio-search-test PRIVATE
+ bongostreamio
+ bongoxpl
+ bongoconnio
+ bongoutil
+ bongojson
+ bongomsgapi
+ GnuTLS::GnuTLS
+ Threads::Threads)
+ add_test(NAME streamio-search COMMAND streamio-search-test)
+endif()
diff --git a/src/libs/streamio/tests/search-test.c b/src/libs/streamio/tests/search-test.c
new file mode 100644
index 0000000..2faa6b3
--- /dev/null
+++ b/src/libs/streamio/tests/search-test.c
@@ -0,0 +1,73 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2006 Novell, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact Novell, Inc.
+ *
+ * To contact Novell about this file by physical or electronic mail, you
+ * may find current contact information at www.novell.com.
+ *
+ ****************************************************************************/
+
+#include
+#include
+
+#include
+#include
+#include
+
+typedef struct {
+ const char *data;
+ size_t length;
+ size_t offset;
+} MemorySource;
+
+static long
+ReadMemory(void *opaque, char *buffer, unsigned long maximum)
+{
+ MemorySource *source = opaque;
+ size_t remaining = source->length - source->offset;
+ size_t count = remaining < maximum ? remaining : maximum;
+
+ memcpy(buffer, source->data + source->offset, count);
+ source->offset += count;
+ return (long)count;
+}
+
+static int
+Search(const char *message, char *header, char *substring)
+{
+ MemorySource source = { message, strlen(message), 0U };
+
+ return BongoStreamSearchRfc822Header(ReadMemory, &source, source.length,
+ header, substring);
+}
+
+int
+main(void)
+{
+ static const char message[] =
+ "From: Test User \r\n"
+ "Subject: Local delivery smoke test\r\n"
+ "X-Folded: first\r\n\tsecond\r\n"
+ "\r\n"
+ "The subject word only in this body must not match headers.\r\n";
+
+ assert(Search(message, "subject", "DELIVERY SMOKE"));
+ assert(Search(message, "from", "example.test"));
+ assert(Search(message, "x-folded", "second"));
+ assert(Search(message, NULL, "local delivery"));
+ assert(!Search(message, "subject", "only in this body"));
+ assert(!Search(message, "to", "example.test"));
+ return 0;
+}