Restore store-backed IMAP header search
This commit is contained in:
+74
-53
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user