bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#include "push-notification-event-messagenew.h"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz#define OX_LOG_LABEL "OX Push Notification: "
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER "vendor/vendor.dovecot/http-notify"
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz/* Default values. */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic const char *const default_events[] = { "MessageNew", NULL };
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic const char *const default_mboxes[] = { "INBOX", NULL };
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz/* This is data that is shared by all plugin users. */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstruct push_notification_driver_ox_global {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic struct push_notification_driver_ox_global *ox_global = NULL;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz/* This is data specific to an OX driver. */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstruct push_notification_driver_ox_config {
630c1df5b6aaec452b0ef040df56cffd978fe9c3Timo Sirainen unsigned int cached_ox_metadata_lifetime_secs;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz/* This is data specific to an OX driver transaction. */
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainenpush_notification_driver_ox_init_global(struct mail_user *user,
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen struct push_notification_driver_ox_config *config)
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen /* this is going to use the first user's settings, but these are
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen unlikely to change between users so it shouldn't matter much. */
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen http_set.max_attempts = config->http_max_retries+1;
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen http_set.request_timeout_msecs = config->http_timeout_msecs;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz ox_global->http_client = http_client_init(&http_set);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzpush_notification_driver_ox_init(struct push_notification_driver_config *config,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct push_notification_driver_ox_config *dconfig;
8b4073f4a7fd6423cb73b1cddf72acbec04598c6Michael Slusarz /* Valid config keys: cache_lifetime, url */
8b4073f4a7fd6423cb73b1cddf72acbec04598c6Michael Slusarz tmp = hash_table_lookup(config->config, (const char *)"url");
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz *error_r = OX_LOG_LABEL "Driver requires the url parameter";
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz dconfig = p_new(pool, struct push_notification_driver_ox_config, 1);
8b4073f4a7fd6423cb73b1cddf72acbec04598c6Michael Slusarz if (http_url_parse(tmp, NULL, HTTP_URL_ALLOW_USERINFO_PART, pool,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz *error_r = t_strdup_printf(OX_LOG_LABEL "Failed to parse OX REST URL %s: %s",
cad784f10db7b8f53661246fbbab1d4789e1a25aTimo Sirainen hash_table_lookup(config->config, (const char *)"user_from_metadata") != NULL;
8b4073f4a7fd6423cb73b1cddf72acbec04598c6Michael Slusarz push_notification_driver_debug(OX_LOG_LABEL, user, "Using URL %s", tmp);
8b4073f4a7fd6423cb73b1cddf72acbec04598c6Michael Slusarz tmp = hash_table_lookup(config->config, (const char *)"cache_lifetime");
630c1df5b6aaec452b0ef040df56cffd978fe9c3Timo Sirainen dconfig->cached_ox_metadata_lifetime_secs = DEFAULT_CACHE_LIFETIME_SECS;
630c1df5b6aaec452b0ef040df56cffd978fe9c3Timo Sirainen else if (settings_get_time(tmp, &dconfig->cached_ox_metadata_lifetime_secs, &error) < 0) {
630c1df5b6aaec452b0ef040df56cffd978fe9c3Timo Sirainen *error_r = t_strdup_printf(OX_LOG_LABEL "Failed to parse OX cache_lifetime %s: %s",
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen tmp = hash_table_lookup(config->config, (const char *)"max_retries");
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen (str_to_uint(tmp, &dconfig->http_max_retries) < 0)) {
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen dconfig->http_max_retries = DEFAULT_RETRY_COUNT;
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen tmp = hash_table_lookup(config->config, (const char *)"timeout_msecs");
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen (str_to_uint(tmp, &dconfig->http_timeout_msecs) < 0)) {
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen dconfig->http_timeout_msecs = DEFAULT_TIMEOUT_MSECS;
8b4073f4a7fd6423cb73b1cddf72acbec04598c6Michael Slusarz push_notification_driver_debug(OX_LOG_LABEL, user,
8b4073f4a7fd6423cb73b1cddf72acbec04598c6Michael Slusarz "Using cache lifetime: %u",
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz ox_global = i_new(struct push_notification_driver_ox_global, 1);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic const char *push_notification_driver_ox_get_metadata
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz(struct push_notification_driver_txn *dtxn)
dfebce6840b843e8ae76f632537db4cfd29041fcTimo Sirainen struct push_notification_driver_ox_config *dconfig = dtxn->duser->context;
cf93f9d7d747a14db76c1d1fd21dad9e0a102088Michael Slusarz bool success = FALSE, use_existing_txn = FALSE;
8b4073f4a7fd6423cb73b1cddf72acbec04598c6Michael Slusarz if ((dconfig->cached_ox_metadata != NULL) &&
630c1df5b6aaec452b0ef040df56cffd978fe9c3Timo Sirainen (time_t)dconfig->cached_ox_metadata_lifetime_secs) > ioloop_time)) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz /* Get canonical INBOX, where private server-level metadata is stored.
cf93f9d7d747a14db76c1d1fd21dad9e0a102088Michael Slusarz if ((dtxn->ptxn->t != NULL) && dtxn->ptxn->mbox->inbox_user) {
cf93f9d7d747a14db76c1d1fd21dad9e0a102088Michael Slusarz ns = mail_namespace_find_inbox(dtxn->ptxn->muser->namespaces);
cf93f9d7d747a14db76c1d1fd21dad9e0a102088Michael Slusarz inbox = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_READONLY);
66c87722e0fd2a85cd59797326bad3d1c409dc3aAki Tuomi ret = mailbox_attribute_get(inbox, MAIL_ATTRIBUTE_TYPE_PRIVATE,
66c87722e0fd2a85cd59797326bad3d1c409dc3aAki Tuomi i_error(OX_LOG_LABEL "Skipped because unable to get attribute: %s",
bf7dc750b95039981c0e9d728f313d50cf38a156Martti Rannanjärvi mailbox_get_last_internal_error(inbox, NULL));
66c87722e0fd2a85cd59797326bad3d1c409dc3aAki Tuomi } else if (ret == 0) {
66c87722e0fd2a85cd59797326bad3d1c409dc3aAki Tuomi push_notification_driver_debug(OX_LOG_LABEL, dtxn->ptxn->muser,
66c87722e0fd2a85cd59797326bad3d1c409dc3aAki Tuomi "Skipped because not active (/private/"OX_METADATA_KEY" METADATA not set)");
101becc1042f7b3633324aa91d9b39c4537bfe0eTimo Sirainen dconfig->cached_ox_metadata = i_strdup(attr.value);
8b4073f4a7fd6423cb73b1cddf72acbec04598c6Michael Slusarz dconfig->cached_ox_metadata_timestamp = ioloop_time;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic bool push_notification_driver_ox_begin_txn
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz(struct push_notification_driver_txn *dtxn)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct push_notification_event_messagenew_config *config;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz const char *key, *mbox_curr, *md_value, *value;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct push_notification_driver_ox_txn *txn;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz md_value = push_notification_driver_ox_get_metadata(dtxn);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct mail_user *user = dtxn->ptxn->muser;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz /* Unused keys: events, expire, folder */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz /* TODO: To be implemented later(?) */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz const char *const *events = default_events;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz const char *const *mboxes = default_mboxes;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz push_notification_driver_debug(OX_LOG_LABEL, user,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz "Skipped due to expiration (%ld < %ld)",
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz mbox_curr = mailbox_get_vname(dtxn->ptxn->mbox);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz push_notification_driver_debug(OX_LOG_LABEL, user,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz "Skipped because %s is not a watched mailbox",
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz txn = p_new(dtxn->ptxn->pool, struct push_notification_driver_ox_txn, 1);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz /* Valid keys: user */
cad784f10db7b8f53661246fbbab1d4789e1a25aTimo Sirainen txn->unsafe_user = p_strdup(dtxn->ptxn->pool, value);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_error(OX_LOG_LABEL "No user provided in config");
cad784f10db7b8f53661246fbbab1d4789e1a25aTimo Sirainen push_notification_driver_debug(OX_LOG_LABEL, user, "User (%s)", txn->unsafe_user);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct push_notification_event_messagenew_config, 1);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz config->flags = PUSH_NOTIFICATION_MESSAGE_HDR_FROM |
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz push_notification_event_init(dtxn, "MessageNew", config);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz push_notification_driver_debug(OX_LOG_LABEL, user,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz "Handling MessageNew event");
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic void push_notification_driver_ox_http_callback
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz(const struct http_response *response, struct mail_user *user)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz push_notification_driver_debug(OX_LOG_LABEL, user,
ce3fc9190e82d8d9e9604afd4ebbee1d61957764Stephan Bosch "Notification sent successfully: %s",
ce3fc9190e82d8d9e9604afd4ebbee1d61957764Stephan Bosch i_error(OX_LOG_LABEL "Error when sending notification: %s",
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz/* Callback needed for i_stream_add_destroy_callback() in
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz * push_notification_driver_ox_process_msg. */
5e55914597b8f92b83543d133a8c07d808b3f3ddSteffen Templinstatic int push_notification_driver_ox_get_mailbox_status
282323a5269a0121d9407f098680bf8e7e02f47cSteffen Templin /* The already opened mailbox. We cannot use or sync it, because we are within a save transaction. */
282323a5269a0121d9407f098680bf8e7e02f47cSteffen Templin /* open and sync new instance of the same mailbox to get most recent status */
282323a5269a0121d9407f098680bf8e7e02f47cSteffen Templin box = mailbox_alloc(mailbox_get_namespace(mbox)->list, mailbox_get_name(mbox), MAILBOX_FLAG_READONLY);
bf7dc750b95039981c0e9d728f313d50cf38a156Martti Rannanjärvi i_error("mailbox_sync(%s) failed: %s", mailbox_get_vname(mbox), mailbox_get_last_internal_error(box, NULL));
f948338e87fe0af30e2370b6d663654765b3c5bbTimo Sirainen /* only 'unseen' is needed at the moment */
f948338e87fe0af30e2370b6d663654765b3c5bbTimo Sirainen mailbox_get_open_status(box, STATUS_UNSEEN, r_box_status);
282323a5269a0121d9407f098680bf8e7e02f47cSteffen Templin push_notification_driver_debug(OX_LOG_LABEL, dtxn->ptxn->muser, "Got status of mailbox '%s': (unseen: %u)",
282323a5269a0121d9407f098680bf8e7e02f47cSteffen Templin mailbox_get_vname(box), r_box_status->unseen);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic void push_notification_driver_ox_process_msg
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz(struct push_notification_driver_txn *dtxn,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct push_notification_driver_ox_config *dconfig =
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz (struct push_notification_driver_ox_config *)dtxn->duser->context;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct push_notification_event_messagenew_data *messagenew;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct push_notification_driver_ox_txn *txn =
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz (struct push_notification_driver_ox_txn *)dtxn->context;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz struct mail_user *user = dtxn->ptxn->muser;
282323a5269a0121d9407f098680bf8e7e02f47cSteffen Templin if (push_notification_driver_ox_get_mailbox_status(dtxn, &box_status) < 0) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz messagenew = push_notification_txn_msg_get_eventdata(msg, "MessageNew");
0f58da3fd10d43bf701eba068cb3b84a216f89f1Timo Sirainen push_notification_driver_ox_init_global(user, dconfig);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz http_req = http_client_request_url(ox_global->http_client, "PUT",
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz push_notification_driver_ox_http_callback,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz http_client_request_add_header(http_req, "Content-Type",
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz "application/json; charset=utf-8");
cad784f10db7b8f53661246fbbab1d4789e1a25aTimo Sirainen json_append_escaped(str, dconfig->use_unsafe_username ?
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz str_append(str, "\",\"event\":\"messageNew\",\"folder\":\"");
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz str_printfa(str, "\",\"imap-uidvalidity\":%u,\"imap-uid\":%u",
3722c793bbea1495000de35815d31091a75f3f9cSteffen Templin json_append_escaped(str, messagenew->subject);
3722c793bbea1495000de35815d31091a75f3f9cSteffen Templin json_append_escaped(str, messagenew->snippet);
3df60858b15645fbc64ac18842c2545da0439c08Michael Koch str_printfa(str, ",\"unseen\":%u", box_status.unseen);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz push_notification_driver_debug(OX_LOG_LABEL, user,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz payload = i_stream_create_from_data(str_data(str), str_len(str));
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz i_stream_add_destroy_callback(payload, str_free_i, str);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz http_client_request_set_payload(http_req, payload, FALSE);
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic void push_notification_driver_ox_deinit
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz(struct push_notification_driver_user *duser ATTR_UNUSED)
101becc1042f7b3633324aa91d9b39c4537bfe0eTimo Sirainen struct push_notification_driver_ox_config *dconfig = duser->context;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstatic void push_notification_driver_ox_cleanup(void)
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz if ((ox_global != NULL) && (ox_global->refcount <= 0)) {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz/* Driver definition */
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzextern struct push_notification_driver push_notification_driver_ox;
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarzstruct push_notification_driver push_notification_driver_ox = {
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz .begin_txn = push_notification_driver_ox_begin_txn,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz .process_msg = push_notification_driver_ox_process_msg,
51ed197520dd9ea534fbc3bc1790ebe3cb5421e2Michael M Slusarz .deinit = push_notification_driver_ox_deinit,