s3:libsmb: get rid of cli_has_async_calls
[bbaumbach/samba-autobuild/.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 #include "../lib/util/tevent_ntstatus.h"
22 #include "async_smb.h"
23 #include "libsmb/libsmb.h"
24 #include "../libcli/smb/smbXcli_base.h"
25
26 struct cli_message_start_state {
27         uint16_t grp;
28 };
29
30 static void cli_message_start_done(struct tevent_req *subreq);
31
32 static struct tevent_req *cli_message_start_send(TALLOC_CTX *mem_ctx,
33                                                  struct tevent_context *ev,
34                                                  struct cli_state *cli,
35                                                  const char *host,
36                                                  const char *username)
37 {
38         struct tevent_req *req, *subreq;
39         struct cli_message_start_state *state;
40         char *htmp = NULL;
41         char *utmp = NULL;
42         size_t hlen, ulen;
43         uint8_t *bytes, *p;
44
45         req = tevent_req_create(mem_ctx, &state,
46                                 struct cli_message_start_state);
47         if (req == NULL) {
48                 return NULL;
49         }
50
51         if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
52                                    username, strlen(username)+1,
53                                    &utmp, &ulen)) {
54                 goto fail;
55         }
56         if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
57                                    host, strlen(host)+1,
58                                    &htmp, &hlen)) {
59                 goto fail;
60         }
61
62         bytes = talloc_array(state, uint8_t, ulen+hlen+2);
63         if (bytes == NULL) {
64                 goto fail;
65         }
66         p = bytes;
67
68         *p++ = 4;
69         memcpy(p, utmp, ulen);
70         p += ulen;
71         *p++ = 4;
72         memcpy(p, htmp, hlen);
73         p += hlen;
74         TALLOC_FREE(htmp);
75         TALLOC_FREE(utmp);
76
77         subreq = cli_smb_send(state, ev, cli, SMBsendstrt, 0, 0, NULL,
78                               talloc_get_size(bytes), bytes);
79         if (tevent_req_nomem(subreq, req)) {
80                 return tevent_req_post(req, ev);
81         }
82         tevent_req_set_callback(subreq, cli_message_start_done, req);
83         return req;
84 fail:
85         TALLOC_FREE(htmp);
86         TALLOC_FREE(utmp);
87         tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
88         return tevent_req_post(req, ev);
89 }
90
91 static void cli_message_start_done(struct tevent_req *subreq)
92 {
93         struct tevent_req *req = tevent_req_callback_data(
94                 subreq, struct tevent_req);
95         struct cli_message_start_state *state = tevent_req_data(
96                 req, struct cli_message_start_state);
97         NTSTATUS status;
98         uint8_t wct;
99         uint16_t *vwv;
100         uint8_t *inbuf;
101
102         status = cli_smb_recv(subreq, state, &inbuf, 0, &wct, &vwv,
103                               NULL, NULL);
104         TALLOC_FREE(subreq);
105         if (!NT_STATUS_IS_OK(status)) {
106                 TALLOC_FREE(subreq);
107                 tevent_req_nterror(req, status);
108                 return;
109         }
110         if (wct >= 1) {
111                 state->grp = SVAL(vwv+0, 0);
112         } else {
113                 state->grp = 0;
114         }
115         tevent_req_done(req);
116 }
117
118 static NTSTATUS cli_message_start_recv(struct tevent_req *req,
119                                        uint16_t *pgrp)
120 {
121         struct cli_message_start_state *state = tevent_req_data(
122                 req, struct cli_message_start_state);
123         NTSTATUS status;
124
125         if (tevent_req_is_nterror(req, &status)) {
126                 return status;
127         }
128         *pgrp = state->grp;
129         return NT_STATUS_OK;
130 }
131
132 struct cli_message_text_state {
133         uint16_t vwv;
134 };
135
136 static void cli_message_text_done(struct tevent_req *subreq);
137
138 static struct tevent_req *cli_message_text_send(TALLOC_CTX *mem_ctx,
139                                                 struct tevent_context *ev,
140                                                 struct cli_state *cli,
141                                                 uint16_t grp,
142                                                 const char *msg,
143                                                 int msglen)
144 {
145         struct tevent_req *req, *subreq;
146         struct cli_message_text_state *state;
147         char *tmp;
148         size_t tmplen;
149         uint8_t *bytes;
150
151         req = tevent_req_create(mem_ctx, &state,
152                                 struct cli_message_text_state);
153         if (req == NULL) {
154                 return NULL;
155         }
156
157         SSVAL(&state->vwv, 0, grp);
158
159         if (convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, msg, msglen,
160                                   &tmp, &tmplen)) {
161                 msg = tmp;
162                 msglen = tmplen;
163         } else {
164                 DEBUG(3, ("Conversion failed, sending message in UNIX "
165                           "charset\n"));
166                 tmp = NULL;
167         }
168
169         bytes = talloc_array(state, uint8_t, msglen+3);
170         if (tevent_req_nomem(bytes, req)) {
171                 TALLOC_FREE(tmp);
172                 return tevent_req_post(req, ev);
173         }
174         SCVAL(bytes, 0, 1);     /* pad */
175         SSVAL(bytes+1, 0, msglen);
176         memcpy(bytes+3, msg, msglen);
177         TALLOC_FREE(tmp);
178
179         subreq = cli_smb_send(state, ev, cli, SMBsendtxt, 0, 1, &state->vwv,
180                               talloc_get_size(bytes), bytes);
181         if (tevent_req_nomem(subreq, req)) {
182                 return tevent_req_post(req, ev);
183         }
184         tevent_req_set_callback(subreq, cli_message_text_done, req);
185         return req;
186 }
187
188 static void cli_message_text_done(struct tevent_req *subreq)
189 {
190         struct tevent_req *req = tevent_req_callback_data(
191                 subreq, struct tevent_req);
192         NTSTATUS status;
193
194         status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
195         TALLOC_FREE(subreq);
196         if (!NT_STATUS_IS_OK(status)) {
197                 tevent_req_nterror(req, status);
198                 return;
199         }
200         tevent_req_done(req);
201 }
202
203 static NTSTATUS cli_message_text_recv(struct tevent_req *req)
204 {
205         return tevent_req_simple_recv_ntstatus(req);
206 }
207
208 struct cli_message_end_state {
209         uint16_t vwv;
210 };
211
212 static void cli_message_end_done(struct tevent_req *subreq);
213
214 static struct tevent_req *cli_message_end_send(TALLOC_CTX *mem_ctx,
215                                                 struct tevent_context *ev,
216                                                 struct cli_state *cli,
217                                                 uint16_t grp)
218 {
219         struct tevent_req *req, *subreq;
220         struct cli_message_end_state *state;
221
222         req = tevent_req_create(mem_ctx, &state,
223                                 struct cli_message_end_state);
224         if (req == NULL) {
225                 return NULL;
226         }
227
228         SSVAL(&state->vwv, 0, grp);
229
230         subreq = cli_smb_send(state, ev, cli, SMBsendend, 0, 1, &state->vwv,
231                               0, NULL);
232         if (tevent_req_nomem(subreq, req)) {
233                 return tevent_req_post(req, ev);
234         }
235         tevent_req_set_callback(subreq, cli_message_end_done, req);
236         return req;
237 }
238
239 static void cli_message_end_done(struct tevent_req *subreq)
240 {
241         struct tevent_req *req = tevent_req_callback_data(
242                 subreq, struct tevent_req);
243         NTSTATUS status;
244
245         status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
246         TALLOC_FREE(subreq);
247         if (!NT_STATUS_IS_OK(status)) {
248                 tevent_req_nterror(req, status);
249                 return;
250         }
251         tevent_req_done(req);
252 }
253
254 static NTSTATUS cli_message_end_recv(struct tevent_req *req)
255 {
256         return tevent_req_simple_recv_ntstatus(req);
257 }
258
259 struct cli_message_state {
260         struct tevent_context *ev;
261         struct cli_state *cli;
262         size_t sent;
263         const char *message;
264         uint16_t grp;
265 };
266
267 static void cli_message_started(struct tevent_req *subreq);
268 static void cli_message_sent(struct tevent_req *subreq);
269 static void cli_message_done(struct tevent_req *subreq);
270
271 struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx,
272                                     struct tevent_context *ev,
273                                     struct cli_state *cli,
274                                     const char *host, const char *username,
275                                     const char *message)
276 {
277         struct tevent_req *req, *subreq;
278         struct cli_message_state *state;
279
280         req = tevent_req_create(mem_ctx, &state, struct cli_message_state);
281         if (req == NULL) {
282                 return NULL;
283         }
284         state->ev = ev;
285         state->cli = cli;
286         state->sent = 0;
287         state->message = message;
288
289         subreq = cli_message_start_send(state, ev, cli, host, username);
290         if (tevent_req_nomem(subreq, req)) {
291                 return tevent_req_post(req, ev);
292         }
293         tevent_req_set_callback(subreq, cli_message_started, req);
294         return req;
295 }
296
297 static void cli_message_started(struct tevent_req *subreq)
298 {
299         struct tevent_req *req = tevent_req_callback_data(
300                 subreq, struct tevent_req);
301         struct cli_message_state *state = tevent_req_data(
302                 req, struct cli_message_state);
303         NTSTATUS status;
304         size_t thistime;
305
306         status = cli_message_start_recv(subreq, &state->grp);
307         TALLOC_FREE(subreq);
308         if (!NT_STATUS_IS_OK(status)) {
309                 tevent_req_nterror(req, status);
310                 return;
311         }
312
313         thistime = MIN(127, strlen(state->message));
314
315         subreq = cli_message_text_send(state, state->ev, state->cli,
316                                        state->grp, state->message, thistime);
317         if (tevent_req_nomem(subreq, req)) {
318                 return;
319         }
320         state->sent += thistime;
321         tevent_req_set_callback(subreq, cli_message_sent, req);
322 }
323
324 static void cli_message_sent(struct tevent_req *subreq)
325 {
326         struct tevent_req *req = tevent_req_callback_data(
327                 subreq, struct tevent_req);
328         struct cli_message_state *state = tevent_req_data(
329                 req, struct cli_message_state);
330         NTSTATUS status;
331         size_t left, thistime;
332
333         status = cli_message_text_recv(subreq);
334         TALLOC_FREE(subreq);
335         if (!NT_STATUS_IS_OK(status)) {
336                 tevent_req_nterror(req, status);
337                 return;
338         }
339
340         if (state->sent >= strlen(state->message)) {
341                 subreq = cli_message_end_send(state, state->ev, state->cli,
342                                               state->grp);
343                 if (tevent_req_nomem(subreq, req)) {
344                         return;
345                 }
346                 tevent_req_set_callback(subreq, cli_message_done, req);
347                 return;
348         }
349
350         left = strlen(state->message) - state->sent;
351         thistime = MIN(127, left);
352
353         subreq = cli_message_text_send(state, state->ev, state->cli,
354                                        state->grp,
355                                        state->message + state->sent,
356                                        thistime);
357         if (tevent_req_nomem(subreq, req)) {
358                 return;
359         }
360         state->sent += thistime;
361         tevent_req_set_callback(subreq, cli_message_sent, req);
362 }
363
364 static void cli_message_done(struct tevent_req *subreq)
365 {
366         struct tevent_req *req = tevent_req_callback_data(
367                 subreq, struct tevent_req);
368         NTSTATUS status;
369
370         status = cli_message_end_recv(subreq);
371         TALLOC_FREE(subreq);
372         if (!NT_STATUS_IS_OK(status)) {
373                 tevent_req_nterror(req, status);
374                 return;
375         }
376         tevent_req_done(req);
377 }
378
379 NTSTATUS cli_message_recv(struct tevent_req *req)
380 {
381         return tevent_req_simple_recv_ntstatus(req);
382 }
383
384 NTSTATUS cli_message(struct cli_state *cli, const char *host,
385                      const char *username, const char *message)
386 {
387         TALLOC_CTX *frame = talloc_stackframe();
388         struct event_context *ev;
389         struct tevent_req *req;
390         NTSTATUS status = NT_STATUS_OK;
391
392         if (smbXcli_conn_has_async_calls(cli->conn)) {
393                 /*
394                  * Can't use sync call while an async call is in flight
395                  */
396                 status = NT_STATUS_INVALID_PARAMETER;
397                 goto fail;
398         }
399
400         ev = event_context_init(frame);
401         if (ev == NULL) {
402                 status = NT_STATUS_NO_MEMORY;
403                 goto fail;
404         }
405
406         req = cli_message_send(frame, ev, cli, host, username, message);
407         if (req == NULL) {
408                 status = NT_STATUS_NO_MEMORY;
409                 goto fail;
410         }
411
412         if (!tevent_req_poll(req, ev)) {
413                 status = map_nt_error_from_unix(errno);
414                 goto fail;
415         }
416
417         status = cli_message_recv(req);
418  fail:
419         TALLOC_FREE(frame);
420         return status;
421 }