<commit_before>/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "hash.h"
#include "http-client.h"
#include "http-url.h"
#include "ioloop.h"
#include "istream.h"
#include "json-parser.h"
#include "mailbox-attribute.h"
#include "mail-storage-private.h"
#include "str.h"

#include "push-notification-drivers.h"
#include "push-notification-event-messagenew.h"
#include "push-notification-events.h"
#include "push-notification-txn-msg.h"


#define OX_LOG_LABEL "OX Push Notification: "

#define OX_METADATA_KEY \
    MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER "vendor/vendor.dovecot/http-notify"

/* Default values. */
static const char *const default_events[] = { "MessageNew", NULL };
static const char *const default_mboxes[] = { "INBOX", NULL };


/* This is data that is shared by all plugin users. */
struct push_notification_driver_ox_global {
    struct http_client *http_client;
    int refcount;
};
static struct push_notification_driver_ox_global *ox_global = NULL;

/* This is data specific to an OX driver. */
struct push_notification_driver_ox_config {
    struct http_url *http_url;
};

/* This is data specific to an OX driver transaction. */
struct push_notification_driver_ox_txn {
    const char *user;
};

static void
push_notification_driver_ox_init_global(struct mail_user *user)
{
    struct http_client_settings http_set;

    if (ox_global->http_client == NULL) {
        memset(&http_set, 0, sizeof(http_set));
        http_set.debug = user->mail_debug;

        ox_global->http_client = http_client_init(&http_set);
    }
}

static int
push_notification_driver_ox_init(struct push_notification_driver_config *config,
                                 struct mail_user *user, pool_t pool,
                                 void **context, const char **error_r)
{
    struct push_notification_driver_ox_config *dconfig;
    const char *error, *url;

    /* Valid config keys: url */
    url = hash_table_lookup(config->config, (const char *)"url");
    if (url == NULL) {
        *error_r = OX_LOG_LABEL "Driver requires the url parameter";
        return -1;
    }

    dconfig = p_new(pool, struct push_notification_driver_ox_config, 1);

    if (http_url_parse(url, NULL, HTTP_URL_ALLOW_USERINFO_PART, pool,
                       &dconfig->http_url, &error) < 0) {
        *error_r = t_strdup_printf(OX_LOG_LABEL "Failed to parse OX REST URL %s: %s",
                                   url, error);
        return -1;
    }

    push_notification_driver_debug(OX_LOG_LABEL, user, "Using URL %s", url);

    if (ox_global == NULL) {
        ox_global = i_new(struct push_notification_driver_ox_global, 1);
        ox_global->refcount = 0;
    }

    ++ox_global->refcount;
    *context = dconfig;

    return 0;
}

static const char *push_notification_driver_ox_get_metadata
(struct push_notification_driver_txn *dtxn)
{
    struct mail_attribute_value attr;
    struct mailbox *inbox;
    struct mailbox_transaction_context *mctx = NULL;
    struct mail_namespace *ns;
    bool success = FALSE, use_existing_txn = FALSE;
    int ret;

    /* Get canonical INBOX, where private server-level metadata is stored.
     * See imap/cmd-getmetadata.c */
    if ((dtxn->ptxn->t != NULL) && dtxn->ptxn->mbox->inbox_user) {
        /* Use the currently open transaction. */
        inbox = dtxn->ptxn->mbox;
        mctx = dtxn->ptxn->t;
        use_existing_txn = TRUE;
    } else {
        ns = mail_namespace_find_inbox(dtxn->ptxn->muser->namespaces);
        inbox = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY);
        if (mailbox_open(inbox) < 0) {
            i_error(OX_LOG_LABEL "Skipped because unable to open INBOX: %s",
                    mailbox_get_last_error(inbox, NULL));
        } else {
            mctx = mailbox_transaction_begin(inbox, 0);
        }
    }

    if (mctx != NULL) {
        ret = mailbox_attribute_get(mctx, MAIL_ATTRIBUTE_TYPE_PRIVATE,
                                    OX_METADATA_KEY, &attr);
        if (ret < 0) {
            i_error(OX_LOG_LABEL "Skipped because unable to get attribute: %s",
                    mailbox_get_last_error(inbox, NULL));
        } else if (ret == 0) {
            push_notification_driver_debug(OX_LOG_LABEL, dtxn->ptxn->muser,
                                           "Skipped because not active (/private/"OX_METADATA_KEY" METADATA not set)");
        } else {
            success = TRUE;
        }

        if (!use_existing_txn && (mailbox_transaction_commit(&mctx) < 0)) {
            i_error(OX_LOG_LABEL "Transaction commit failed: %s",
                    mailbox_get_last_error(inbox, NULL));
            /* the commit doesn't matter though. */
        }
    }

    if (!use_existing_txn) {
        mailbox_free(&inbox);
    }

    return (success == TRUE) ? attr.value : NULL;
}

