summaryrefslogtreecommitdiffstats
path: root/CHANGELOG.md
AgeCommit message (Expand)Author
2021-02-05Release 15.3.015.3.0Benjamin Brahmer
2021-02-05DB: Remove unused fieldsSean Molenaar
2021-02-04fix result ordering for purging queryanoy
2021-02-03update changelogBenjamin Brahmer
2021-02-02Release 15.2.215.2.2Benjamin Brahmer
2021-02-02Release 15.2.115.2.1Benjamin Brahmer
2021-02-02Composer: Remove unneeded files in releasesSean Molenaar
2021-02-02fix articlesPerUpdate aliasanoy
2021-02-02Release 15.2.015.2.0Benjamin Brahmer
2021-02-02Controllers: Export Starred or unread instead of andSean Molenaar
2021-02-02fix item purger for PostgreSQLanoy
2021-01-31Release 15.2.0-rc115.2.0-rc1Benjamin Brahmer
2021-01-30Mappers: Implement item purgingSean Molenaar
2021-01-26Base: Update psalm definition and dependency listSean Molenaar
2021-01-20Fetcher: Update client and add testSean Molenaar
2021-01-18remove alternating row colors; increase row heightanoy
2021-01-18Migration: Don't use unsigned for pubdateSean Molenaar
2021-01-18Controllers: Fetch feed after creatingSean Molenaar
2021-01-1715.2.0-beta2 - 2021-01-1715.2.0-beta2Benjamin Brahmer
2021-01-17fix TypeError caused by type conversion in controlleranoy
2021-01-15fix TypeError in ItemMapperanoy
2021-01-13fix opened state of folders is not restoredanoy
2021-01-12fix changelog and version15.2.0-beta1Benjamin Brahmer
2021-01-12Release 15.2.0beta1Benjamin Brahmer
2021-01-11remove deprecated colon prefix for parametersanoy
2021-01-02Add changelog entry about whitespace changesheyarne
2020-12-30Remove PHPunit integration testsSean Molenaar
2020-12-29Remove PHPunit integration testsSean Molenaar
2020-12-27Release 15.1.115.1.1Benjamin Brahmer
2020-12-23fix FeedMapper find exceptionanoy
2020-12-23Release 15.1.1-rc215.1.1-rc2Benjamin Brahmer
2020-12-21add changelogChristoph Stenglein
2020-12-17Fix mapper->find and empty user sessionsSean Molenaar
2020-12-16Release 15.1.1-rc115.1.1-rc1Benjamin Brahmer
2020-12-15Remove LastModified-based cursor when updating feedsKevin Decherf
2020-12-08Fix alias usage in v2 mapperSean Molenaar
2020-12-08Remove usage of old Folder codeSean Molenaar
2020-12-08Release 15.1.015.1.0Benjamin Brahmer
2020-11-11Release 15.1.0-rc315.1.0-rc3Benjamin Brahmer
2020-11-11fix second where overrides previous conditionanoy
2020-11-10Release 15.1.0-rc215.1.0-rc2Benjamin Brahmer
2020-11-10Unremove but deprecate User APISean Molenaar
2020-11-10Fix unread count and related issuesSean Molenaar
2020-11-09add locale-aware sorting for folders and feedsanoy
2020-11-07remove deprecated YouTube playlist APIanoy
2020-11-06Release 15.1.0-rc115.1.0-rc1Benjamin Brahmer
2020-11-03Controllers: Use v2 servicesSean Molenaar
2020-11-03fix delete apiBenjamin Brahmer
2020-11-03Add migration with foreign keysSean Molenaar
2020-10-29Upload codecoverage to codecov.ioBenjamin Brahmer
#ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
// SPDX-License-Identifier: GPL-3.0-or-later

#include "log2journal.h"

// ----------------------------------------------------------------------------

void log_job_init(LOG_JOB *jb) {
    memset(jb, 0, sizeof(*jb));
    simple_hashtable_init_KEY(&jb->hashtable, 32);
    hashed_key_set(&jb->line.key, "LINE");
}

static void simple_hashtable_cleanup_allocated_keys(SIMPLE_HASHTABLE_KEY *ht) {
    SIMPLE_HASHTABLE_FOREACH_READ_ONLY(ht, sl, _KEY) {
        HASHED_KEY *k = SIMPLE_HASHTABLE_FOREACH_READ_ONLY_VALUE(sl);
        if(k && k->flags & HK_HASHTABLE_ALLOCATED) {
            // the order of these statements is important!
            simple_hashtable_del_slot_KEY(ht, sl); // remove any references to n
            hashed_key_cleanup(k); // cleanup the internals of n
            freez(k); // free n
        }
    }
}

void log_job_cleanup(LOG_JOB *jb) {
    hashed_key_cleanup(&jb->line.key);

    if(jb->prefix) {
        freez((void *) jb->prefix);
        jb->prefix = NULL;
    }

    if(jb->pattern) {
        freez((void *) jb->pattern);
        jb->pattern = NULL;
    }

    for(size_t i = 0; i < jb->injections.used ;i++)
        injection_cleanup(&jb->injections.keys[i]);

    for(size_t i = 0; i < jb->unmatched.injections.used ;i++)
        injection_cleanup(&jb->unmatched.injections.keys[i]);

    for(size_t i = 0; i < jb->renames.used ;i++)
        rename_cleanup(&jb->renames.array[i]);

    for(size_t i = 0; i < jb->rewrites.used; i++)
        rewrite_cleanup(&jb->rewrites.array[i]);

    txt_cleanup(&jb->rewrites.tmp);
    txt_cleanup(&jb->filename.current);

    simple_hashtable_cleanup_allocated_keys(&jb->hashtable);
    simple_hashtable_destroy_KEY(&jb->hashtable);

    // remove references to everything else, to reveal them in valgrind
    memset(jb, 0, sizeof(*jb));
}

