1 ###################################################
2 # Samba3 client generator for IDL structures
3 # on top of Samba4 style NDR functions
4 # Copyright jelmer@samba.org 2005-2006
5 # Copyright gd@samba.org 2008
6 # released under the GNU GPL
8 package Parse::Pidl::Samba3::ClientNDR;
12 @EXPORT_OK = qw(ParseFunction $res $res_hdr ParseOutputArgument);
15 use Parse::Pidl qw(fatal warning error);
16 use Parse::Pidl::Util qw(has_property ParseExpr);
17 use Parse::Pidl::Typelist qw(mapTypeName);
18 use Parse::Pidl::Samba4 qw(DeclLong);
19 use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv);
21 use vars qw($VERSION);
24 sub indent($) { my ($self) = @_; $self->{tabs}.="\t"; }
25 sub deindent($) { my ($self) = @_; $self->{tabs} = substr($self->{tabs}, 1); }
26 sub pidl($$) { my ($self,$txt) = @_; $self->{res} .= $txt ? "$self->{tabs}$txt\n" : "\n"; }
27 sub pidl_hdr($$) { my ($self, $txt) = @_; $self->{res_hdr} .= "$txt\n"; }
28 sub fn_declare($$) { my ($self,$n) = @_; $self->pidl($n); $self->pidl_hdr("$n;"); }
33 my $nt = int((length($s)+1)/8);
35 my $ns = (length($s)-$lt);
36 return "\t"x($nt)." "x($ns);
42 my $self = { res => "", res_hdr => "", tabs => "" };
46 sub ElementDirection($)
50 return "[in,out]" if (has_property($e, "in") and has_property($e, "out"));
51 return "[in]" if (has_property($e, "in"));
52 return "[out]" if (has_property($e, "out"));
56 sub HeaderProperties($$)
58 my($props,$ignores) = @_;
61 foreach my $d (keys %{$props}) {
62 next if (grep(/^$d$/, @$ignores));
63 if($props->{$d} ne "1") {
64 $ret.= "$d($props->{$d}),";
71 return "[" . substr($ret, 0, -1) . "]";
75 sub ParseInvalidResponse($$)
77 my ($self, $type) = @_;
79 if ($type eq "sync") {
80 $self->pidl("return NT_STATUS_INVALID_NETWORK_RESPONSE;");
81 } elsif ($type eq "async") {
82 $self->pidl("tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);");
83 $self->pidl("return;");
85 die("ParseInvalidResponse($type)");
89 sub ParseOutputArgument($$$;$$$)
91 my ($self, $fn, $e, $r, $o, $invalid_response_type) = @_;
93 $r = "r." unless defined($r);
94 $o = "" unless defined($o);
95 $invalid_response_type = "sync" unless defined($invalid_response_type);
97 if ($e->{LEVELS}[0]->{TYPE} ne "POINTER" and $e->{LEVELS}[0]->{TYPE} ne "ARRAY") {
98 $self->pidl("return NT_STATUS_NOT_SUPPORTED;");
99 error($e->{ORIGINAL}, "[out] argument is not a pointer or array");
103 if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") {
105 if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") {
106 $self->pidl("if ($o$e->{NAME} && ${r}out.$e->{NAME}) {");
111 if ($e->{LEVELS}[$level]->{TYPE} eq "ARRAY") {
112 # This is a call to GenerateFunctionInEnv intentionally.
113 # Since the data is being copied into a user-provided data
114 # structure, the user should be able to know the size beforehand
115 # to allocate a structure of the right size.
116 my $in_env = GenerateFunctionInEnv($fn, $r);
117 my $out_env = GenerateFunctionOutEnv($fn, $r);
118 my $l = $e->{LEVELS}[$level];
119 unless (defined($l->{SIZE_IS})) {
120 $self->pidl('#error No size known for [out] array `$e->{NAME}');
121 error($e->{ORIGINAL}, "no size known for [out] array `$e->{NAME}'");
123 my $in_size_is = ParseExpr($l->{SIZE_IS}, $in_env, $e->{ORIGINAL});
124 my $out_size_is = ParseExpr($l->{SIZE_IS}, $out_env, $e->{ORIGINAL});
125 my $out_length_is = $out_size_is;
126 if (defined($l->{LENGTH_IS})) {
127 $out_length_is = ParseExpr($l->{LENGTH_IS}, $out_env, $e->{ORIGINAL});
129 if ($out_size_is ne $in_size_is) {
130 $self->pidl("if (($out_size_is) > ($in_size_is)) {");
132 $self->ParseInvalidResponse($invalid_response_type);
136 if ($out_length_is ne $out_size_is) {
137 $self->pidl("if (($out_length_is) > ($out_size_is)) {");
139 $self->ParseInvalidResponse($invalid_response_type);
143 if (has_property($e, "charset")) {
144 $self->pidl("memcpy(discard_const_p(uint8_t *, $o$e->{NAME}), ${r}out.$e->{NAME}, ($out_length_is) * sizeof(*$o$e->{NAME}));");
146 $self->pidl("memcpy($o$e->{NAME}, ${r}out.$e->{NAME}, ($out_length_is) * sizeof(*$o$e->{NAME}));");
150 $self->pidl("*$o$e->{NAME} = *${r}out.$e->{NAME};");
153 if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") {
154 if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") {
161 sub ParseFunctionAsyncState($$$)
163 my ($self, $if, $fn) = @_;
165 my $state_str = "struct rpccli_$fn->{NAME}_state";
166 my $done_fn = "rpccli_$fn->{NAME}_done";
168 $self->pidl("$state_str {");
170 $self->pidl("TALLOC_CTX *out_mem_ctx;");
171 if (defined($fn->{RETURN_TYPE})) {
172 $self->pidl(mapTypeName($fn->{RETURN_TYPE}). " result;");
177 $self->pidl("static void $done_fn(struct tevent_req *subreq);");
181 sub ParseFunctionAsyncSend($$$)
183 my ($self, $if, $fn) = @_;
187 my $ufn = "NDR_".uc($fn->{NAME});
188 my $state_str = "struct rpccli_$fn->{NAME}_state";
189 my $done_fn = "rpccli_$fn->{NAME}_done";
190 my $out_mem_ctx = "rpccli_$fn->{NAME}_out_memory";
191 my $fn_str = "struct tevent_req *rpccli_$fn->{NAME}_send";
192 my $pad = genpad($fn_str);
194 $fn_args .= "TALLOC_CTX *mem_ctx";
195 $fn_args .= ",\n" . $pad . "struct tevent_context *ev";
196 $fn_args .= ",\n" . $pad . "struct rpc_pipe_client *cli";
198 foreach (@{$fn->{ELEMENTS}}) {
199 my $dir = ElementDirection($_);
200 my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]);
201 $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */";
204 $self->fn_declare("$fn_str($fn_args)");
207 $self->pidl("struct tevent_req *req;");
208 $self->pidl("$state_str *state;");
209 $self->pidl("struct tevent_req *subreq;");
211 $self->pidl("req = tevent_req_create(mem_ctx, &state,");
212 $self->pidl("\t\t\t$state_str);");
213 $self->pidl("if (req == NULL) {");
215 $self->pidl("return NULL;");
218 $self->pidl("state->out_mem_ctx = NULL;");
222 foreach (@{$fn->{ELEMENTS}}) {
223 if (grep(/out/, @{$_->{DIRECTION}})) {
228 if ($out_params > 0) {
229 $self->pidl("state->out_mem_ctx = talloc_named_const(state, 0,");
230 $self->pidl("\t\t \"$out_mem_ctx\");");
231 $self->pidl("if (tevent_req_nomem(state->out_mem_ctx, req)) {");
233 $self->pidl("return tevent_req_post(req, ev);");
239 $fn_str = "subreq = dcerpc_$fn->{NAME}_send";
240 $pad = "\t" . genpad($fn_str);
241 $fn_args = "state,\n" . $pad . "ev,\n" . $pad . "cli->binding_handle";
242 foreach (@{$fn->{ELEMENTS}}) {
243 $fn_args .= ",\n" . $pad . "_". $_->{NAME};
246 $self->pidl("$fn_str($fn_args);");
247 $self->pidl("if (tevent_req_nomem(subreq, req)) {");
249 $self->pidl("return tevent_req_post(req, ev);");
252 $self->pidl("tevent_req_set_callback(subreq, $done_fn, req);");
253 $self->pidl("return req;");
259 sub ParseFunctionAsyncDone($$$)
261 my ($self, $if, $fn) = @_;
263 my $state_str = "struct rpccli_$fn->{NAME}_state";
264 my $done_fn = "rpccli_$fn->{NAME}_done";
266 $self->pidl("static void $done_fn(struct tevent_req *subreq)");
269 $self->pidl("struct tevent_req *req = tevent_req_callback_data(");
270 $self->pidl("\tsubreq, struct tevent_req);");
271 $self->pidl("$state_str *state = tevent_req_data(");
272 $self->pidl("\treq, $state_str);");
273 $self->pidl("NTSTATUS status;");
274 $self->pidl("TALLOC_CTX *mem_ctx;");
277 $self->pidl("if (state->out_mem_ctx) {");
279 $self->pidl("mem_ctx = state->out_mem_ctx;");
281 $self->pidl("} else {");
283 $self->pidl("mem_ctx = state;");
288 my $fn_str = "status = dcerpc_$fn->{NAME}_recv";
289 my $pad = "\t" . genpad($fn_str);
290 my $fn_args = "subreq,\n" . $pad . "mem_ctx";
291 if (defined($fn->{RETURN_TYPE})) {
292 $fn_args .= ",\n" . $pad . "&state->result";
295 $self->pidl("$fn_str($fn_args);");
296 $self->pidl("TALLOC_FREE(subreq);");
297 $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
299 $self->pidl("tevent_req_nterror(req, status);");
300 $self->pidl("return;");
305 $self->pidl("tevent_req_done(req);");
311 sub ParseFunctionAsyncRecv($$$)
313 my ($self, $if, $fn) = @_;
316 my $state_str = "struct rpccli_$fn->{NAME}_state";
317 my $fn_str = "NTSTATUS rpccli_$fn->{NAME}_recv";
318 my $pad = genpad($fn_str);
320 $fn_args .= "struct tevent_req *req,\n" . $pad . "TALLOC_CTX *mem_ctx";
322 if (defined($fn->{RETURN_TYPE})) {
323 $fn_args .= ",\n" . $pad . "$fn->{RETURN_TYPE} *result";
326 $self->fn_declare("$fn_str($fn_args)");
329 $self->pidl("$state_str *state = tevent_req_data(");
330 $self->pidl("\treq, $state_str);");
331 $self->pidl("NTSTATUS status;");
333 $self->pidl("if (tevent_req_is_nterror(req, &status)) {");
335 $self->pidl("tevent_req_received(req);");
336 $self->pidl("return status;");
341 $self->pidl("/* Steal possbile out parameters to the callers context */");
342 $self->pidl("talloc_steal(mem_ctx, state->out_mem_ctx);");
345 if (defined($fn->{RETURN_TYPE})) {
346 $self->pidl("/* Return result */");
347 $self->pidl("*result = state->result;");
351 $self->pidl("tevent_req_received(req);");
352 $self->pidl("return NT_STATUS_OK;");
358 sub ParseFunctionSync($$$)
360 my ($self, $if, $fn) = @_;
364 my $ufn = "NDR_".uc($fn->{NAME});
365 my $fn_str = "NTSTATUS rpccli_$fn->{NAME}";
366 my $pad = genpad($fn_str);
368 $fn_args .= "struct rpc_pipe_client *cli,\n" . $pad . "TALLOC_CTX *mem_ctx";
370 foreach (@{$fn->{ELEMENTS}}) {
371 my $dir = ElementDirection($_);
372 my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]);
373 $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */";
376 if (defined($fn->{RETURN_TYPE}) && ($fn->{RETURN_TYPE} eq "WERROR")) {
377 $fn_args .= ",\n" . $pad . "WERROR *werror";
380 $self->fn_declare("$fn_str($fn_args)");
383 if (defined($fn->{RETURN_TYPE})) {
384 $self->pidl(mapTypeName($fn->{RETURN_TYPE})." result;");
386 $self->pidl("NTSTATUS status;");
389 $fn_str = "status = dcerpc_$fn->{NAME}";
390 $pad = "\t" . genpad($fn_str);
391 $fn_args = "cli->binding_handle,\n" . $pad . "mem_ctx";
392 foreach (@{$fn->{ELEMENTS}}) {
393 $fn_args .= ",\n" . $pad . "_". $_->{NAME};
395 if (defined($fn->{RETURN_TYPE})) {
396 $fn_args .= ",\n" . $pad . "&result";
399 $self->pidl("$fn_str($fn_args);");
400 $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
402 $self->pidl("return status;");
407 $self->pidl("/* Return result */");
408 if (not $fn->{RETURN_TYPE}) {
409 $self->pidl("return NT_STATUS_OK;");
410 } elsif ($fn->{RETURN_TYPE} eq "NTSTATUS") {
411 $self->pidl("return result;");
412 } elsif ($fn->{RETURN_TYPE} eq "WERROR") {
413 $self->pidl("if (werror) {");
415 $self->pidl("*werror = result;");
419 $self->pidl("return werror_to_ntstatus(result);");
421 warning($fn->{ORIGINAL}, "Unable to convert $fn->{RETURN_TYPE} to NTSTATUS");
422 $self->pidl("return NT_STATUS_OK;");
430 sub ParseFunction($$$)
432 my ($self, $if, $fn) = @_;
434 $self->ParseFunctionAsyncState($if, $fn);
435 $self->ParseFunctionAsyncSend($if, $fn);
436 $self->ParseFunctionAsyncDone($if, $fn);
437 $self->ParseFunctionAsyncRecv($if, $fn);
439 $self->ParseFunctionSync($if, $fn);
442 sub ParseInterface($$)
444 my ($self, $if) = @_;
446 my $uif = uc($if->{NAME});
448 $self->pidl_hdr("#ifndef __CLI_$uif\__");
449 $self->pidl_hdr("#define __CLI_$uif\__");
450 foreach my $fn (@{$if->{FUNCTIONS}}) {
451 next if has_property($fn, "noopnum");
452 next if has_property($fn, "todo");
453 $self->ParseFunction($if->{NAME}, $fn);
455 $self->pidl_hdr("#endif /* __CLI_$uif\__ */");
460 my($self,$ndr,$header,$c_header) = @_;
463 $self->pidl(" * Unix SMB/CIFS implementation.");
464 $self->pidl(" * client auto-generated by pidl. DO NOT MODIFY!");
467 $self->pidl("#include \"includes.h\"");
468 $self->pidl("#include \"$header\"");
469 $self->pidl_hdr("#include \"$c_header\"");
473 $self->ParseInterface($_) if ($_->{TYPE} eq "INTERFACE");
476 return ($self->{res}, $self->{res_hdr});