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