Restore store-backed IMAP header search

This commit is contained in:
Mario Fetka
2026-07-19 10:31:06 +02:00
parent 503f87c34f
commit 6e6a5bc2df
3 changed files with 164 additions and 53 deletions
+74 -53
View File
@@ -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;
}
+17
View File
@@ -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()
+73
View File
@@ -0,0 +1,73 @@
/****************************************************************************
* <Novell-copyright>
* 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.
* </Novell-copyright>
****************************************************************************/
#include <xpl.h>
#include <bongostream.h>
#include <assert.h>
#include <stddef.h>
#include <string.h>
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 <test@example.test>\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;
}