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;
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)
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;
}
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);
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
*/
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);
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;
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;
}
void *context)
{
struct smtp_client_transaction_rcpt *rcpt;
+ pool_t pool;
smtp_client_transaction_debug(trans, "Add recipient");
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);
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;
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