]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-smtp: client: transaction: Add alternative function for adding a recipient on...
authorStephan Bosch <stephan.bosch@dovecot.fi>
Wed, 10 Oct 2018 22:49:33 +0000 (00:49 +0200)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Tue, 12 Feb 2019 13:41:11 +0000 (15:41 +0200)
This allows modifying the recipient object beyond approval. Before, it was
always moved to the transaction pool, thereby invalidating the original returned
pointer. This way, the data_calback and context can be set at a later time,
e.g. when the DATA command is being processed. This makes a choice between
LMTP-style and SMTP-style replies to the DATA command a bit easier to handle.
Also, the recipient is entirely allocated on a single pool between the client
and server side, which should improve memory consumption a little. As a bonus,
this removes the need to have dummy DATA callbacks.

src/lib-smtp/smtp-client-private.h
src/lib-smtp/smtp-client-transaction.c
src/lib-smtp/smtp-client-transaction.h

index d186320c3e085a74e2d85a951c3d4517fc1f1db2..3d3f81566fe513b933d250911bc709561a0f5e70 100644 (file)
@@ -76,11 +76,14 @@ struct smtp_client_transaction_rcpt {
        struct smtp_params_rcpt rcpt_params;
 
        smtp_client_command_callback_t *rcpt_callback;
-       smtp_client_command_callback_t *data_callback;
        void *context;
 
+       smtp_client_command_callback_t *data_callback;
+       void *data_context;
+
        struct smtp_client_command *cmd_rcpt_to;
 
+       bool external_pool:1;
        bool queued:1;
        bool failed:1;
 };
