s3: Convert libsmb/cli_message to the async API
authorVolker Lendecke <vl@samba.org>
Tue, 10 Nov 2009 18:49:41 +0000 (19:49 +0100)
committerVolker Lendecke <vl@samba.org>
Tue, 10 Nov 2009 22:48:22 +0000 (23:48 +0100)
source3/client/client.c
source3/include/proto.h
source3/libsmb/climessage.c

index 6b273b47b0bdaf1668a6e4f765992209dd5ce6e8..435ede904edaaf84371632fdbb29ade6cdcfd9b7 100644 (file)
@@ -243,51 +243,29 @@ static size_t push_source(uint8_t *buf, size_t n, void *priv)
 
 static void send_message(const char *username)
 {
-       int total_len = 0;
-       int grp_id;
-
-       if (!cli_message_start(cli, desthost, username, &grp_id)) {
-               d_printf("message start: %s\n", cli_errstr(cli));
-               return;
-       }
-
-
-       d_printf("Connected. Type your message, ending it with a Control-D\n");
-
-       while (!feof(stdin) && total_len < 1600) {
-               int maxlen = MIN(1600 - total_len,127);
-               char msg[1024];
-               int l=0;
-               int c;
-
-               ZERO_ARRAY(msg);
+       char buf[1600];
+       NTSTATUS status;
+       int i;
 
-               for (l=0;l<maxlen && (c=fgetc(stdin))!=EOF;l++) {
-                       if (c == '\n')
-                               msg[l++] = '\r';
-                       msg[l] = c;
-               }
+       d_printf("Type your message, ending it with a Control-D\n");
 
-               if ((total_len > 0) && (strlen(msg) == 0)) {
+       i = 0;
+       while (i<sizeof(buf)-2) {
+               int c = fgetc(stdin);
+               if (c == EOF) {
                        break;
                }
-
-               if (!cli_message_text(cli, msg, l, grp_id)) {
-                       d_printf("SMBsendtxt failed (%s)\n",cli_errstr(cli));
-                       return;
+               if (c == '\n') {
+                       buf[i++] = '\r';
                }
-
-               total_len += l;
+               buf[i++] = c;
        }
+       buf[i] = '\0';
 
-       if (total_len >= 1600)
-               d_printf("the message was truncated to 1600 bytes\n");
-       else
-               d_printf("sent %d bytes\n",total_len);
-
-       if (!cli_message_end(cli, grp_id)) {
-               d_printf("SMBsendend failed (%s)\n",cli_errstr(cli));
-               return;
+       status = cli_message(cli, desthost, username, buf);
+       if (!NT_STATUS_IS_OK(status)) {
+               d_fprintf(stderr, "cli_message returned %s\n",
+                         nt_errstr(status));
        }
 }
 
index 69555931796f2baee71b79cd454825cb68a4ca69..09933405eed6f3bf85d0f81c9024d7df61a67d6a 100644 (file)
@@ -2732,13 +2732,14 @@ int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
 
 /* The following definitions come from libsmb/climessage.c  */
 
-int cli_message_start_build(struct cli_state *cli, const char *host, const char *username);
-bool cli_message_start(struct cli_state *cli, const char *host, const char *username,
-                             int *grp);
-int cli_message_text_build(struct cli_state *cli, const char *msg, int len, int grp);
-bool cli_message_text(struct cli_state *cli, const char *msg, int len, int grp);
-int cli_message_end_build(struct cli_state *cli, int grp);
-bool cli_message_end(struct cli_state *cli, int grp);
+struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct cli_state *cli,
+                                   const char *host, const char *username,
+                                   const char *message);
+NTSTATUS cli_message_recv(struct tevent_req *req);
+NTSTATUS cli_message(struct cli_state *cli, const char *host,
+                    const char *username, const char *message);
 
 /* The following definitions come from libsmb/clioplock.c  */
 
index 6538902f5d10c42f3f691f35fcac8cefa724b142..2c8ef58b512e82f65b24433d83ea2aff948c7fda 100644 (file)
-/* 
+/*
    Unix SMB/CIFS implementation.
    client message handling routines
    Copyright (C) Andrew Tridgell 1994-1998
-   
+
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 
-/****************************************************************************
- Start a message sequence.
-****************************************************************************/
+struct cli_message_start_state {
+       uint16_t grp;
+};
+
+static void cli_message_start_done(struct tevent_req *subreq);
 
-int cli_message_start_build(struct cli_state *cli, const char *host, const char *username)
+static struct tevent_req *cli_message_start_send(TALLOC_CTX *mem_ctx,
+                                                struct tevent_context *ev,
+                                                struct cli_state *cli,
+                                                const char *host,
+                                                const char *username)
 {
-       char *p;
+       struct tevent_req *req, *subreq;
+       struct cli_message_start_state *state;
+       char *htmp = NULL;
+       char *utmp = NULL;
+       size_t hlen, ulen;
+       uint8_t *bytes, *p;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_message_start_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
+                                  username, strlen(username)+1,
+                                  &utmp, &ulen, true)) {
+               goto fail;
+       }
+       if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS,
+                                  host, strlen(host)+1,
+                                  &htmp, &hlen, true)) {
+               goto fail;
+       }
 
