2397f1a17d1070d266613330296a70ac6984cd4c
[samba.git] / pidl / lib / Parse / Pidl / Samba4 / NDR / Client.pm
1 ###################################################
2 # client calls generator
3 # Copyright tridge@samba.org 2003
4 # Copyright jelmer@samba.org 2005-2006
5 # released under the GNU GPL
6
7 package Parse::Pidl::Samba4::NDR::Client;
8
9 use Exporter;
10 @ISA = qw(Exporter);
11 @EXPORT_OK = qw(Parse);
12
13 use Parse::Pidl qw(fatal warning error);
14 use Parse::Pidl::Util qw(has_property ParseExpr);
15 use Parse::Pidl::Typelist qw(mapTypeName);
16 use Parse::Pidl::Samba4 qw(choose_header is_intree DeclLong);
17 use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv);
18
19 use vars qw($VERSION);
20 $VERSION = '0.01';
21
22 use strict;
23
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 pidl_both($$) { my ($self, $txt) = @_; $self->{hdr} .= "$txt\n"; $self->{res_hdr} .= "$txt\n"; }
29 sub fn_declare($$) { my ($self,$n) = @_; $self->pidl($n); $self->pidl_hdr("$n;"); }
30
31 sub genpad($)
32 {
33         my ($s) = @_;
34         my $nt = int((length($s)+1)/8);
35         my $lt = ($nt*8)-1;
36         my $ns = (length($s)-$lt);
37         return "\t"x($nt)." "x($ns);
38 }
39
40 sub new($)
41 {
42         my ($class) = shift;
43         my $self = { res => "", res_hdr => "", tabs => "" };
44         bless($self, $class);
45 }
46
47 sub ParseFunction_r_State($$$$)
48 {
49         my ($self, $if, $fn, $name) = @_;
50         my $uname = uc $name;
51
52         $self->pidl("struct dcerpc_$name\_r_state {");
53         $self->indent;
54         $self->pidl("TALLOC_CTX *out_mem_ctx;");
55         $self->deindent;
56         $self->pidl("};");
57         $self->pidl("");
58         $self->pidl("static void dcerpc_$name\_r_done(struct tevent_req *subreq);");
59         $self->pidl("");
60 }
61
62 sub ParseFunction_r_Send($$$$)
63 {
64         my ($self, $if, $fn, $name) = @_;
65         my $uname = uc $name;
66
67         my $proto = "struct tevent_req *dcerpc_$name\_r_send(TALLOC_CTX *mem_ctx,\n";
68         $proto   .= "\tstruct tevent_context *ev,\n",
69         $proto   .= "\tstruct dcerpc_binding_handle *h,\n",
70         $proto   .= "\tstruct $name *r)";
71
72         $self->fn_declare($proto);
73
74         $self->pidl("{");
75         $self->indent;
76
77         $self->pidl("struct tevent_req *req;");
78         $self->pidl("struct dcerpc_$name\_r_state *state;");
79         $self->pidl("struct tevent_req *subreq;");
80         $self->pidl("");
81
82         $self->pidl("req = tevent_req_create(mem_ctx, &state,");
83         $self->pidl("\t\t\tstruct dcerpc_$name\_r_state);");
84         $self->pidl("if (req == NULL) {");
85         $self->indent;
86         $self->pidl("return NULL;");
87         $self->deindent;
88         $self->pidl("}");
89         $self->pidl("");
90
91         my $out_params = 0;
92         foreach (@{$fn->{ELEMENTS}}) {
93                 if (grep(/out/, @{$_->{DIRECTION}})) {
94                         $out_params++;
95                 }
96         }
97
98         my $submem;
99         if ($out_params > 0) {
100                 $self->pidl("state->out_mem_ctx = talloc_new(state);");
101                 $self->pidl("if (tevent_req_nomem(state->out_mem_ctx, req)) {");
102                 $self->indent;
103                 $self->pidl("return tevent_req_post(req, ev);");
104                 $self->deindent;
105                 $self->pidl("}");
106                 $submem = "state->out_mem_ctx";
107         } else {
108                 $self->pidl("state->out_mem_ctx = NULL;");
109                 $submem = "state";
110         }
111         $self->pidl("");
112
113         $self->pidl("subreq = dcerpc_binding_handle_call_send(state, ev, h,");
114         $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},");
115         $self->pidl("\t\tNDR_$uname, $submem, r);");
116         $self->pidl("if (tevent_req_nomem(subreq, req)) {");
117         $self->indent;
118         $self->pidl("return tevent_req_post(req, ev);");
119         $self->deindent;
120         $self->pidl("}");
121         $self->pidl("tevent_req_set_callback(subreq, dcerpc_$name\_r_done, req);");
122         $self->pidl("");
123
124         $self->pidl("return req;");
125         $self->deindent;
126         $self->pidl("}");
127         $self->pidl("");
128 }
129
130 sub ParseFunction_r_Done($$$$)
131 {
132         my ($self, $if, $fn, $name) = @_;
133         my $uname = uc $name;
134
135         my $proto = "static void dcerpc_$name\_r_done(struct tevent_req *subreq)";
136
137         $self->pidl("$proto");
138         $self->pidl("{");
139         $self->indent;
140
141         $self->pidl("struct tevent_req *req =");
142         $self->pidl("\ttevent_req_callback_data(subreq,");
143         $self->pidl("\tstruct tevent_req);");
144         $self->pidl("NTSTATUS status;");
145         $self->pidl("");
146
147         $self->pidl("status = dcerpc_binding_handle_call_recv(subreq);");
148         $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
149         $self->indent;
150         $self->pidl("tevent_req_nterror(req, status);");
151         $self->pidl("return;");
152         $self->deindent;
153         $self->pidl("}");
154         $self->pidl("");
155
156         $self->pidl("tevent_req_done(req);");
157         $self->deindent;
158         $self->pidl("}");
159         $self->pidl("");
160 }
161
162 sub ParseFunction_r_Recv($$$$)
163 {
164         my ($self, $if, $fn, $name) = @_;
165         my $uname = uc $name;
166
167         my $proto = "NTSTATUS dcerpc_$name\_r_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx)";
168
169         $self->fn_declare($proto);
170
171         $self->pidl("{");
172         $self->indent;
173
174         $self->pidl("struct dcerpc_$name\_r_state *state =");
175         $self->pidl("\ttevent_req_data(req,");
176         $self->pidl("\tstruct dcerpc_$name\_r_state);");
177         $self->pidl("NTSTATUS status;");
178         $self->pidl("");
179
180         $self->pidl("if (tevent_req_is_nterror(req, &status)) {");
181         $self->indent;
182         $self->pidl("tevent_req_received(req);");
183         $self->pidl("return status;");
184         $self->deindent;
185         $self->pidl("}");
186         $self->pidl("");
187
188         $self->pidl("talloc_steal(mem_ctx, state->out_mem_ctx);");
189         $self->pidl("");
190
191         $self->pidl("tevent_req_received(req);");
192         $self->pidl("return NT_STATUS_OK;");
193         $self->deindent;
194         $self->pidl("}");
195         $self->pidl("");
196 }
197
198 sub ParseFunction_r_Sync($$$$)
199 {
200         my ($self, $if, $fn, $name) = @_;
201         my $uname = uc $name;
202
203         my $proto = "NTSTATUS dcerpc_$name\_r(struct dcerpc_binding_handle *h, TALLOC_CTX *mem_ctx, struct $name *r)";
204
205         $self->fn_declare($proto);
206
207         $self->pidl("{");
208         $self->indent;
209         $self->pidl("NTSTATUS status;");
210         $self->pidl("");
211
212         $self->pidl("status = dcerpc_binding_handle_call(h,");
213         $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},");
214         $self->pidl("\t\tNDR_$uname, mem_ctx, r);");
215         $self->pidl("");
216         $self->pidl("return status;");
217
218         $self->deindent;
219         $self->pidl("}");
220         $self->pidl("");
221 }
222
223 sub ElementDirection($)
224 {
225         my ($e) = @_;
226
227         return "[in,out]" if (has_property($e, "in") and has_property($e, "out"));
228         return "[in]" if (has_property($e, "in"));
229         return "[out]" if (has_property($e, "out"));
230         return "[in,out]";
231 }
232
233 sub HeaderProperties($$)
234 {
235         my($props,$ignores) = @_;
236         my $ret = "";
237
238         foreach my $d (keys %{$props}) {
239                 next if (grep(/^$d$/, @$ignores));
240                 if($props->{$d} ne "1") {
241                         $ret.= "$d($props->{$d}),";
242                 } else {
243                         $ret.="$d,";
244                 }
245         }
246
247         if ($ret) {
248                 return "[" . substr($ret, 0, -1) . "]";
249         }
250 }
251
252 sub ParseCopyArgument($$$$$)
253 {
254         my ($self, $fn, $e, $r, $i) = @_;
255         my $l = $e->{LEVELS}[0];
256
257         if ($l->{TYPE} eq "ARRAY" and $l->{IS_FIXED} == 1) {
258                 $self->pidl("memcpy(${r}$e->{NAME}, ${i}$e->{NAME}, sizeof(${r}$e->{NAME}));");
259         } else {
260                 $self->pidl("${r}$e->{NAME} = ${i}$e->{NAME};");
261         }
262 }
263
264 sub ParseInvalidResponse($$)
265 {
266         my ($self, $type) = @_;
267
268         if ($type eq "sync") {
269                 $self->pidl("return NT_STATUS_INVALID_NETWORK_RESPONSE;");
270         } elsif ($type eq "async") {
271                 $self->pidl("tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);");
272                 $self->pidl("return;");
273         } else {
274                 die("ParseInvalidResponse($type)");
275         }
276 }
277
278 sub ParseOutputArgument($$$$$$)
279 {
280         my ($self, $fn, $e, $r, $o, $invalid_response_type) = @_;
281         my $level = 0;
282
283         if ($e->{LEVELS}[0]->{TYPE} ne "POINTER" and $e->{LEVELS}[0]->{TYPE} ne "ARRAY") {
284                 fatal($e->{ORIGINAL}, "[out] argument is not a pointer or array");
285                 return;
286         }
287
288         if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") {
289                 $level = 1;
290                 if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") {
291                         $self->pidl("if ($o$e->{NAME} && ${r}out.$e->{NAME}) {");
292                         $self->indent;
293                 }
294         }
295
296         if ($e->{LEVELS}[$level]->{TYPE} eq "ARRAY") {
297                 # This is a call to GenerateFunctionInEnv intentionally.
298                 # Since the data is being copied into a user-provided data
299                 # structure, the user should be able to know the size beforehand
300                 # to allocate a structure of the right size.
301                 my $in_env = GenerateFunctionInEnv($fn, $r);
302                 my $out_env = GenerateFunctionOutEnv($fn, $r);
303                 my $l = $e->{LEVELS}[$level];
304
305                 my $in_var = undef;
306                 if (grep(/in/, @{$e->{DIRECTION}})) {
307                         $in_var = ParseExpr($e->{NAME}, $in_env, $e->{ORIGINAL});
308                 }
309                 my $out_var = ParseExpr($e->{NAME}, $out_env, $e->{ORIGINAL});
310
311                 my $in_size_is = undef;
312                 my $out_size_is = undef;
313                 my $out_length_is = undef;
314
315                 my $avail_len = undef;
316                 my $needed_len = undef;
317
318                 $self->pidl("{");
319                 $self->indent;
320                 my $copy_len_var = "_copy_len_$e->{NAME}";
321                 $self->pidl("size_t $copy_len_var;");
322
323                 if (not defined($l->{SIZE_IS})) {
324                         if (not $l->{IS_ZERO_TERMINATED}) {
325                                 fatal($e->{ORIGINAL}, "no size known for [out] array `$e->{NAME}'");
326                         }
327                         if (has_property($e, "charset")) {
328                                 $avail_len = "ndr_charset_length($in_var, CH_UNIX)";
329                                 $needed_len = "ndr_charset_length($out_var, CH_UNIX)";
330                         } else {
331                                 $avail_len = "ndr_string_length($in_var, sizeof(*$in_var))";
332                                 $needed_len = "ndr_string_length($out_var, sizeof(*$out_var))";
333                         }
334                         $in_size_is = "";
335                         $out_size_is = "";
336                         $out_length_is = "";
337                 } else {
338                         $in_size_is = ParseExpr($l->{SIZE_IS}, $in_env, $e->{ORIGINAL});
339                         $out_size_is = ParseExpr($l->{SIZE_IS}, $out_env, $e->{ORIGINAL});
340                         $out_length_is = $out_size_is;
341                         if (defined($l->{LENGTH_IS})) {
342                                 $out_length_is = ParseExpr($l->{LENGTH_IS}, $out_env, $e->{ORIGINAL});
343                         }
344                         if (has_property($e, "charset")) {
345                                 if (defined($in_var)) {
346                                         $avail_len = "ndr_charset_length($in_var, CH_UNIX)";
347                                 } else {
348                                         $avail_len = $out_length_is;
349                                 }
350                                 $needed_len = "ndr_charset_length($out_var, CH_UNIX)";
351                         }
352                 }
353
354                 if ($out_size_is ne $in_size_is) {
355                         $self->pidl("if (($out_size_is) > ($in_size_is)) {");
356                         $self->indent;
357                         $self->ParseInvalidResponse($invalid_response_type);
358                         $self->deindent;
359                         $self->pidl("}");
360                 }
361                 if ($out_length_is ne $out_size_is) {
362                         $self->pidl("if (($out_length_is) > ($out_size_is)) {");
363                         $self->indent;
364                         $self->ParseInvalidResponse($invalid_response_type);
365                         $self->deindent;
366                         $self->pidl("}");
367                 }
368                 if (defined($needed_len)) {
369                         $self->pidl("$copy_len_var = $needed_len;");
370                         $self->pidl("if ($copy_len_var > $avail_len) {");
371                         $self->indent;
372                         $self->ParseInvalidResponse($invalid_response_type);
373                         $self->deindent;
374                         $self->pidl("}");
375                 } else {
376                         $self->pidl("$copy_len_var = $out_length_is;");
377                 }
378
379                 if (has_property($e, "charset")) {
380                         $self->pidl("memcpy(discard_const_p(uint8_t *, $o$e->{NAME}), $out_var, $copy_len_var * sizeof(*$o$e->{NAME}));");
381                 } else {
382                         $self->pidl("memcpy($o$e->{NAME}, $out_var, $copy_len_var * sizeof(*$o$e->{NAME}));");
383                 }
384
385                 $self->deindent;
386                 $self->pidl("}");
387         } else {
388                 $self->pidl("*$o$e->{NAME} = *${r}out.$e->{NAME};");
389         }
390
391         if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") {
392                 if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") {
393                         $self->deindent;
394                         $self->pidl("}");
395                 }
396         }
397 }
398
399 sub ParseFunction_State($$$$)
400 {
401         my ($self, $if, $fn, $name) = @_;
402
403         my $state_str = "struct dcerpc_$name\_state";
404         my $done_fn = "dcerpc_$name\_done";
405
406         $self->pidl("$state_str {");
407         $self->indent;
408         $self->pidl("struct $name orig;");
409         $self->pidl("struct $name tmp;");
410         $self->pidl("TALLOC_CTX *out_mem_ctx;");
411         $self->deindent;
412         $self->pidl("};");
413         $self->pidl("");
414         $self->pidl("static void $done_fn(struct tevent_req *subreq);");
415         $self->pidl("");
416 }
417
418 sub ParseFunction_Send($$$$)
419 {
420         my ($self, $if, $fn, $name) = @_;
421
422         my $fn_args = "";
423         my $state_str = "struct dcerpc_$name\_state";
424         my $done_fn = "dcerpc_$name\_done";
425         my $out_mem_ctx = "dcerpc_$name\_out_memory";
426         my $fn_str = "struct tevent_req *dcerpc_$name\_send";
427         my $pad = genpad($fn_str);
428
429         $fn_args .= "TALLOC_CTX *mem_ctx";
430         $fn_args .= ",\n" . $pad . "struct tevent_context *ev";
431         $fn_args .= ",\n" . $pad . "struct dcerpc_binding_handle *h";
432
433         foreach (@{$fn->{ELEMENTS}}) {
434                 my $dir = ElementDirection($_);
435                 my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]);
436                 $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */";
437         }
438
439         $self->fn_declare("$fn_str($fn_args)");
440         $self->pidl("{");
441         $self->indent;
442         $self->pidl("struct tevent_req *req;");
443         $self->pidl("$state_str *state;");
444         $self->pidl("struct tevent_req *subreq;");
445         $self->pidl("");
446         $self->pidl("req = tevent_req_create(mem_ctx, &state,");
447         $self->pidl("\t\t\t$state_str);");
448         $self->pidl("if (req == NULL) {");
449         $self->indent;
450         $self->pidl("return NULL;");
451         $self->deindent;
452         $self->pidl("}");
453         $self->pidl("state->out_mem_ctx = NULL;");
454         $self->pidl("");
455
456         $self->pidl("/* In parameters */");
457         foreach my $e (@{$fn->{ELEMENTS}}) {
458                 next unless (grep(/in/, @{$e->{DIRECTION}}));
459
460                 $self->ParseCopyArgument($fn, $e, "state->orig.in.", "_");
461         }
462         $self->pidl("");
463
464         my $out_params = 0;
465         $self->pidl("/* Out parameters */");
466         foreach my $e (@{$fn->{ELEMENTS}}) {
467                 next unless grep(/out/, @{$e->{DIRECTION}});
468
469                 $self->ParseCopyArgument($fn, $e, "state->orig.out.", "_");
470                 $out_params++;
471         }
472         $self->pidl("");
473
474         if (defined($fn->{RETURN_TYPE})) {
475                 $self->pidl("/* Result */");
476                 $self->pidl("ZERO_STRUCT(state->orig.out.result);");
477                 $self->pidl("");
478         }
479
480         if ($out_params > 0) {
481                 $self->pidl("state->out_mem_ctx = talloc_named_const(state, 0,");
482                 $self->pidl("\t\t     \"$out_mem_ctx\");");
483                 $self->pidl("if (tevent_req_nomem(state->out_mem_ctx, req)) {");
484                 $self->indent;
485                 $self->pidl("return tevent_req_post(req, ev);");
486                 $self->deindent;
487                 $self->pidl("}");
488                 $self->pidl("");
489         }
490
491         $self->pidl("/* make a temporary copy, that we pass to the dispatch function */");
492         $self->pidl("state->tmp = state->orig;");
493         $self->pidl("");
494
495         $self->pidl("subreq = dcerpc_$name\_r_send(state, ev, h, &state->tmp);");
496         $self->pidl("if (tevent_req_nomem(subreq, req)) {");
497         $self->indent;
498         $self->pidl("return tevent_req_post(req, ev);");
499         $self->deindent;
500         $self->pidl("}");
501         $self->pidl("tevent_req_set_callback(subreq, $done_fn, req);");
502         $self->pidl("return req;");
503         $self->deindent;
504         $self->pidl("}");
505         $self->pidl("");
506 }
507
508 sub ParseFunction_Done($$$$)
509 {
510         my ($self, $if, $fn, $name) = @_;
511
512         my $state_str = "struct dcerpc_$name\_state";
513         my $done_fn = "dcerpc_$name\_done";
514
515         $self->pidl("static void $done_fn(struct tevent_req *subreq)");
516         $self->pidl("{");
517         $self->indent;
518         $self->pidl("struct tevent_req *req = tevent_req_callback_data(");
519         $self->pidl("\tsubreq, struct tevent_req);");
520         $self->pidl("$state_str *state = tevent_req_data(");
521         $self->pidl("\treq, $state_str);");
522         $self->pidl("NTSTATUS status;");
523         $self->pidl("TALLOC_CTX *mem_ctx;");
524         $self->pidl("");
525
526         $self->pidl("if (state->out_mem_ctx) {");
527         $self->indent;
528         $self->pidl("mem_ctx = state->out_mem_ctx;");
529         $self->deindent;
530         $self->pidl("} else {");
531         $self->indent;
532         $self->pidl("mem_ctx = state;");
533         $self->deindent;
534         $self->pidl("}");
535         $self->pidl("");
536
537         $self->pidl("status = dcerpc_$name\_r_recv(subreq, mem_ctx);");
538         $self->pidl("TALLOC_FREE(subreq);");
539         $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
540         $self->indent;
541         $self->pidl("tevent_req_nterror(req, status);");
542         $self->pidl("return;");
543         $self->deindent;
544         $self->pidl("}");
545         $self->pidl("");
546
547         $self->pidl("/* Copy out parameters */");
548         foreach my $e (@{$fn->{ELEMENTS}}) {
549                 next unless (grep(/out/, @{$e->{DIRECTION}}));
550
551                 $self->ParseOutputArgument($fn, $e,
552                                            "state->tmp.",
553                                            "state->orig.out.",
554                                            "async");
555         }
556         $self->pidl("");
557
558         if (defined($fn->{RETURN_TYPE})) {
559                 $self->pidl("/* Copy result */");
560                 $self->pidl("state->orig.out.result = state->tmp.out.result;");
561                 $self->pidl("");
562         }
563
564         $self->pidl("/* Reset temporary structure */");
565         $self->pidl("ZERO_STRUCT(state->tmp);");
566         $self->pidl("");
567
568         $self->pidl("tevent_req_done(req);");
569         $self->deindent;
570         $self->pidl("}");
571         $self->pidl("");
572 }
573
574 sub ParseFunction_Recv($$$$)
575 {
576         my ($self, $if, $fn, $name) = @_;
577
578         my $fn_args = "";
579         my $state_str = "struct dcerpc_$name\_state";
580         my $fn_str = "NTSTATUS dcerpc_$name\_recv";
581         my $pad = genpad($fn_str);
582
583         $fn_args .= "struct tevent_req *req,\n" . $pad . "TALLOC_CTX *mem_ctx";
584
585         if (defined($fn->{RETURN_TYPE})) {
586                 $fn_args .= ",\n" . $pad . mapTypeName($fn->{RETURN_TYPE}). " *result";
587         }
588
589         $self->fn_declare("$fn_str($fn_args)");
590         $self->pidl("{");
591         $self->indent;
592         $self->pidl("$state_str *state = tevent_req_data(");
593         $self->pidl("\treq, $state_str);");
594         $self->pidl("NTSTATUS status;");
595         $self->pidl("");
596         $self->pidl("if (tevent_req_is_nterror(req, &status)) {");
597         $self->indent;
598         $self->pidl("tevent_req_received(req);");
599         $self->pidl("return status;");
600         $self->deindent;
601         $self->pidl("}");
602         $self->pidl("");
603
604         $self->pidl("/* Steal possible out parameters to the callers context */");
605         $self->pidl("talloc_steal(mem_ctx, state->out_mem_ctx);");
606         $self->pidl("");
607
608         if (defined($fn->{RETURN_TYPE})) {
609                 $self->pidl("/* Return result */");
610                 $self->pidl("*result = state->orig.out.result;");
611                 $self->pidl("");
612         }
613
614         $self->pidl("tevent_req_received(req);");
615         $self->pidl("return NT_STATUS_OK;");
616         $self->deindent;
617         $self->pidl("}");
618         $self->pidl("");
619 }
620
621 sub ParseFunction_Sync($$$$)
622 {
623         my ($self, $if, $fn, $name) = @_;
624
625         my $uname = uc $name;
626         my $fn_args = "";
627         my $fn_str = "NTSTATUS dcerpc_$name";
628         my $pad = genpad($fn_str);
629
630         $fn_args .= "struct dcerpc_binding_handle *h,\n" . $pad . "TALLOC_CTX *mem_ctx";
631
632         foreach (@{$fn->{ELEMENTS}}) {
633                 my $dir = ElementDirection($_);
634                 my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]);
635                 $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */";
636         }
637
638         if (defined($fn->{RETURN_TYPE})) {
639                 $fn_args .= ",\n" . $pad . mapTypeName($fn->{RETURN_TYPE}). " *result";
640         }
641
642         $self->fn_declare("$fn_str($fn_args)");
643         $self->pidl("{");
644         $self->indent;
645         $self->pidl("struct $name r;");
646         $self->pidl("NTSTATUS status;");
647         $self->pidl("");
648
649         $self->pidl("/* In parameters */");
650         foreach my $e (@{$fn->{ELEMENTS}}) {
651                 next unless (grep(/in/, @{$e->{DIRECTION}}));
652
653                 $self->ParseCopyArgument($fn, $e, "r.in.", "_");
654         }
655         $self->pidl("");
656
657         $self->pidl("status = dcerpc_$name\_r(h, mem_ctx, &r);");
658         $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
659         $self->indent;
660         $self->pidl("return status;");
661         $self->deindent;
662         $self->pidl("}");
663         $self->pidl("");
664
665         $self->pidl("/* Return variables */");
666         foreach my $e (@{$fn->{ELEMENTS}}) {
667                 next unless (grep(/out/, @{$e->{DIRECTION}}));
668
669                 $self->ParseOutputArgument($fn, $e, "r.", "_", "sync");
670         }
671         $self->pidl("");
672
673         $self->pidl("/* Return result */");
674         if ($fn->{RETURN_TYPE}) {
675                 $self->pidl("*result = r.out.result;");
676         }
677         $self->pidl("");
678
679         $self->pidl("return NT_STATUS_OK;");
680
681         $self->deindent;
682         $self->pidl("}");
683         $self->pidl("");
684 }
685
686 #####################################################################
687 # parse a function
688 sub ParseFunction($$$)
689 {
690         my ($self, $if, $fn) = @_;
691
692         $self->ParseFunction_r_State($if, $fn, $fn->{NAME});
693         $self->ParseFunction_r_Send($if, $fn, $fn->{NAME});
694         $self->ParseFunction_r_Done($if, $fn, $fn->{NAME});
695         $self->ParseFunction_r_Recv($if, $fn, $fn->{NAME});
696         $self->ParseFunction_r_Sync($if, $fn, $fn->{NAME});
697
698         foreach my $e (@{$fn->{ELEMENTS}}) {
699                 next unless (grep(/out/, @{$e->{DIRECTION}}));
700
701                 my $reason = "is not a pointer or array";
702
703                 # TODO: make this fatal at NDR level
704                 if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") {
705                         if ($e->{LEVELS}[1]->{TYPE} eq "DATA" and
706                             $e->{LEVELS}[1]->{DATA_TYPE} eq "string") {
707                                 $reason = "is a pointer to type 'string'";
708                         } elsif ($e->{LEVELS}[1]->{TYPE} eq "ARRAY" and
709                                  $e->{LEVELS}[1]->{IS_ZERO_TERMINATED}) {
710                                 next;
711                         } elsif ($e->{LEVELS}[1]->{TYPE} eq "ARRAY" and
712                                  not defined($e->{LEVELS}[1]->{SIZE_IS})) {
713                                 $reason = "is a pointer to an unsized array";
714                         } else {
715                                 next;
716                         }
717                 }
718                 if ($e->{LEVELS}[0]->{TYPE} eq "ARRAY") {
719                         if (not defined($e->{LEVELS}[0]->{SIZE_IS})) {
720                                 $reason = "is an unsized array";
721                         } else {
722                                 next;
723                         }
724                 }
725
726                 $self->pidl_both("/*");
727                 $self->pidl_both(" * The following functions are skipped because");
728                 $self->pidl_both(" * an [out] argument $e->{NAME} $reason:");
729                 $self->pidl_both(" *");
730                 $self->pidl_both(" * dcerpc_$fn->{NAME}_send()");
731                 $self->pidl_both(" * dcerpc_$fn->{NAME}_recv()");
732                 $self->pidl_both(" * dcerpc_$fn->{NAME}()");
733                 $self->pidl_both(" */");
734                 $self->pidl_both("");
735
736                 error($e->{ORIGINAL}, "$fn->{NAME}: [out] argument '$e->{NAME}' $reason, skip client functions");
737                 return;
738         }
739
740         $self->ParseFunction_State($if, $fn, $fn->{NAME});
741         $self->ParseFunction_Send($if, $fn, $fn->{NAME});
742         $self->ParseFunction_Done($if, $fn, $fn->{NAME});
743         $self->ParseFunction_Recv($if, $fn, $fn->{NAME});
744         $self->ParseFunction_Sync($if, $fn, $fn->{NAME});
745
746         $self->pidl_hdr("");
747 }
748
749 my %done;
750
751 #####################################################################
752 # parse the interface definitions
753 sub ParseInterface($$)
754 {
755         my ($self, $if) = @_;
756         my $ifu = uc($if->{NAME});
757
758         $self->pidl_hdr("#ifndef _HEADER_RPC_$if->{NAME}");
759         $self->pidl_hdr("#define _HEADER_RPC_$if->{NAME}");
760         $self->pidl_hdr("");
761
762         if (defined $if->{PROPERTIES}->{uuid}) {
763                 $self->pidl_hdr("extern const struct ndr_interface_table ndr_table_$if->{NAME};");
764                 $self->pidl_hdr("");
765         }
766
767         $self->pidl("/* $if->{NAME} - client functions generated by pidl */");
768         $self->pidl("");
769
770         foreach my $fn (@{$if->{FUNCTIONS}}) {
771                 next if defined($done{$fn->{NAME}});
772                 next if has_property($fn, "noopnum");
773                 next if has_property($fn, "todo");
774                 $self->ParseFunction($if, $fn);
775                 $done{$fn->{NAME}} = 1;
776         }
777
778         $self->pidl_hdr("#endif /* _HEADER_RPC_$if->{NAME} */");
779 }
780
781 sub Parse($$$$$$)
782 {
783         my($self,$ndr,$header,$ndr_header,$client_header) = @_;
784
785         $self->pidl("/* client functions auto-generated by pidl */");
786         $self->pidl("");
787         if (is_intree()) {
788                 $self->pidl("#include \"includes.h\"");
789         } else {
790                 $self->pidl("#ifndef _GNU_SOURCE");
791                 $self->pidl("#define _GNU_SOURCE");
792                 $self->pidl("#endif");
793                 $self->pidl("#include <stdio.h>");
794                 $self->pidl("#include <stdbool.h>");
795                 $self->pidl("#include <stdlib.h>");
796                 $self->pidl("#include <stdint.h>");
797                 $self->pidl("#include <stdarg.h>");
798                 $self->pidl("#include <string.h>");
799                 $self->pidl("#include <core/ntstatus.h>");
800         }
801         $self->pidl("#include <tevent.h>");
802         $self->pidl(choose_header("lib/util/tevent_ntstatus.h", "util/tevent_ntstatus.h")."");
803         $self->pidl("#include \"$ndr_header\"");
804         $self->pidl("#include \"$client_header\"");
805         $self->pidl("");
806
807         $self->pidl_hdr(choose_header("librpc/rpc/dcerpc.h", "dcerpc.h")."");
808         $self->pidl_hdr("#include \"$header\"");
809
810         foreach my $x (@{$ndr}) {
811                 ($x->{TYPE} eq "INTERFACE") && $self->ParseInterface($x);
812         }
813
814         return ($self->{res},$self->{res_hdr});
815 }
816
817 1;