1 #ifndef SMTP_CLIENT_TRANSACTION_H
2 #define SMTP_CLIENT_TRANSACTION_H
3 
4 #include "net.h"
5 #include "istream.h"
6 
7 struct smtp_address;
8 struct smtp_client_transaction;
9 struct smtp_client_transaction_mail;
10 struct smtp_client_transaction_rcpt;
11 
12 enum smtp_client_transaction_flags {
13 	SMTP_CLIENT_TRANSACTION_FLAG_REPLY_PER_RCPT = BIT(0),
14 };
15 
16 enum smtp_client_transaction_state {
17 	SMTP_CLIENT_TRANSACTION_STATE_NEW = 0,
18 	SMTP_CLIENT_TRANSACTION_STATE_PENDING,
19 	SMTP_CLIENT_TRANSACTION_STATE_MAIL_FROM,
20 	SMTP_CLIENT_TRANSACTION_STATE_RCPT_TO,
21 	SMTP_CLIENT_TRANSACTION_STATE_DATA,
22 	SMTP_CLIENT_TRANSACTION_STATE_RESET,
23 	SMTP_CLIENT_TRANSACTION_STATE_FINISHED,
24 	SMTP_CLIENT_TRANSACTION_STATE_ABORTED
25 };
26 extern const char *const smtp_client_transaction_state_names[];
27 
28 struct smtp_client_transaction_times {
29 	struct timeval started;
30 	struct timeval finished;
31 };
32 
33 /* Called when the transaction is finished, either because the MAIL FROM
34    failed, all RCPT TOs failed or because all DATA replies have been
35    received. */
36 typedef void
37 smtp_client_transaction_callback_t(void *context);
38 
39 /* Create an empty transaction (i.e. even without the parameters for the
40    MAIL FROM command) */
41 struct smtp_client_transaction *
42 smtp_client_transaction_create_empty(
43 	struct smtp_client_connection *conn,
44 	enum smtp_client_transaction_flags flags,
45 	smtp_client_transaction_callback_t *callback, void *context)
46 	ATTR_NULL(4);
47 #define smtp_client_transaction_create_empty(conn, flags, callback, context) \
48 	smtp_client_transaction_create_empty(conn, flags - \
49 		CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \
50 		(smtp_client_transaction_callback_t *)callback, context)
51 /* Create a new transaction, including the parameters for the MAIL FROM
52    command */
53 struct smtp_client_transaction *
54 smtp_client_transaction_create(struct smtp_client_connection *conn,
55 		const struct smtp_address *mail_from,
56 		const struct smtp_params_mail *mail_params,
57 		enum smtp_client_transaction_flags flags,
58 		smtp_client_transaction_callback_t *callback, void *context)
59 		ATTR_NULL(2, 3, 6);
60 #define smtp_client_transaction_create(conn, \
61 		mail_from, mail_params, flags, callback, context) \
62 	smtp_client_transaction_create(conn, mail_from, mail_params, flags - \
63 		CALLBACK_TYPECHECK(callback, void (*)(typeof(context))), \
64 		(smtp_client_transaction_callback_t *)callback, context)
65 
66 void smtp_client_transaction_ref(struct smtp_client_transaction *trans);
67 void smtp_client_transaction_unref(struct smtp_client_transaction **_trans);
68 void smtp_client_transaction_destroy(struct smtp_client_transaction **trans);
69 
70 void smtp_client_transaction_abort(struct smtp_client_transaction *trans);
71 void smtp_client_transaction_fail_reply(struct smtp_client_transaction *trans,
72 	const struct smtp_reply *reply);
73 void smtp_client_transaction_fail(struct smtp_client_transaction *trans,
74 	unsigned int status, const char *error);
75 
76 void smtp_client_transaction_set_event(struct smtp_client_transaction *trans,
77 				       struct event *event);
78 void smtp_client_transaction_set_timeout(struct smtp_client_transaction *trans,
79 	unsigned int timeout_msecs);
80 
81 /* Start the transaction with a MAIL command. The mail_from_callback is
82    called once the server replies to the MAIL FROM command. Calling this
83    function is not mandatory; it is called implicitly by
84    smtp_client_transaction_send() if the transaction wasn't already started.
85  */
86 void smtp_client_transaction_start(struct smtp_client_transaction *trans,
87 	smtp_client_command_callback_t *mail_callback, void *context);
88 #define smtp_client_transaction_start(trans, mail_callback, context) \
89 	smtp_client_transaction_start(trans, \
90 		(smtp_client_command_callback_t *)mail_callback, TRUE ? context : \
91 		CALLBACK_TYPECHECK(mail_callback, void (*)( \
92 			const struct smtp_reply *reply, typeof(context))))
93 /* Start the transaction with a MAIL command. This function allows providing the
94    parameters for the MAIL FROM command for when the transaction was created
95    empty. The mail_from_callback is called once the server replies to the MAIL
96    FROM command. Calling this function is not mandatory; it is called implicitly
97    by smtp_client_transaction_send() if the transaction wasn't already started.
98    In that case, the NULL sender ("<>") will be used when the transaction was
99    created empty.
100  */
101 void smtp_client_transaction_start_empty(
102 	struct smtp_client_transaction *trans,
103 	const struct smtp_address *mail_from,
104 	const struct smtp_params_mail *mail_params,
105 	smtp_client_command_callback_t *mail_callback, void *context);
106 #define smtp_client_transaction_start_empty(trans, mail_from, mail_params, \
107 					    mail_callback, context) \
108 	smtp_client_transaction_start_empty(trans, mail_from, mail_params, \
109 		(smtp_client_command_callback_t *)mail_callback, TRUE ? context : \
110 		CALLBACK_TYPECHECK(mail_callback, void (*)( \
111 			const struct smtp_reply *reply, typeof(context))))
112 
113 /* Add an extra pipelined MAIL command to the transaction. The mail_callback is
114    called once the server replies to the MAIL command. This is usually only
115    useful for forwarding pipelined SMTP transactions, which can involve more
116    than a single MAIL command (e.g. to have an implicit fallback sender address
117    in the pipeline when the first one fails). Of course, only one MAIL command
118    will succeed and therefore error replies for the others will not abort the
119    transaction. This function returns a struct that can be used to abort the
120    MAIL command prematurely (see below). */
121 struct smtp_client_transaction_mail *
122 smtp_client_transaction_add_mail(struct smtp_client_transaction *trans,
123 				 const struct smtp_address *mail_from,
124 				 const struct smtp_params_mail *mail_params,
125 				 smtp_client_command_callback_t *mail_callback,
126 				 void *context)
127 	ATTR_NOWARN_UNUSED_RESULT ATTR_NULL(3,5);
128 #define smtp_client_transaction_add_mail(trans, \
129 		mail_from, mail_params, mail_callback, context) \
130 	smtp_client_transaction_add_mail(trans, mail_from - \
131 		CALLBACK_TYPECHECK(mail_callback, void (*)( \
132 			const struct smtp_reply *reply, typeof(context))), \
133 		mail_params, \
134 		(smtp_client_command_callback_t *)mail_callback, context)
135 /* Abort the MAIL command prematurely. This function must not be called after
136    the mail_callback from smtp_client_transaction_add_mail() is called. */
137 void smtp_client_transaction_mail_abort(
138 	struct smtp_client_transaction_mail **_mail);
139 
140 /* Add recipient to the transaction with a RCPT TO command. The
141    rcpt_to_callback is called once the server replies to the RCPT TO command.
142    If RCPT TO succeeded, the data_callback is called once the server replies
143    to the DATA command. The data_callback will not be called until
144    smtp_client_transaction_send() is called for the transaction (see below).
145    Until that time, any failure is remembered. This function returns a struct
146    that can be used to abort the RCPT command prematurely (see below). This
147    struct must not be used after the rcpt_callback is called. */
148 struct smtp_client_transaction_rcpt *
149 smtp_client_transaction_add_rcpt(struct smtp_client_transaction *trans,
150 				 const struct smtp_address *rcpt_to,
151 				 const struct smtp_params_rcpt *rcpt_params,
152 				 smtp_client_command_callback_t *rcpt_callback,
153 				 smtp_client_command_callback_t *data_callback,
154 				 void *context)
155 	ATTR_NOWARN_UNUSED_RESULT ATTR_NULL(3,5,6);
156 #define smtp_client_transaction_add_rcpt(trans, \
157 		rcpt_to, rcpt_params, rcpt_callback, data_callback, context) \
158 	smtp_client_transaction_add_rcpt(trans, rcpt_to - \
159 		CALLBACK_TYPECHECK(rcpt_callback, void (*)( \
160 			const struct smtp_reply *reply, typeof(context))) - \
161 		CALLBACK_TYPECHECK(data_callback, void (*)( \
162 			const struct smtp_reply *reply, typeof(context))), \
163 		rcpt_params, \
164 		(smtp_client_command_callback_t *)rcpt_callback, \
165 		(smtp_client_command_callback_t *)data_callback, context)
166 /* Add recipient to the transaction with a RCPT TO command. The
167    rcpt_to_callback is called once the server replies to the RCPT TO command.
168    This function returns a struct that can be used to abort the RCPT command
169    prematurely (see below). This struct is allocated on the provided pool (the
170    pool is referenced) and remains valid until the destruction of the
171    transaction.
172  */
173 struct smtp_client_transaction_rcpt *
174 smtp_client_transaction_add_pool_rcpt(
175 	struct smtp_client_transaction *trans, pool_t pool,
176 	const struct smtp_address *rcpt_to,
177 	const struct smtp_params_rcpt *rcpt_params,
178 	smtp_client_command_callback_t *rcpt_callback, void *context)
179 	ATTR_NOWARN_UNUSED_RESULT ATTR_NULL(4,6,7);
180 #define smtp_client_transaction_add_pool_rcpt(trans, pool, \
181 		rcpt_to, rcpt_params, rcpt_callback, context) \
182 	smtp_client_transaction_add_pool_rcpt(trans, pool, rcpt_to - \
183 		CALLBACK_TYPECHECK(rcpt_callback, void (*)( \
184 			const struct smtp_reply *reply, typeof(context))), \
185 		rcpt_params, \
186 		(smtp_client_command_callback_t *)rcpt_callback, context)
187 /* Abort the RCPT command prematurely. This function must not be called after
188    the rcpt_callback from smtp_client_transaction_add_rcpt() is called. */
189 void smtp_client_transaction_rcpt_abort(
190 	struct smtp_client_transaction_rcpt **_rcpt);
191 /* Set the DATA callback for this recipient. If RCPT TO succeeded, the callback
192    is called once the server replies to the DATA command. Until that time, any
193    failure is remembered. The callback will not be called until
194    smtp_client_transaction_send() is called for the transaction (see below). */
195 void smtp_client_transaction_rcpt_set_data_callback(
196 	struct smtp_client_transaction_rcpt *rcpt,
197 	smtp_client_command_callback_t *callback, void *context)
198 	ATTR_NULL(3);
199 #define smtp_client_transaction_rcpt_set_data_callback(trans, \
200 						       callback, context) \
201 	smtp_client_transaction_rcpt_set_data_callback(trans, \
202 		(smtp_client_command_callback_t *)callback, \
203 		(TRUE ? context : \
204 		 CALLBACK_TYPECHECK(callback, void (*)( \
205 			const struct smtp_reply *reply, typeof(context)))))
206 
207 /* Start sending input stream as DATA. This completes the transaction, which
208    means that any pending failures that got recorded before this function was
209    called will be triggered now. If any RCPT TO succeeded, the provided
210    data_callback is called once the server replies to the DATA command. This
211    callback is mainly useful for SMTP, for LMTP it will only yield the reply for
212    the last recipient. This function starts the transaction implicitly. */
213 void smtp_client_transaction_send(
214 	struct smtp_client_transaction *trans, struct istream *data_input,
215 	smtp_client_command_callback_t *data_callback, void *data_context);
216 #define smtp_client_transaction_send(trans, \
217 		data_input, data_callback, data_context) \
218 	smtp_client_transaction_send(trans, data_input - \
219 		CALLBACK_TYPECHECK(data_callback, void (*)( \
220 			const struct smtp_reply *reply, typeof(data_context))), \
221 		(smtp_client_command_callback_t *)data_callback, data_context)
222 
223 /* Gracefully reset the transaction by sending the RSET command and waiting for
224    the response. This does not try to abort pending MAIL and RCPT commands,
225    allowing the transaction to be evaluated without proceeding with the DATA
226    command. */
227 void smtp_client_transaction_reset(
228 	struct smtp_client_transaction *trans,
229 	smtp_client_command_callback_t *reset_callback, void *reset_context);
230 #define smtp_client_transaction_reset(trans, reset_callback, reset_context) \
231 	smtp_client_transaction_reset(trans, \
232 		(smtp_client_command_callback_t *)reset_callback, \
233 		TRUE ? reset_context : \
234 		CALLBACK_TYPECHECK(reset_callback, void (*)( \
235 			const struct smtp_reply *reply, typeof(reset_context))))
236 
237 /* Enables mode in which all commands are submitted immediately and (non-
238    transaction) commands can be interleaved. This is mainly important for
239    relaying SMTP in realtime. */
240 void smtp_client_transaction_set_immediate(
241 	struct smtp_client_transaction *trans, bool immediate);
242 
243 /* Return transaction statistics. */
244 const struct smtp_client_transaction_times *
245 smtp_client_transaction_get_times(struct smtp_client_transaction *trans);
246 
247 /* Return transaction state */
248 enum smtp_client_transaction_state
249 smtp_client_transaction_get_state(struct smtp_client_transaction *trans)
250 	ATTR_PURE;
251 const char *
252 smtp_client_transaction_get_state_name(struct smtp_client_transaction *trans)
253 	ATTR_PURE;
254 const char *
255 smtp_client_transaction_get_state_destription(
256 	struct smtp_client_transaction *trans);
257 
258 #endif
259