-       /* construct a SMBsendstrt command */
-       memset(cli->outbuf,'\0',smb_size);
-       cli_set_message(cli->outbuf,0,0,True);
-       SCVAL(cli->outbuf,smb_com,SMBsendstrt);
-       SSVAL(cli->outbuf,smb_tid,cli->cnum);
-       cli_setup_packet(cli);
+       bytes = talloc_array(state, uint8_t, ulen+hlen+2);
+       if (bytes == NULL) {
+               goto fail;
+       }
+       p = bytes;
 
-       p = smb_buf(cli->outbuf);
        *p++ = 4;
-       p += clistr_push(cli, p, username,
-                       cli->bufsize - PTR_DIFF(p,cli->outbuf), STR_ASCII|STR_TERMINATE);
+       memcpy(p, utmp, ulen);
        *p++ = 4;
-       p += clistr_push(cli, p, host,
-                       cli->bufsize - PTR_DIFF(p,cli->outbuf), STR_ASCII|STR_TERMINATE);
+       memcpy(p, htmp, hlen);
+       TALLOC_FREE(htmp);
+       TALLOC_FREE(utmp);
+
+       subreq = cli_smb_send(state, ev, cli, SMBsendstrt, 0, 0, NULL,
+                             talloc_get_size(bytes), bytes);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_message_start_done, req);
+       return req;
+fail:
+       TALLOC_FREE(htmp);
+       TALLOC_FREE(utmp);
+       tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+       return tevent_req_post(req, ev);
+}
 
-       cli_setup_bcc(cli, p);
+static void cli_message_start_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_message_start_state *state = tevent_req_data(
+               req, struct cli_message_start_state);
+       NTSTATUS status;
+       uint8_t wct;
+       uint16_t *vwv;
+
+       status = cli_smb_recv(subreq, 0, &wct, &vwv, NULL, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(subreq);
+               tevent_req_nterror(req, status);
+               return;
+       }
+       if (wct >= 1) {
+               state->grp = SVAL(vwv+0, 0);
+       } else {
+               state->grp = 0;
+       }
+       TALLOC_FREE(subreq);
+       tevent_req_done(req);
+}
 
-       return(PTR_DIFF(p, cli->outbuf));
+static NTSTATUS cli_message_start_recv(struct tevent_req *req,
+                                      uint16_t *pgrp)
+{
+       struct cli_message_start_state *state = tevent_req_data(
+               req, struct cli_message_start_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *pgrp = state->grp;
+       return NT_STATUS_OK;
 }
 
-bool cli_message_start(struct cli_state *cli, const char *host, const char *username,
-                             int *grp)
+struct cli_message_text_state {
+       uint16_t vwv;
+};
+
+static void cli_message_text_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_message_text_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct cli_state *cli,
+                                               uint16_t grp,
+                                               const char *msg,
+                                               int msglen)
 {
-       cli_message_start_build(cli, host, username);
-       cli_send_smb(cli);
+       struct tevent_req *req, *subreq;
+       struct cli_message_text_state *state;
+       char *tmp;
+       size_t tmplen;
+       uint8_t *bytes;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_message_text_state);
+       if (req == NULL) {
+               return NULL;
+       }
 
