fix: harden flatfile backend, make SQLite optional
Some checks failed
CI Code / Check spelling (pull_request) Successful in 21s
CI Code / Check coding style (pull_request) Failing after 32s
CI Code / Code Coverage (pull_request) Successful in 4m50s
CI Code / Linux (debian) (pull_request) Successful in 6m13s
CI Code / Linux (ubuntu) (pull_request) Successful in 7m14s
CI Code / Linux (arch) (pull_request) Successful in 11m2s
Some checks failed
CI Code / Check spelling (pull_request) Successful in 21s
CI Code / Check coding style (pull_request) Failing after 32s
CI Code / Code Coverage (pull_request) Successful in 4m50s
CI Code / Linux (debian) (pull_request) Successful in 6m13s
CI Code / Linux (ubuntu) (pull_request) Successful in 7m14s
CI Code / Linux (arch) (pull_request) Successful in 11m2s
Build system:
- Add --without-sqlite configure flag (AM_CONDITIONAL BUILD_SQLITE)
- Guard database_sqlite.c with HAVE_SQLITE in Makefile.am, database.c,
database.h and stub_database.c
- Fall back to flatfile when SQLite not compiled in
Security (database_flatfile.c):
- MAM dedup: skip incoming messages with duplicate stanza-id (archive_id)
- LMC sender validation: reject corrections from mismatched JIDs
- Add flock() advisory locking to prevent interleaved writes from
concurrent instances
- Check fprintf return when writing file header
Autocomplete (cmd_ac.c):
- Add 'flatfile' to /privacy logging autocomplete
- Add dedicated /history autocomplete with on/off/verify (was boolean-only)
Code quality:
- Fix fwrite return type: size_t not ssize_t (database_flatfile_parser.c)
- Fix mixed allocators in ff_readline: use malloc() consistently for
overlength-line fallback instead of g_strdup()
- Fix index alignment in _ff_state_extend: use local counter so index
step doesn't depend on total_lines from initial build
Documentation:
- Fix page: correct directory structure (history.log not per-day files)
- Fix page: add aid:{archive_id} to line format example
- Fix page: missing newline before .SH BUGS
This commit is contained in:
@@ -3,7 +3,6 @@ core_sources = \
|
|||||||
src/log.c src/common.c \
|
src/log.c src/common.c \
|
||||||
src/chatlog.c src/chatlog.h \
|
src/chatlog.c src/chatlog.h \
|
||||||
src/database.h src/database.c \
|
src/database.h src/database.c \
|
||||||
src/database_sqlite.c \
|
|
||||||
src/database_flatfile.c src/database_flatfile.h \
|
src/database_flatfile.c src/database_flatfile.h \
|
||||||
src/database_flatfile_parser.c \
|
src/database_flatfile_parser.c \
|
||||||
src/database_flatfile_verify.c \
|
src/database_flatfile_verify.c \
|
||||||
@@ -259,6 +258,12 @@ core_sources += $(omemo_sources)
|
|||||||
unittest_sources += $(omemo_unittest_sources)
|
unittest_sources += $(omemo_unittest_sources)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
sqlite_sources = src/database_sqlite.c
|
||||||
|
|
||||||
|
if BUILD_SQLITE
|
||||||
|
core_sources += $(sqlite_sources)
|
||||||
|
endif
|
||||||
|
|
||||||
all_c_sources = $(core_sources) $(unittest_sources) \
|
all_c_sources = $(core_sources) $(unittest_sources) \
|
||||||
$(pgp_sources) $(pgp_unittest_sources) \
|
$(pgp_sources) $(pgp_unittest_sources) \
|
||||||
$(otr4_sources) $(otr_unittest_sources) \
|
$(otr4_sources) $(otr_unittest_sources) \
|
||||||
|
|||||||
14
configure.ac
14
configure.ac
@@ -94,8 +94,17 @@ PKG_CHECK_MODULES([curl], [libcurl >= 7.62.0], [],
|
|||||||
[AC_CHECK_LIB([curl], [main], [],
|
[AC_CHECK_LIB([curl], [main], [],
|
||||||
[AC_MSG_ERROR([libcurl 7.62.0 or higher is required])])])
|
[AC_MSG_ERROR([libcurl 7.62.0 or higher is required])])])
|
||||||
|
|
||||||
PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.22.0], [],
|
### sqlite (optional — can be disabled with --without-sqlite)
|
||||||
[AC_MSG_ERROR([sqlite3 3.22.0 or higher is required])])
|
AC_ARG_WITH([sqlite],
|
||||||
|
[AS_HELP_STRING([--without-sqlite], [build without SQLite support (flat-file backend only)])],
|
||||||
|
[], [with_sqlite=yes])
|
||||||
|
|
||||||
|
AS_IF([test "x$with_sqlite" != "xno"],
|
||||||
|
[PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.22.0],
|
||||||
|
[AC_DEFINE([HAVE_SQLITE], [1], [SQLite support])],
|
||||||
|
[AC_MSG_ERROR([sqlite3 3.22.0 or higher is required (use --without-sqlite to disable)])])],
|
||||||
|
[AC_MSG_NOTICE([Building without SQLite — flat-file backend only])])
|
||||||
|
AM_CONDITIONAL([BUILD_SQLITE], [test "x$with_sqlite" != "xno"])
|
||||||
|
|
||||||
ACX_PTHREAD([], [AC_MSG_ERROR([pthread is required])])
|
ACX_PTHREAD([], [AC_MSG_ERROR([pthread is required])])
|
||||||
AS_IF([test "x$PTHREAD_CC" != x], [ CC="$PTHREAD_CC" ])
|
AS_IF([test "x$PTHREAD_CC" != x], [ CC="$PTHREAD_CC" ])
|
||||||
@@ -416,6 +425,7 @@ AC_OUTPUT
|
|||||||
AC_MSG_NOTICE([Summary of build options:
|
AC_MSG_NOTICE([Summary of build options:
|
||||||
PLATFORM : $target_os
|
PLATFORM : $target_os
|
||||||
PACKAGE_STATUS : $PACKAGE_STATUS
|
PACKAGE_STATUS : $PACKAGE_STATUS
|
||||||
|
SQLite support : $with_sqlite
|
||||||
GTK_VERSION : $GTK_VERSION
|
GTK_VERSION : $GTK_VERSION
|
||||||
LIBS : $LIBS
|
LIBS : $LIBS
|
||||||
Install themes : $THEMES_INSTALL
|
Install themes : $THEMES_INSTALL
|
||||||
|
|||||||
@@ -215,17 +215,18 @@ Or use the command:
|
|||||||
Flat-file logs are stored under
|
Flat-file logs are stored under
|
||||||
.IR $XDG_DATA_HOME/profanity/flatlog/ ,
|
.IR $XDG_DATA_HOME/profanity/flatlog/ ,
|
||||||
organized as
|
organized as
|
||||||
.IR {account_jid}/{contact_jid}/{YYYY_MM_DD}.log .
|
.IR {account_jid}/{contact_jid}/history.log .
|
||||||
.PP
|
.PP
|
||||||
Each line has the format:
|
Each line has the format:
|
||||||
.br
|
.br
|
||||||
.EX
|
.EX
|
||||||
{ISO8601} [{type}|{enc}|id:{id}|corrects:{id}] {sender}: {message}
|
{ISO8601} [{type}|{enc}|id:{id}|aid:{archive_id}|corrects:{id}] {sender}: {message}
|
||||||
.EE
|
.EE
|
||||||
.PP
|
.PP
|
||||||
Use
|
Use
|
||||||
.B /history verify
|
.B /history verify
|
||||||
to check integrity of stored history (both SQLite and flat-file)..SH BUGS
|
to check integrity of stored history (both SQLite and flat-file).
|
||||||
|
.SH BUGS
|
||||||
Bugs can either be reported by raising an issue at the Github issue tracker:
|
Bugs can either be reported by raising an issue at the Github issue tracker:
|
||||||
.br
|
.br
|
||||||
.PP
|
.PP
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ static Autocomplete logging_ac;
|
|||||||
static Autocomplete logging_group_ac;
|
static Autocomplete logging_group_ac;
|
||||||
static Autocomplete privacy_ac;
|
static Autocomplete privacy_ac;
|
||||||
static Autocomplete privacy_log_ac;
|
static Autocomplete privacy_log_ac;
|
||||||
|
static Autocomplete history_ac;
|
||||||
static Autocomplete color_ac;
|
static Autocomplete color_ac;
|
||||||
static Autocomplete correction_ac;
|
static Autocomplete correction_ac;
|
||||||
static Autocomplete avatar_ac;
|
static Autocomplete avatar_ac;
|
||||||
@@ -429,6 +430,7 @@ static Autocomplete* all_acs[] = {
|
|||||||
&logging_group_ac,
|
&logging_group_ac,
|
||||||
&privacy_ac,
|
&privacy_ac,
|
||||||
&privacy_log_ac,
|
&privacy_log_ac,
|
||||||
|
&history_ac,
|
||||||
&color_ac,
|
&color_ac,
|
||||||
&correction_ac,
|
&correction_ac,
|
||||||
&avatar_ac,
|
&avatar_ac,
|
||||||
@@ -1138,6 +1140,11 @@ cmd_ac_init(void)
|
|||||||
autocomplete_add(privacy_log_ac, "on");
|
autocomplete_add(privacy_log_ac, "on");
|
||||||
autocomplete_add(privacy_log_ac, "off");
|
autocomplete_add(privacy_log_ac, "off");
|
||||||
autocomplete_add(privacy_log_ac, "redact");
|
autocomplete_add(privacy_log_ac, "redact");
|
||||||
|
autocomplete_add(privacy_log_ac, "flatfile");
|
||||||
|
|
||||||
|
autocomplete_add(history_ac, "on");
|
||||||
|
autocomplete_add(history_ac, "off");
|
||||||
|
autocomplete_add(history_ac, "verify");
|
||||||
|
|
||||||
autocomplete_add(logging_group_ac, "on");
|
autocomplete_add(logging_group_ac, "on");
|
||||||
autocomplete_add(logging_group_ac, "off");
|
autocomplete_add(logging_group_ac, "off");
|
||||||
@@ -1776,7 +1783,7 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ
|
|||||||
|
|
||||||
// autocomplete boolean settings
|
// autocomplete boolean settings
|
||||||
gchar* boolean_choices[] = { "/beep", "/states", "/outtype", "/flash", "/splash",
|
gchar* boolean_choices[] = { "/beep", "/states", "/outtype", "/flash", "/splash",
|
||||||
"/history", "/vercheck", "/privileges", "/wrap",
|
"/vercheck", "/privileges", "/wrap",
|
||||||
"/carbons", "/slashguard", "/mam", "/silence" };
|
"/carbons", "/slashguard", "/mam", "/silence" };
|
||||||
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(boolean_choices); i++) {
|
for (int i = 0; i < ARRAY_SIZE(boolean_choices); i++) {
|
||||||
@@ -1786,6 +1793,11 @@ _cmd_ac_complete_params(ProfWin* window, const char* const input, gboolean previ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result = autocomplete_param_with_ac(input, "/history", history_ac, TRUE, previous);
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// autocomplete nickname in chat rooms
|
// autocomplete nickname in chat rooms
|
||||||
if (window->type == WIN_MUC) {
|
if (window->type == WIN_MUC) {
|
||||||
ProfMucWin* mucwin = (ProfMucWin*)window;
|
ProfMucWin* mucwin = (ProfMucWin*)window;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "config.h"
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
#include "config/preferences.h"
|
#include "config/preferences.h"
|
||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
@@ -69,8 +70,13 @@ log_database_init(ProfAccount* account)
|
|||||||
active_db_backend = db_backend_flatfile();
|
active_db_backend = db_backend_flatfile();
|
||||||
log_info("Using flat-file database backend");
|
log_info("Using flat-file database backend");
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef HAVE_SQLITE
|
||||||
active_db_backend = db_backend_sqlite();
|
active_db_backend = db_backend_sqlite();
|
||||||
log_info("Using SQLite database backend");
|
log_info("Using SQLite database backend");
|
||||||
|
#else
|
||||||
|
log_info("SQLite not compiled in, falling back to flat-file backend");
|
||||||
|
active_db_backend = db_backend_flatfile();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!active_db_backend) {
|
if (!active_db_backend) {
|
||||||
|
|||||||
@@ -85,7 +85,9 @@ typedef struct db_backend_t
|
|||||||
extern db_backend_t* active_db_backend;
|
extern db_backend_t* active_db_backend;
|
||||||
|
|
||||||
// Backend registry
|
// Backend registry
|
||||||
|
#ifdef HAVE_SQLITE
|
||||||
db_backend_t* db_backend_sqlite(void);
|
db_backend_t* db_backend_sqlite(void);
|
||||||
|
#endif
|
||||||
db_backend_t* db_backend_flatfile(void);
|
db_backend_t* db_backend_flatfile(void);
|
||||||
|
|
||||||
// Public API (dispatches to active_db_backend)
|
// Public API (dispatches to active_db_backend)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/file.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -180,7 +181,9 @@ _ff_state_extend(ff_contact_state_t* state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t msg_count = state->total_lines;
|
// Use a local counter for new messages so index step alignment
|
||||||
|
// doesn't depend on the total from the initial build.
|
||||||
|
size_t new_count = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
off_t pos = ftell(fp);
|
off_t pos = ftell(fp);
|
||||||
@@ -200,7 +203,7 @@ _ff_state_extend(ff_contact_state_t* state)
|
|||||||
|
|
||||||
state->total_lines++;
|
state->total_lines++;
|
||||||
|
|
||||||
if (msg_count % FF_INDEX_STEP == 0) {
|
if (new_count % FF_INDEX_STEP == 0) {
|
||||||
char* space = strchr(buf, ' ');
|
char* space = strchr(buf, ' ');
|
||||||
if (space) {
|
if (space) {
|
||||||
char* ts_str = g_strndup(buf, space - buf);
|
char* ts_str = g_strndup(buf, space - buf);
|
||||||
@@ -219,7 +222,7 @@ _ff_state_extend(ff_contact_state_t* state)
|
|||||||
g_free(ts_str);
|
g_free(ts_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
msg_count++;
|
new_count++;
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,8 +393,17 @@ _ff_add_message(const char* type, const char* stanza_id, const char* archive_id,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Advisory lock to prevent interleaved writes from concurrent instances
|
||||||
|
if (flock(fd, LOCK_EX) != 0) {
|
||||||
|
log_warning("flatfile: flock(%s) failed (errno=%d: %s), writing anyway",
|
||||||
|
log_path, errno, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
if (is_new) {
|
if (is_new) {
|
||||||
fprintf(fp, "%s", FLATFILE_HEADER);
|
if (fprintf(fp, "%s", FLATFILE_HEADER) < 0) {
|
||||||
|
log_error("flatfile: failed to write header to %s (errno=%d)",
|
||||||
|
log_path, errno);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ff_write_line(fp, date_fmt, type, ff_get_message_enc_str(enc),
|
ff_write_line(fp, date_fmt, type, ff_get_message_enc_str(enc),
|
||||||
@@ -399,6 +411,7 @@ _ff_add_message(const char* type, const char* stanza_id, const char* archive_id,
|
|||||||
from_barejid, from_resource, effective_msg);
|
from_barejid, from_resource, effective_msg);
|
||||||
|
|
||||||
fflush(fp);
|
fflush(fp);
|
||||||
|
// fclose also releases the flock
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
g_free(effective_msg);
|
g_free(effective_msg);
|
||||||
}
|
}
|
||||||
@@ -453,6 +466,82 @@ _flatfile_close(void)
|
|||||||
// Backend callbacks: add message
|
// Backend callbacks: add message
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
|
// =========================================================================
|
||||||
|
// Incoming message validation helpers
|
||||||
|
// =========================================================================
|
||||||
|
|
||||||
|
// Search log file for a line containing aid:{archive_id}, return TRUE if found.
|
||||||
|
static gboolean
|
||||||
|
_ff_has_archive_id(const char* log_path, const char* archive_id)
|
||||||
|
{
|
||||||
|
if (!log_path || !archive_id || strlen(archive_id) == 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
FILE* fp = fopen(log_path, "r");
|
||||||
|
if (!fp)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
auto_gchar gchar* needle = g_strdup_printf("aid:%s", archive_id);
|
||||||
|
gboolean found = FALSE;
|
||||||
|
|
||||||
|
while (!found) {
|
||||||
|
gboolean trunc = FALSE;
|
||||||
|
char* line = ff_readline(fp, &trunc);
|
||||||
|
if (!line)
|
||||||
|
break;
|
||||||
|
if (trunc) {
|
||||||
|
free(line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (strstr(line, needle)) {
|
||||||
|
found = TRUE;
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search log file for a line with stanza id matching replace_id, return the
|
||||||
|
// from_jid of the original message (caller must g_free). Returns NULL if not found.
|
||||||
|
static char*
|
||||||
|
_ff_find_original_sender(const char* log_path, const char* replace_id)
|
||||||
|
{
|
||||||
|
if (!log_path || !replace_id || strlen(replace_id) == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
FILE* fp = fopen(log_path, "r");
|
||||||
|
if (!fp)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
auto_gchar gchar* needle = g_strdup_printf("id:%s", replace_id);
|
||||||
|
char* sender = NULL;
|
||||||
|
|
||||||
|
while (!sender) {
|
||||||
|
gboolean trunc = FALSE;
|
||||||
|
char* line = ff_readline(fp, &trunc);
|
||||||
|
if (!line)
|
||||||
|
break;
|
||||||
|
if (trunc) {
|
||||||
|
free(line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (strstr(line, needle)) {
|
||||||
|
ff_parsed_line_t* pl = ff_parse_line(line);
|
||||||
|
if (pl && pl->stanza_id && g_strcmp0(pl->stanza_id, replace_id) == 0) {
|
||||||
|
sender = g_strdup(pl->from_jid);
|
||||||
|
}
|
||||||
|
if (pl)
|
||||||
|
ff_parsed_line_free(pl);
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_flatfile_add_incoming(ProfMessage* message)
|
_flatfile_add_incoming(ProfMessage* message)
|
||||||
{
|
{
|
||||||
@@ -462,6 +551,39 @@ _flatfile_add_incoming(ProfMessage* message)
|
|||||||
const Jid* to_jid = message->to_jid ? message->to_jid : connection_get_jid();
|
const Jid* to_jid = message->to_jid ? message->to_jid : connection_get_jid();
|
||||||
const char* type = ff_get_message_type_str(message->type);
|
const char* type = ff_get_message_type_str(message->type);
|
||||||
|
|
||||||
|
// Determine contact JID for log path (same logic as _ff_add_message)
|
||||||
|
const char* contact = NULL;
|
||||||
|
const Jid* myjid = connection_get_jid();
|
||||||
|
if (myjid && myjid->barejid && g_strcmp0(message->from_jid->barejid, myjid->barejid) == 0) {
|
||||||
|
contact = to_jid->barejid;
|
||||||
|
} else {
|
||||||
|
contact = message->from_jid->barejid;
|
||||||
|
}
|
||||||
|
auto_gchar gchar* log_path = ff_get_log_path(contact);
|
||||||
|
|
||||||
|
// MAM dedup: skip if this stanza-id already exists in the log (XEP-0313 replay)
|
||||||
|
if (message->stanzaid && !message->is_mam && log_path) {
|
||||||
|
if (_ff_has_archive_id(log_path, message->stanzaid)) {
|
||||||
|
log_error("flatfile: duplicate stanza-id '%s' from %s, skipping",
|
||||||
|
message->stanzaid, message->from_jid->barejid);
|
||||||
|
cons_show_error("Got a message with duplicate (server-generated) stanza-id from %s.",
|
||||||
|
message->from_jid->fulljid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LMC sender validation: verify the correction comes from the original sender
|
||||||
|
if (message->replace_id && log_path) {
|
||||||
|
auto_gchar gchar* original_sender = _ff_find_original_sender(log_path, message->replace_id);
|
||||||
|
if (original_sender && g_strcmp0(original_sender, message->from_jid->barejid) != 0) {
|
||||||
|
log_error("flatfile: LMC sender mismatch — corrected msg sender: %s, original: %s, replace-id: %s",
|
||||||
|
message->from_jid->barejid, original_sender, message->replace_id);
|
||||||
|
cons_show_error("%s sent a message correction with mismatched sender. See log for details.",
|
||||||
|
message->from_jid->barejid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ff_add_message(type, message->id, message->stanzaid, message->replace_id,
|
_ff_add_message(type, message->id, message->stanzaid, message->replace_id,
|
||||||
message->from_jid->barejid, message->from_jid->resourcepart,
|
message->from_jid->barejid, message->from_jid->resourcepart,
|
||||||
to_jid->barejid, message->plain,
|
to_jid->barejid, message->plain,
|
||||||
|
|||||||
@@ -369,8 +369,11 @@ ff_readline(FILE* fp, gboolean* truncated)
|
|||||||
}
|
}
|
||||||
if (truncated)
|
if (truncated)
|
||||||
*truncated = FALSE;
|
*truncated = FALSE;
|
||||||
// Return empty string so caller's loop continues (parse will reject it)
|
// Return empty string so caller's loop continues (parse will reject it).
|
||||||
return g_strdup("");
|
// Use malloc() so callers can always use free() consistently.
|
||||||
|
char* empty = malloc(1);
|
||||||
|
if (empty) empty[0] = '\0';
|
||||||
|
return empty;
|
||||||
}
|
}
|
||||||
if (truncated)
|
if (truncated)
|
||||||
*truncated = FALSE;
|
*truncated = FALSE;
|
||||||
@@ -450,9 +453,9 @@ ff_write_line(FILE* fp, const char* timestamp, const char* type, const char* enc
|
|||||||
timestamp, meta->str, sender->str, safe_msg);
|
timestamp, meta->str, sender->str, safe_msg);
|
||||||
|
|
||||||
size_t to_write = full_line->len;
|
size_t to_write = full_line->len;
|
||||||
ssize_t written = fwrite(full_line->str, 1, to_write, fp);
|
size_t written = fwrite(full_line->str, 1, to_write, fp);
|
||||||
if (written != (ssize_t)to_write) {
|
if (written != to_write) {
|
||||||
log_error("flatfile: partial write (%zd/%zu)", written, to_write);
|
log_error("flatfile: partial write (%zu/%zu)", written, to_write);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_string_free(full_line, TRUE);
|
g_string_free(full_line, TRUE);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include "prof_cmocka.h"
|
#include "prof_cmocka.h"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
|
||||||
db_backend_t* active_db_backend = NULL;
|
db_backend_t* active_db_backend = NULL;
|
||||||
@@ -66,11 +67,13 @@ integrity_issue_free(integrity_issue_t* issue)
|
|||||||
g_free(issue);
|
g_free(issue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef HAVE_SQLITE
|
||||||
db_backend_t*
|
db_backend_t*
|
||||||
db_backend_sqlite(void)
|
db_backend_sqlite(void)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
db_backend_t*
|
db_backend_t*
|
||||||
db_backend_flatfile(void)
|
db_backend_flatfile(void)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user