static bool push_notification_driver_ox_begin_txn
(struct push_notification_driver_txn *dtxn)
{
    const char *const *args;
    struct push_notification_event_messagenew_config *config;
    const char *key, *mbox_curr, *md_value, *value;
    bool mbox_found = FALSE;
    struct push_notification_driver_ox_txn *txn;

    md_value = push_notification_driver_ox_get_metadata(dtxn);
    if (md_value == NULL) {
        return FALSE;
    }
    struct mail_user *user = dtxn->ptxn->muser;

    /* Unused keys: events, expire, folder */
    /* TODO: To be implemented later(?) */
    const char *const *events = default_events;
    time_t expire = INT_MAX;
    const char *const *mboxes = default_mboxes;

    if (expire < ioloop_time) {
        push_notification_driver_debug(OX_LOG_LABEL, user,
                                       "Skipped due to expiration (%ld < %ld)",
                                       (long)expire, (long)ioloop_time);<commit_msg>push-notification: Cache the metadata lookup in OX driver.<commit_after>/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "hash.h"
#include "http-client.h"
#include "http-url.h"
#include "ioloop.h"
#include "istream.h"
#include "json-parser.h"
#include "mailbox-attribute.h"
#include "mail-storage-private.h"
#include "str.h"

#include "push-notification-drivers.h"
#include "push-notification-event-messagenew.h"
#include "push-notification-events.h"
#include "push-notification-txn-msg.h"


#define OX_LOG_LABEL "OX Push Notification: "

#define OX_METADATA_KEY \
    MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER "vendor/vendor.dovecot/http-notify"

/* Default values. */
static const char *const default_events[] = { "MessageNew", NULL };
static const char *const default_mboxes[] = { "INBOX", NULL };


/* This is data that is shared by all plugin users. */
struct push_notification_driver_ox_global {
    struct http_client *http_client;
    int refcount;
};
static struct push_notification_driver_ox_global *ox_global = NULL;

/* This is data specific to an OX driver. */
struct push_notification_driver_ox_config {
    struct http_url *http_url;
    const char *cached_ox_metadata;
};

/* This is data specific to an OX driver transaction. */
struct push_notification_driver_ox_txn {
    const char *user;
};

static void
push_notification_driver_ox_init_global(struct mail_user *user)
{
    struct http_client_settings http_set;

    if (ox_global->http_client == NULL) {
        memset(&http_set, 0, sizeof(http_set));
        http_set.debug = user->mail_debug;

        ox_global->http_client = http_client_init(&http_set);
    }
}

static int
push_notification_driver_ox_init(struct push_notification_driver_config *config,
                                 struct mail_user *user, pool_t pool,
                                 void **context, const char **error_r)
{
    struct push_notification_driver_ox_config *dconfig;
    const char *error, *url;

    /* Valid config keys: url */
    url = hash_table_lookup(config->config, (const char *)"url");
    if (url == NULL) {
        *error_r = OX_LOG_LABEL "Driver requires the url parameter";
        return -1;
    }

    dconfig = p_new(pool, struct push_notification_driver_ox_config, 1);

    if (http_url_parse(url, NULL, HTTP_URL_ALLOW_USERINFO_PART, pool,
                       &dconfig->http_url, &error) < 0) {
        *error_r = t_strdup_printf(OX_LOG_LABEL "Failed to parse OX REST URL %s: %s",
                                   url, error);
        return -1;
    }

    push_notification_driver_debug(OX_LOG_LABEL, user, "Using URL %s", url);

    if (ox_global == NULL) {
        ox_global = i_new(struct push_notification_driver_ox_global, 1);
        ox_global->refcount = 0;
    }

    ++ox_global->refcount;
    *context = dconfig;

    return 0;
}

static const char *push_notification_driver_ox_get_metadata
(struct push_notification_driver_txn *dtxn)
{
    struct push_notification_driver_ox_config *dconfig = dtxn->duser->context;
    struct mail_attribute_value attr;
    struct mailbox *inbox;
    struct mailbox_transaction_context *mctx = NULL;
    struct mail_namespace *ns;
    bool success = FALSE, use_existing_txn = FALSE;
    int ret;

    if (dconfig->cached_ox_metadata != NULL)
        return dconfig->cached_ox_metadata;

    /* Get canonical INBOX, where private server-level metadata is stored.
     * See imap/cmd-getmetadata.c */
    if ((dtxn->ptxn->t != NULL) && dtxn->ptxn->mbox->inbox_user) {
        /* Use the currently open transaction. */
        inbox = dtxn->ptxn->mbox;
        mctx = dtxn->ptxn->t;
        use_existing_txn = TRUE;
    } else {
        ns = mail_namespace_find_inbox(dtxn->ptxn->muser->namespaces);
        inbox = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY);
        if (mailbox_open(inbox) < 0) {
            i_error(OX_LOG_LABEL "Skipped because unable to open INBOX: %s",
                    mailbox_get_last_error(inbox, NULL));
        } else {
            mctx = mailbox_transaction_begin(inbox, 0);
        }
    }

    if (mctx != NULL) {
        ret = mailbox_attribute_get(mctx, MAIL_ATTRIBUTE_TYPE_PRIVATE,
                                    OX_METADATA_KEY, &attr);
        if (ret < 0) {
            i_error(OX_LOG_LABEL "Skipped because unable to get attribute: %s",
                    mailbox_get_last_error(inbox, NULL));
        } else if (ret == 0) {
            push_notification_driver_debug(OX_LOG_LABEL, dtxn->ptxn->muser,
                                           "Skipped because not active (/private/"OX_METADATA_KEY" METADATA not set)");
        } else {
            success = TRUE;
        }

        if (!use_existing_txn && (mailbox_transaction_commit(&mctx) < 0)) {
            i_error(OX_LOG_LABEL "Transaction commit failed: %s",
                    mailbox_get_last_error(inbox, NULL));
            /* the commit doesn't matter though. */
        }
    }

    if (!use_existing_txn) {
        mailbox_free(&inbox);
    }

    dconfig->cached_ox_metadata =
        p_strdup(dtxn->ptxn->puser->driverlist->pool, attr.value);
    return (success == TRUE) ? attr.value : NULL;
}

static bool push_notification_driver_ox_begin_txn
(struct push_notification_driver_txn *dtxn)
{
    const char *const *args;
    struct push_notification_event_messagenew_config *config;
    const char *key, *mbox_curr, *md_value, *value;
    bool mbox_found = FALSE;
    struct push_notification_driver_ox_txn *txn;

    md_value = push_notification_driver_ox_get_metadata(dtxn);
    if (md_value == NULL) {
        return FALSE;
    }
    struct mail_user *user = dtxn->ptxn->muser;

    /* Unused keys: events, expire, folder */
    /* TODO: To be implemented later(?) */
    const char *const *events = default_events;
    time_t expire = INT_MAX;
    const char *const *mboxes = default_mboxes;

    if (expire < ioloop_time) {
        push_notification_driver_debug(OX_LOG_LABEL, user,
                                       "Skipped due to expiration (%ld < %ld)",
                                       (long)expire, (long)ioloop_time);
{'fraction_non_alphanumeric': 0.10553184366892628, 'fraction_numerical': 0.0028454263955142688, 'mean_word_length': 2.78044922492882, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2890025', 'n_tokens_mistral': 3954, 'n_tokens_neox': 3842, 'n_words': 885}