-       if (!cli_receive_smb(cli)) {
-               return False;
+       SSVAL(&state->vwv, 0, grp);
+
+       if (convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, msg, msglen,
+                                 &tmp, &tmplen, true)) {
+               msg = tmp;
+               msglen = tmplen;
+       } else {
+               DEBUG(3, ("Conversion failed, sending message in UNIX "
+                         "charset\n"));
+               tmp = NULL;
        }
 
-       if (cli_is_error(cli)) return False;
+       bytes = talloc_array(state, uint8_t, msglen+3);
+       if (tevent_req_nomem(bytes, req)) {
+               TALLOC_FREE(tmp);
+               return tevent_req_post(req, ev);
+       }
+       SCVAL(bytes, 0, 0);     /* pad */
+       SSVAL(bytes, 1, msglen);
+       memcpy(bytes+3, msg, msglen);
+       TALLOC_FREE(tmp);
+
+       subreq = cli_smb_send(state, ev, cli, SMBsendtxt, 0, 1, &state->vwv,
+                             talloc_get_size(bytes), bytes);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_message_text_done, req);
+       return req;
+}
 
-       *grp = SVAL(cli->inbuf,smb_vwv0);
+static void cli_message_text_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_smb_recv(subreq, 0, NULL, NULL, NULL, NULL);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
 
-       return True;
+static NTSTATUS cli_message_text_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
 }
 
-/****************************************************************************
- Send a message
-****************************************************************************/
+struct cli_message_end_state {
+       uint16_t vwv;
+};
 
-int cli_message_text_build(struct cli_state *cli, const char *msg, int len, int grp)
+static void cli_message_end_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_message_end_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct cli_state *cli,
+                                               uint16_t grp)
 {
-       char *msgdos;
-       size_t lendos;
-       char *p;
-
-       memset(cli->outbuf,'\0',smb_size);
-       cli_set_message(cli->outbuf,1,0,True);
-       SCVAL(cli->outbuf,smb_com,SMBsendtxt);
-       SSVAL(cli->outbuf,smb_tid,cli->cnum);
-       cli_setup_packet(cli);
-
-       SSVAL(cli->outbuf,smb_vwv0,grp);
-
-       p = smb_buf(cli->outbuf);
-       *p++ = 1;
-
-       if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, msg, len,
-               (void **)(void *)&msgdos, &lendos, True) || !msgdos) {
-               DEBUG(3,("Conversion failed, sending message in UNIX charset\n"));
-               SSVAL(p, 0, len); p += 2;
-               if (len > cli->bufsize - PTR_DIFF(p,cli->outbuf)) {
-                       return -1;
-               }
-               memcpy(p, msg, len);
-               p += len;
-       } else {
-               SSVAL(p, 0, lendos); p += 2;
-               if (lendos > cli->bufsize - PTR_DIFF(p,cli->outbuf)) {
-                       return -1;
-               }
-               memcpy(p, msgdos, lendos);
-               p += lendos;
-               TALLOC_FREE(msgdos);
+       struct tevent_req *req, *subreq;
+       struct cli_message_end_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_message_end_state);
+       if (req == NULL) {
+               return NULL;
        }
 
-       cli_setup_bcc(cli, p);
+       SSVAL(&state->vwv, 0, grp);
+
+       subreq = cli_smb_send(state, ev, cli, SMBsendend, 0, 1, &state->vwv,
+                             0, NULL);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_message_end_done, req);
+       return req;
+}
 
-       return(PTR_DIFF(p, cli->outbuf));
+static void cli_message_end_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_smb_recv(subreq, 0, NULL, NULL, NULL, NULL);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
 }
 