// ----------------------------------------------------------------------------

bool log_job_filename_key_set(LOG_JOB *jb, const char *key, size_t key_len) {
    if(!key || !*key) {
        log2stderr("filename key cannot be empty.");
        return false;
    }

    hashed_key_len_set(&jb->filename.key, key, key_len);

    return true;
}

bool log_job_key_prefix_set(LOG_JOB *jb, const char *prefix, size_t prefix_len) {
    if(!prefix || !*prefix) {
        log2stderr("filename key cannot be empty.");
        return false;
    }

    if(jb->prefix)
        freez((char*)jb->prefix);

    jb->prefix = strndupz(prefix, prefix_len);

    return true;
}

bool log_job_pattern_set(LOG_JOB *jb, const char *pattern, size_t pattern_len) {
    if(!pattern || !*pattern) {
        log2stderr("filename key cannot be empty.");
        return false;
    }

    if(jb->pattern)
        freez((char*)jb->pattern);

    jb->pattern = strndupz(pattern, pattern_len);

    return true;
}

bool log_job_include_pattern_set(LOG_JOB *jb, const char *pattern, size_t pattern_len) {
    if(jb->filter.include.re) {
        log2stderr("FILTER INCLUDE: there is already an include filter set");
        return false;
    }

    if(!search_pattern_set(&jb->filter.include, pattern, pattern_len)) {
        log2stderr("FILTER INCLUDE: failed: %s", jb->filter.include.error.txt);
        return false;
    }

    return true;
}

bool log_job_exclude_pattern_set(LOG_JOB *jb, const char *pattern, size_t pattern_len) {
    if(jb->filter.exclude.re) {
        log2stderr("FILTER INCLUDE: there is already an exclude filter set");
        return false;
    }

    if(!search_pattern_set(&jb->filter.exclude, pattern, pattern_len)) {
        log2stderr("FILTER EXCLUDE: failed: %s", jb->filter.exclude.error.txt);
        return false;
    }

    return true;
}

// ----------------------------------------------------------------------------

static bool parse_rename(LOG_JOB *jb, const char *param) {
    // Search for '=' in param
    const char *equal_sign = strchr(param, '=');
    if (!equal_sign || equal_sign == param) {
        log2stderr("Error: Invalid rename format, '=' not found in %s", param);
        return false;
    }

    const char *new_key = param;
    size_t new_key_len = equal_sign - new_key;

    const char *old_key = equal_sign + 1;
    size_t old_key_len = strlen(old_key);

    return log_job_rename_add(jb, new_key, new_key_len, old_key, old_key_len);
}

static bool is_symbol(char c) {
    return !isalpha(c) && !isdigit(c) && !iscntrl(c);
}

struct {
    const char *keyword;
    int action;
    RW_FLAGS flag;
} rewrite_flags[] = {
        {"match",       1, RW_MATCH_PCRE2},
        {"match",       0, RW_MATCH_NON_EMPTY},

        {"regex",       1, RW_MATCH_PCRE2},
        {"regex",       0, RW_MATCH_NON_EMPTY},

        {"pcre2",       1, RW_MATCH_PCRE2},
        {"pcre2",       0, RW_MATCH_NON_EMPTY},

        {"non_empty",   1, RW_MATCH_NON_EMPTY},
        {"non_empty",   0, RW_MATCH_PCRE2},

        {"non-empty",   1, RW_MATCH_NON_EMPTY},
        {"non-empty",   0, RW_MATCH_PCRE2},

        {"not_empty",   1, RW_MATCH_NON_EMPTY},
        {"not_empty",   0, RW_MATCH_PCRE2},

        {"not-empty",   1, RW_MATCH_NON_EMPTY},
        {"not-empty",   0, RW_MATCH_PCRE2},

        {"stop",        0, RW_DONT_STOP},
        {"no-stop",     1, RW_DONT_STOP},
        {"no_stop",     1, RW_DONT_STOP},
        {"dont-stop",   1, RW_DONT_STOP},
        {"dont_stop",   1, RW_DONT_STOP},
        {"continue",    1, RW_DONT_STOP},
        {"inject",      1, RW_INJECT},
        {"existing",    0, RW_INJECT},
};

RW_FLAGS parse_rewrite_flags(const char *options) {
    RW_FLAGS flags = RW_MATCH_PCRE2; // Default option

    // Tokenize the input options using ","
    char *token;
    char *optionsCopy = strdup(options); // Make a copy to avoid modifying the original
    token = strtok(optionsCopy, ",");

    while (token != NULL) {
        // Find the keyword-action mapping
        bool found = false;

        for (size_t i = 0; i < sizeof(rewrite_flags) / sizeof(rewrite_flags[0]); i++) {
            if (strcmp(token, rewrite_flags[i].keyword) == 0) {
                if (rewrite_flags[i].action == 1) {
                    flags