index d875277e5eabbf84f15188bf22dbef8ff56ce302..8f1a0f1118189885242b248e213eebd5cea56b5b 100644 (file)
@@ -100,14 +100,14 @@ void smtp_client_transaction_mail_abort(
 
 static struct smtp_client_transaction_rcpt *
 smtp_client_transaction_rcpt_new(
-       struct smtp_client_transaction *trans,
+       struct smtp_client_transaction *trans, pool_t pool,
        const struct smtp_address *rcpt_to,
        const struct smtp_params_rcpt *rcpt_params)
 {
        struct smtp_client_transaction_rcpt *rcpt;
-       pool_t pool;
 
-       pool = pool_alloconly_create("smtp transaction rcpt", 512);
+       pool_ref(pool);
+
        rcpt = p_new(pool, struct smtp_client_transaction_rcpt, 1);
        rcpt->pool = pool;
        rcpt->trans = trans;
@@ -146,12 +146,33 @@ smtp_client_transaction_rcpt_free(
                trans->rcpts_count--;
        }
 
-       if (rcpt->queued) {
+       if (rcpt->queued || rcpt->external_pool) {
                i_assert(rcpt->pool != NULL);
                pool_unref(&rcpt->pool);
        }
 }
 
+static void
+smtp_client_transaction_rcpt_drop_pending(
+       struct smtp_client_transaction_rcpt *rcpt)
+{
+       struct smtp_client_transaction *trans = rcpt->trans;
+
+       i_assert(rcpt->queued);
+
+       if (rcpt->external_pool) {
+               rcpt->queued = FALSE;
+               if (trans->rcpts_send == rcpt)
+                       trans->rcpts_send = rcpt->next;
+               DLLIST2_REMOVE(&trans->rcpts_queue_head,
+                              &trans->rcpts_queue_tail, rcpt);
+               trans->rcpts_queue_count--;
+               return;
+       }
+
+       smtp_client_transaction_rcpt_free(&rcpt);
+}
+
 static void
 smtp_client_transaction_rcpt_approved(
        struct smtp_client_transaction_rcpt **_rcpt)
@@ -161,24 +182,28 @@ smtp_client_transaction_rcpt_approved(
        struct smtp_client_transaction_rcpt *rcpt;
        pool_t pool;
 
-       /* move to transaction pool */
-       pool = trans->pool;
-       rcpt = p_new(pool, struct smtp_client_transaction_rcpt, 1);
-       rcpt->trans = trans;
-       rcpt->rcpt_to = smtp_address_clone(pool, prcpt->rcpt_to);
-       smtp_params_rcpt_copy(pool, &rcpt->rcpt_params, &prcpt->rcpt_params);
-       rcpt->data_callback = prcpt->data_callback;
-       rcpt->context = prcpt->context;
+       if (prcpt->external_pool)
+               rcpt = prcpt;
+       else {
+               /* move to transaction pool */
+               pool = trans->pool;
+               rcpt = p_new(pool, struct smtp_client_transaction_rcpt, 1);
+               rcpt->trans = trans;
+               rcpt->rcpt_to = smtp_address_clone(pool, prcpt->rcpt_to);
+               smtp_params_rcpt_copy(pool, &rcpt->rcpt_params,
+                                     &prcpt->rcpt_params);
+               rcpt->data_callback = prcpt->data_callback;
+               rcpt->data_context = prcpt->data_context;
+       }
+
+       /* not pending anymore */
+       smtp_client_transaction_rcpt_drop_pending(prcpt);
 
        /* recipient is approved */
        DLLIST2_APPEND(&trans->rcpts_head, &trans->rcpts_tail, rcpt);
        trans->rcpts_count++;
        if (trans->rcpts_data == NULL)
                trans->rcpts_data = trans->rcpts_head;
-       rcpt->queued = FALSE;
-
-       /* not pending anymore */
-       smtp_client_transaction_rcpt_free(&prcpt);
 
        *_rcpt = rcpt;
 }
@@ -201,7 +226,7 @@ void smtp_client_transaction_rcpt_abort(
        struct smtp_client_transaction_rcpt *rcpt = *_rcpt;
        struct smtp_client_transaction *trans = rcpt->trans;
 
-       i_assert(rcpt->queued);
+       i_assert(rcpt->queued || rcpt->external_pool);
 
        i_assert(trans->state <= SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO ||
                 trans->state == SMTP_CLIENT_TRANSACTION_STATE_ABORTED);
@@ -209,6 +234,15 @@ void smtp_client_transaction_rcpt_abort(
        smtp_client_transaction_rcpt_free(_rcpt);
 }
 
+#undef smtp_client_transaction_rcpt_set_data_callback
+void smtp_client_transaction_rcpt_set_data_callback(
+       struct smtp_client_transaction_rcpt *rcpt,
+       smtp_client_command_callback_t *callback, void *context)
+{
+       rcpt->data_callback = callback;
+       rcpt->data_context = context;
+}
+
 /*
  * Transaction
  */
@@ -380,6 +414,12 @@ void smtp_client_transaction_unref(struct smtp_client_transaction **_trans)
        i_stream_unref(&trans->data_input);
        smtp_client_transaction_abort(trans);
 
+       while (trans->rcpts_count > 0) {
+               struct smtp_client_transaction_rcpt *rcpt =
+                       trans->rcpts_head;
+               smtp_client_transaction_rcpt_free(&rcpt);
+       }
+
        i_assert(trans->state >= SMTP_CLIENT_TRANSACTION_STATE_FINISHED);
        pool_unref(&trans->pool);
 
@@ -415,6 +455,13 @@ void smtp_client_transaction_destroy(struct smtp_client_transaction **_trans)
        if (trans->cmd_plug != NULL)
                smtp_client_command_abort(&trans->cmd_plug);
 
+       /* Free any approved recipients early */
+       while (trans->rcpts_count > 0) {
+               struct smtp_client_transaction_rcpt *rcpt =
+                       trans->rcpts_head;
+               smtp_client_transaction_rcpt_free(&rcpt);
+       }
+
        if (trans->state < SMTP_CLIENT_TRANSACTION_STATE_FINISHED) {
                struct smtp_client_transaction *trans_tmp = trans;
 
@@ -504,7 +551,7 @@ void smtp_client_transaction_fail_reply(struct smtp_client_transaction *trans,
                for (rcpt = trans->rcpts_data; rcpt != NULL;
                     rcpt = rcpt->next) {
                        if (rcpt->data_callback != NULL) {
-                               rcpt->data_callback(reply, rcpt->context);
+                               rcpt->data_callback(reply, rcpt->data_context);
                        }
                        rcpt->data_callback = NULL;
                }
@@ -759,6 +806,7 @@ smtp_client_transaction_add_rcpt(struct smtp_client_transaction *trans,
                                 void *context)
 {
        struct smtp_client_transaction_rcpt *rcpt;
+       pool_t pool;
 
        smtp_client_transaction_debug(trans, "Add recipient");
 
@@ -771,10 +819,48 @@ smtp_client_transaction_add_rcpt(struct smtp_client_transaction *trans,
                trans->state == SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM)
                trans->state = SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO;
 
-       rcpt = smtp_client_transaction_rcpt_new(trans, rcpt_to, rcpt_params);
+       pool = pool_alloconly_create("smtp transaction rcpt", 512);
+       rcpt = smtp_client_transaction_rcpt_new(trans, pool,
+                                               rcpt_to, rcpt_params);
+       pool_unref(&pool);
+
        rcpt->rcpt_callback = rcpt_callback;
+       rcpt->context = context;
+
        rcpt->data_callback = data_callback;
+       rcpt->data_context = context;
+
+       smtp_client_transaction_submit(trans, FALSE);
+
+       return rcpt;
+}
+
+#undef smtp_client_transaction_add_pool_rcpt
+struct smtp_client_transaction_rcpt *
+smtp_client_transaction_add_pool_rcpt(
+       struct smtp_client_transaction *trans, pool_t pool,
+       const struct smtp_address *rcpt_to,
+       const struct smtp_params_rcpt *rcpt_params,
+       smtp_client_command_callback_t *rcpt_callback, void *context)
+{
+       struct smtp_client_transaction_rcpt *rcpt;
+
+       smtp_client_transaction_debug(trans, "Add recipient (external pool)");
+
+       i_assert(!trans->data_provided);
+       i_assert(!trans->reset);
+
+       i_assert(trans->state < SMTP_CLIENT_TRANSACTION_STATE_FINISHED);
+
+       if (trans->mail_head == NULL &&
+               trans->state == SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM)
+               trans->state = SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO;
+
+       rcpt = smtp_client_transaction_rcpt_new(trans, pool,
+                                               rcpt_to, rcpt_params);
+       rcpt->rcpt_callback = rcpt_callback;
        rcpt->context = context;
+       rcpt->external_pool = TRUE;
 
        smtp_client_transaction_submit(trans, FALSE);
 
@@ -802,7 +888,7 @@ smtp_client_transaction_data_cb(const struct smtp_reply *reply,
 
                trans->rcpts_data = trans->rcpts_data->next;
                if (rcpt->data_callback != NULL)
-                       rcpt->data_callback(reply, rcpt->context);
+                       rcpt->data_callback(reply, rcpt->data_context);
                rcpt->data_callback = NULL;
                if (conn->protocol == SMTP_PROTOCOL_LMTP)
                        break;
index a6920d4898d530020193c5f62dd46fbf08da99a5..d5f88ff2d46426a51a58bd542642ac22c8fbd01c 100644 (file)
@@ -155,10 +155,46 @@ smtp_client_transaction_add_rcpt(struct smtp_client_transaction *trans,
                rcpt_params, \
                (smtp_client_command_callback_t *)rcpt_callback, \
                (smtp_client_command_callback_t *)data_callback, context)
+/* Add recipient to the transaction with a RCPT TO command. The
+   rcpt_to_callback is called once the server replies to the RCPT TO command.
+   This function returns a struct that can be used to abort the RCPT command
+   prematurely (see below). This struct is allocated on the provided pool (the
+   pool is referenced) and remains valid until the destruction of the
+   transaction.
+ */
+struct smtp_client_transaction_rcpt *
+smtp_client_transaction_add_pool_rcpt(
+       struct smtp_client_transaction *trans, pool_t pool,
+       const struct smtp_address *rcpt_to,
+       const struct smtp_params_rcpt *rcpt_params,
+       smtp_client_command_callback_t *rcpt_callback, void *context)
+       ATTR_NOWARN_UNUSED_RESULT ATTR_NULL(4,6,7);
+#define smtp_client_transaction_add_pool_rcpt(trans, pool, \
+               rcpt_to, rcpt_params, rcpt_callback, context) \
+       smtp_client_transaction_add_pool_rcpt(trans, pool, rcpt_to + \
+               CALLBACK_TYPECHECK(rcpt_callback, void (*)( \
+                       const struct smtp_reply *reply, typeof(context))), \
+               rcpt_params, \
+               (smtp_client_command_callback_t *)rcpt_callback, context)
 /* Abort the RCPT command prematurely. This function must not be called after
    the rcpt_callback from smtp_client_transaction_add_rcpt() is called. */
 void smtp_client_transaction_rcpt_abort(
        struct smtp_client_transaction_rcpt **_rcpt);
+/* Set the DATA callback for this recipient. If RCPT TO succeeded, the callback
+   is called once the server replies to the DATA command. Until that time, any
+   failure is remembered. The callback will not be called until
+   smtp_client_transaction_send() is called for the transaction (see below). */
+void smtp_client_transaction_rcpt_set_data_callback(
+       struct smtp_client_transaction_rcpt *rcpt,
+       smtp_client_command_callback_t *callback, void *context)
+       ATTR_NULL(3);
+#define smtp_client_transaction_rcpt_set_data_callback(trans, \
+                                                      callback, context) \
+       smtp_client_transaction_rcpt_set_data_callback(trans, \
+               (smtp_client_command_callback_t *)callback, \
+               (TRUE ? context : \
+                CALLBACK_TYPECHECK(callback, void (*)( \
+                       const struct smtp_reply *reply, typeof(context)))))
 
 /* Start sending input stream as DATA. This completes the transaction, which
    means that any pending failures that got recorded before this function was