s3: Convert libsmb/cli_message to the async API
[kai/samba.git] / source3 / libsmb / climessage.c
1 /*
2    Unix SMB/CIFS implementation.
3    client message handling routines
4    Copyright (C) Andrew Tridgell 1994-1998
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21
22 struct cli_message_start_state {
23         uint16_t grp;
24 };
25
26 static void cli_message_start_done(struct tevent_req *subreq);
27
28 static struct tevent_req *cli_message_start_send(TALLOC_CTX *mem_ctx,
29                                                  struct tevent_context *ev,
30                                                  struct cli_state *cli,
31                                                  const char *host,
32                                                  const char *username)
33 {
34         struct tevent_req *req, *subreq;
35         struct cli_message_start_state *state;
36         char *htmp = NULL;
37         char *utmp = NULL;
38         size_t hlen, ulen;
39         uint8_t *bytes, *p;
40
41         req = tevent_req_create(mem_ctx, &state,
42                                 struct cli_message_start_state);
43         if (req == NULL) {
44                 return NULL;
45         }
46
47         if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
48                                    username, strlen(username)+1,
49                                    &utmp, &ulen, true)) {
50                 goto fail;
51         }
52         if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
53                                    host, strlen(host)+1,
54                                    &htmp, &hlen, true)) {
55                 goto fail;
56         }
57
58         bytes = talloc_array(state, uint8_t, ulen+hlen+2);
59         if (bytes == NULL) {
60                 goto fail;
61         }
62         p = bytes;
63
64         *p++ = 4;
65         memcpy(p, utmp, ulen);
66         *p++ = 4;
67         memcpy(p, htmp, hlen);
68         TALLOC_FREE(htmp);
69         TALLOC_FREE(utmp);
70
71         subreq = cli_smb_send(state, ev, cli, SMBsendstrt, 0, 0, NULL,
72                               talloc_get_size(bytes), bytes);
73         if (tevent_req_nomem(subreq, req)) {
74                 return tevent_req_post(req, ev);
75         }
76         tevent_req_set_callback(subreq, cli_message_start_done, req);
77         return req;
78 fail:
79         TALLOC_FREE(htmp);
80         TALLOC_FREE(utmp);
81         tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
82         return tevent_req_post(req, ev);
83 }
84
85 static void cli_message_start_done(struct tevent_req *subreq)
86 {
87         struct tevent_req *req = tevent_req_callback_data(
88                 subreq, struct tevent_req);
89         struct cli_message_start_state *state = tevent_req_data(
90                 req, struct cli_message_start_state);
91         NTSTATUS status;
92         uint8_t wct;
93         uint16_t *vwv;
94
95         status = cli_smb_recv(subreq, 0, &wct, &vwv, NULL, NULL);
96         if (!NT_STATUS_IS_OK(status)) {
97                 TALLOC_FREE(subreq);
98                 tevent_req_nterror(req, status);
99                 return;
100         }
101         if (wct >= 1) {
102                 state->grp = SVAL(vwv+0, 0);
103         } else {
104                 state->grp = 0;
105         }
106         TALLOC_FREE(subreq);
107         tevent_req_done(req);
108 }
109
110 static NTSTATUS cli_message_start_recv(struct tevent_req *req,
111                                        uint16_t *pgrp)
112 {
113         struct cli_message_start_state *state = tevent_req_data(
114                 req, struct cli_message_start_state);
115         NTSTATUS status;
116
117         if (tevent_req_is_nterror(req, &status)) {
118                 return status;
119         }
120         *pgrp = state->grp;
121         return NT_STATUS_OK;
122 }
123
124 struct cli_message_text_state {
125         uint16_t vwv;
126 };
127
128 static void cli_message_text_done(struct tevent_req *subreq);
129
130 static struct tevent_req *cli_message_text_send(TALLOC_CTX *mem_ctx,
131                                                 struct tevent_context *ev,
132                                                 struct cli_state *cli,
133                                                 uint16_t grp,
134                                                 const char *msg,
135                                                 int msglen)
136 {
137         struct tevent_req *req, *subreq;
138         struct cli_message_text_state *state;
139         char *tmp;
140         size_t tmplen;
141         uint8_t *bytes;
142
143         req = tevent_req_create(mem_ctx, &state,
144                                 struct cli_message_text_state);
145         if (req == NULL) {
146                 return NULL;
147         }
148
149         SSVAL(&state->vwv, 0, grp);
150
151         if (convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, msg, msglen,
152                                   &tmp, &tmplen, true)) {
153                 msg = tmp;
154                 msglen = tmplen;
155         } else {
156                 DEBUG(3, ("Conversion failed, sending message in UNIX "
157                           "charset\n"));
158                 tmp = NULL;
159         }
160
161         bytes = talloc_array(state, uint8_t, msglen+3);
162         if (tevent_req_nomem(bytes, req)) {
163                 TALLOC_FREE(tmp);
164                 return tevent_req_post(req, ev);
165         }
166         SCVAL(bytes, 0, 0);     /* pad */
167         SSVAL(bytes, 1, msglen);
168         memcpy(bytes+3, msg, msglen);
169         TALLOC_FREE(tmp);
170
171         subreq = cli_smb_send(state, ev, cli, SMBsendtxt, 0, 1, &state->vwv,
172                               talloc_get_size(bytes), bytes);
173         if (tevent_req_nomem(subreq, req)) {
174                 return tevent_req_post(req, ev);
175         }
176         tevent_req_set_callback(subreq, cli_message_text_done, req);
177         return req;
178 }
179
180 static void cli_message_text_done(struct tevent_req *subreq)
181 {
182         struct tevent_req *req = tevent_req_callback_data(
183                 subreq, struct tevent_req);
184         NTSTATUS status;
185
186         status = cli_smb_recv(subreq, 0, NULL, NULL, NULL, NULL);
187         TALLOC_FREE(subreq);
188         if (!NT_STATUS_IS_OK(status)) {
189                 tevent_req_nterror(req, status);
190                 return;
191         }
192         tevent_req_done(req);
193 }
194
195 static NTSTATUS cli_message_text_recv(struct tevent_req *req)
196 {
197         return tevent_req_simple_recv_ntstatus(req);
198 }
199
200 struct cli_message_end_state {
201         uint16_t vwv;
202 };
203
204 static void cli_message_end_done(struct tevent_req *subreq);
205
206 static struct tevent_req *cli_message_end_send(TALLOC_CTX *mem_ctx,
207                                                 struct tevent_context *ev,
208                                                 struct cli_state *cli,
209                                                 uint16_t grp)
210 {
211         struct tevent_req *req, *subreq;
212         struct cli_message_end_state *state;
213
214         req = tevent_req_create(mem_ctx, &state,
215                                 struct cli_message_end_state);
216         if (req == NULL) {
217                 return NULL;
218         }
219
220         SSVAL(&state->vwv, 0, grp);
221
222         subreq = cli_smb_send(state, ev, cli, SMBsendend, 0, 1, &state->vwv,
223                               0, NULL);
224         if (tevent_req_nomem(subreq, req)) {
225                 return tevent_req_post(req, ev);
226         }
227         tevent_req_set_callback(subreq, cli_message_end_done, req);
228         return req;
229 }
230
231 static void cli_message_end_done(struct tevent_req *subreq)
232 {
233         struct tevent_req *req = tevent_req_callback_data(
234                 subreq, struct tevent_req);
235         NTSTATUS status;
236
237         status = cli_smb_recv(subreq, 0, NULL, NULL, NULL, NULL);
238         TALLOC_FREE(subreq);
239         if (!NT_STATUS_IS_OK(status)) {
240                 tevent_req_nterror(req, status);
241                 return;
242         }
243         tevent_req_done(req);
244 }
245
246 static NTSTATUS cli_message_end_recv(struct tevent_req *req)
247 {
248         return tevent_req_simple_recv_ntstatus(req);
249 }
250
251 struct cli_message_state {
252         struct tevent_context *ev;
253         struct cli_state *cli;
254         size_t sent;
255         const char *message;
256         uint16_t grp;
257 };
258
259 static void cli_message_started(struct tevent_req *subreq);
260 static void cli_message_sent(struct tevent_req *subreq);
261 static void cli_message_done(struct tevent_req *subreq);
262
263 struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx,
264                                     struct tevent_context *ev,
265                                     struct cli_state *cli,
266                                     const char *host, const char *username,
267                                     const char *message)
268 {
269         struct tevent_req *req, *subreq;
270         struct cli_message_state *state;
271
272         req = tevent_req_create(mem_ctx, &state, struct cli_message_state);
273         if (req == NULL) {
274                 return NULL;
275         }
276         state->ev = ev;
277         state->cli = cli;
278         state->sent = 0;
279         state->message = message;
280
281         subreq = cli_message_start_send(state, ev, cli, host, username);
282         if (tevent_req_nomem(subreq, req)) {
283                 return tevent_req_post(req, ev);
284         }
285         tevent_req_set_callback(subreq, cli_message_started, req);
286         return req;
287 }
288
289 static void cli_message_started(struct tevent_req *subreq)
290 {
291         struct tevent_req *req = tevent_req_callback_data(
292                 subreq, struct tevent_req);
293         struct cli_message_state *state = tevent_req_data(
294                 req, struct cli_message_state);
295         NTSTATUS status;
296         size_t thistime;
297
298         status = cli_message_start_recv(subreq, &state->grp);
299         TALLOC_FREE(subreq);
300         if (!NT_STATUS_IS_OK(status)) {
301                 tevent_req_nterror(req, status);
302                 return;
303         }
304
305         thistime = MIN(127, strlen(state->message));
306
307         subreq = cli_message_text_send(state, state->ev, state->cli,
308                                        state->grp, state->message, thistime);
309         if (tevent_req_nomem(subreq, req)) {
310                 return;
311         }
312         state->sent += thistime;
313         tevent_req_set_callback(subreq, cli_message_sent, req);
314 }
315
316 static void cli_message_sent(struct tevent_req *subreq)
317 {
318         struct tevent_req *req = tevent_req_callback_data(
319                 subreq, struct tevent_req);
320         struct cli_message_state *state = tevent_req_data(
321                 req, struct cli_message_state);
322         NTSTATUS status;
323         size_t left, thistime;
324
325         status = cli_message_text_recv(subreq);
326         TALLOC_FREE(subreq);
327         if (!NT_STATUS_IS_OK(status)) {
328                 tevent_req_nterror(req, status);
329                 return;
330         }
331
332         if (state->sent >= strlen(state->message)) {
333                 subreq = cli_message_end_send(state, state->ev, state->cli,
334                                               state->grp);
335                 if (tevent_req_nomem(subreq, req)) {
336                         return;
337                 }
338                 tevent_req_set_callback(subreq, cli_message_done, req);
339                 return;
340         }
341
342         left = strlen(state->message) - state->sent;
343         thistime = MIN(127, left);
344
345         subreq = cli_message_text_send(state, state->ev, state->cli,
346                                        state->grp,
347                                        state->message + state->sent,
348                                        thistime);
349         if (tevent_req_nomem(subreq, req)) {
350                 return;
351         }
352         state->sent += thistime;
353         tevent_req_set_callback(subreq, cli_message_sent, req);
354 }
355
356 static void cli_message_done(struct tevent_req *subreq)
357 {
358         struct tevent_req *req = tevent_req_callback_data(
359                 subreq, struct tevent_req);
360         NTSTATUS status;
361
362         status = cli_message_end_recv(subreq);
363         TALLOC_FREE(subreq);
364         if (!NT_STATUS_IS_OK(status)) {
365                 tevent_req_nterror(req, status);
366                 return;
367         }
368         tevent_req_done(req);
369 }
370
371 NTSTATUS cli_message_recv(struct tevent_req *req)
372 {
373         return tevent_req_simple_recv_ntstatus(req);
374 }
375
376 NTSTATUS cli_message(struct cli_state *cli, const char *host,
377                      const char *username, const char *message)
378 {
379         TALLOC_CTX *frame = talloc_stackframe();
380         struct event_context *ev;
381         struct tevent_req *req;
382         NTSTATUS status = NT_STATUS_OK;
383
384         if (cli_has_async_calls(cli)) {
385                 /*
386                  * Can't use sync call while an async call is in flight
387                  */
388                 status = NT_STATUS_INVALID_PARAMETER;
389                 goto fail;
390         }
391
392         ev = event_context_init(frame);
393         if (ev == NULL) {
394                 status = NT_STATUS_NO_MEMORY;
395                 goto fail;
396         }
397
398         req = cli_message_send(frame, ev, cli, host, username, message);
399         if (req == NULL) {
400                 status = NT_STATUS_NO_MEMORY;
401                 goto fail;
402         }
403
404         if (!tevent_req_poll(req, ev)) {
405                 status = map_nt_error_from_unix(errno);
406                 goto fail;
407         }
408
409         status = cli_message_recv(req);
410  fail:
411         TALLOC_FREE(frame);
412         return status;
413 }