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