-bool cli_message_text(struct cli_state *cli, const char *msg, int len, int grp)
+static NTSTATUS cli_message_end_recv(struct tevent_req *req)
 {
-       cli_message_text_build(cli, msg, len, grp);
+       return tevent_req_simple_recv_ntstatus(req);
+}
 
-       cli_send_smb(cli);
+struct cli_message_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       size_t sent;
+       const char *message;
+       uint16_t grp;
+};
+
+static void cli_message_started(struct tevent_req *subreq);
+static void cli_message_sent(struct tevent_req *subreq);
+static void cli_message_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx,
+                                   struct tevent_context *ev,
+                                   struct cli_state *cli,
+                                   const char *host, const char *username,
+                                   const char *message)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_message_state *state;
 
-       if (!cli_receive_smb(cli)) {
-               return False;
+       req = tevent_req_create(mem_ctx, &state, struct cli_message_state);
+       if (req == NULL) {
+               return NULL;
        }
+       state->ev = ev;
+       state->cli = cli;
+       state->sent = 0;
+       state->message = message;
+
+       subreq = cli_message_start_send(state, ev, cli, host, username);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_message_started, req);
+       return req;
+}
 
-       if (cli_is_error(cli)) return False;
+static void cli_message_started(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_message_state *state = tevent_req_data(
+               req, struct cli_message_state);
+       NTSTATUS status;
+       size_t thistime;
+
+       status = cli_message_start_recv(subreq, &state->grp);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
 
-       return True;
-}
+       thistime = MIN(127, strlen(state->message));
 
-/****************************************************************************
- End a message.
-****************************************************************************/
+       subreq = cli_message_text_send(state, state->ev, state->cli,
+                                      state->grp, state->message, thistime);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       state->sent += thistime;
+       tevent_req_set_callback(subreq, cli_message_sent, req);
+}
 
-int cli_message_end_build(struct cli_state *cli, int grp)
+static void cli_message_sent(struct tevent_req *subreq)
 {
-       char *p;
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_message_state *state = tevent_req_data(
+               req, struct cli_message_state);
+       NTSTATUS status;
+       size_t left, thistime;
+
+       status = cli_message_text_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
 
-       memset(cli->outbuf,'\0',smb_size);
-       cli_set_message(cli->outbuf,1,0,True);
-       SCVAL(cli->outbuf,smb_com,SMBsendend);
-       SSVAL(cli->outbuf,smb_tid,cli->cnum);
+       if (state->sent >= strlen(state->message)) {
+               subreq = cli_message_end_send(state, state->ev, state->cli,
+                                             state->grp);
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+               tevent_req_set_callback(subreq, cli_message_done, req);
+               return;
+       }
 
-       SSVAL(cli->outbuf,smb_vwv0,grp);
+       left = strlen(state->message) - state->sent;
+       thistime = MIN(127, left);
 
-       cli_setup_packet(cli);
+       subreq = cli_message_text_send(state, state->ev, state->cli,
+                                      state->grp,
+                                      state->message + state->sent,
+                                      thistime);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       state->sent += thistime;
+       tevent_req_set_callback(subreq, cli_message_sent, req);
+}
 
-       p = smb_buf(cli->outbuf);
+static void cli_message_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_message_end_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
 
-       return(PTR_DIFF(p, cli->outbuf));
+NTSTATUS cli_message_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
 }
 
-bool cli_message_end(struct cli_state *cli, int grp)
+NTSTATUS cli_message(struct cli_state *cli, const char *host,
+                    const char *username, const char *message)
 {
-       cli_message_end_build(cli, grp);
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_OK;
+
+       if (cli_has_async_calls(cli)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
 
-       cli_send_smb(cli);
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
 
-       if (!cli_receive_smb(cli)) {
-               return False;
+       req = cli_message_send(frame, ev, cli, host, username, message);
+       if (req == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
        }
 
-       if (cli_is_error(cli)) return False;
+       if (!tevent_req_poll(req, ev)) {
+               status = map_nt_error_from_unix(errno);
+               goto fail;
+       }
 
-       return True;
+       status = cli_message_recv(req);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
 }