5cb1c8290c55d88de49360189d05a50a846e922e
[metze/samba/wip.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
351                 my $in_var = undef;
352                 if (grep(/in/, @{$e->{DIRECTION}})) {
353                         $in_var = ParseExpr($e->{NAME}, $in_env, $e->{ORIGINAL});
354                 }
355                 my $out_var = ParseExpr($e->{NAME}, $out_env, $e->{ORIGINAL});
356
357                 my $in_size_is = undef;
358                 my $out_size_is = undef;
359                 my $out_length_is = undef;
360
361                 my $avail_len = undef;
362                 my $needed_len = undef;
363
364                 $self->pidl("{");
365                 $self->indent;
366                 my $copy_len_var = "_copy_len_$e->{NAME}";
367                 $self->pidl("size_t $copy_len_var;");
368
369                 if (not defined($l->{SIZE_IS})) {
370                         if (not $l->{IS_ZERO_TERMINATED}) {
371                                 fatal($e->{ORIGINAL}, "no size known for [out] array `$e->{NAME}'");
372                         }
373                         if (has_property($e, "charset")) {
374                                 $avail_len = "ndr_charset_length($in_var, CH_UNIX)";
375                                 $needed_len = "ndr_charset_length($out_var, CH_UNIX)";
376                         } else {
377                                 $avail_len = "ndr_string_length($in_var, sizeof(*$in_var))";
378                                 $needed_len = "ndr_string_length($out_var, sizeof(*$out_var))";
379                         }
380                         $in_size_is = "";
381                         $out_size_is = "";
382                         $out_length_is = "";
383                 } else {
384                         $in_size_is = ParseExpr($l->{SIZE_IS}, $in_env, $e->{ORIGINAL});
385                         $out_size_is = ParseExpr($l->{SIZE_IS}, $out_env, $e->{ORIGINAL});
386                         $out_length_is = $out_size_is;
387                         if (defined($l->{LENGTH_IS})) {
388                                 $out_length_is = ParseExpr($l->{LENGTH_IS}, $out_env, $e->{ORIGINAL});
389                         }
390                         if (has_property($e, "charset")) {
391                                 if (defined($in_var)) {
392                                         $avail_len = "ndr_charset_length($in_var, CH_UNIX)";
393                                 } else {
394                                         $avail_len = $out_length_is;
395                                 }
396                                 $needed_len = "ndr_charset_length($out_var, CH_UNIX)";
397                         }
398                 }
399
400                 if ($out_size_is ne $in_size_is) {
401                         $self->pidl("if (($out_size_is) > ($in_size_is)) {");
402                         $self->indent;
403                         $self->ParseInvalidResponse($invalid_response_type);
404                         $self->deindent;
405                         $self->pidl("}");
406                 }
407                 if ($out_length_is ne $out_size_is) {
408                         $self->pidl("if (($out_length_is) > ($out_size_is)) {");
409                         $self->indent;
410                         $self->ParseInvalidResponse($invalid_response_type);
411                         $self->deindent;
412                         $self->pidl("}");
413                 }
414                 if (defined($needed_len)) {
415                         $self->pidl("$copy_len_var = $needed_len;");
416                         $self->pidl("if ($copy_len_var > $avail_len) {");
417                         $self->indent;
418                         $self->ParseInvalidResponse($invalid_response_type);
419                         $self->deindent;
420                         $self->pidl("}");
421                 } else {
422                         $self->pidl("$copy_len_var = $out_length_is;");
423                 }
424
425                 if (has_property($e, "charset")) {
426                         $self->pidl("memcpy(discard_const_p(uint8_t *, $o$e->{NAME}), $out_var, $copy_len_var * sizeof(*$o$e->{NAME}));");
427                 } else {
428                         $self->pidl("memcpy($o$e->{NAME}, $out_var, $copy_len_var * sizeof(*$o$e->{NAME}));");
429                 }
430
431                 $self->deindent;
432                 $self->pidl("}");
433         } else {
434                 $self->pidl("*$o$e->{NAME} = *${r}out.$e->{NAME};");
435         }
436
437         if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") {
438                 if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") {
439                         $self->deindent;
440                         $self->pidl("}");
441                 }
442         }
443 }
444
445 sub ParseFunction_State($$$$)
446 {
447         my ($self, $if, $fn, $name) = @_;
448
449         my $state_str = "struct dcerpc_$name\_state";
450         my $done_fn = "dcerpc_$name\_done";
451
452         $self->pidl("$state_str {");
453         $self->indent;
454         $self->pidl("struct $name orig;");
455         $self->pidl("struct $name tmp;");
456         $self->pidl("TALLOC_CTX *out_mem_ctx;");
457         $self->deindent;
458         $self->pidl("};");
459         $self->pidl("");
460         $self->pidl("static void $done_fn(struct tevent_req *subreq);");
461         $self->pidl("");
462 }
463
464 sub ParseFunction_Send($$$$)
465 {
466         my ($self, $if, $fn, $name) = @_;
467
468         my $fn_args = "";
469         my $state_str = "struct dcerpc_$name\_state";
470         my $done_fn = "dcerpc_$name\_done";
471         my $out_mem_ctx = "dcerpc_$name\_out_memory";
472         my $fn_str = "struct tevent_req *dcerpc_$name\_send";
473         my $pad = genpad($fn_str);
474
475         $fn_args .= "TALLOC_CTX *mem_ctx";
476         $fn_args .= ",\n" . $pad . "struct tevent_context *ev";
477         $fn_args .= ",\n" . $pad . "struct dcerpc_binding_handle *h";
478
479         foreach (@{$fn->{ELEMENTS}}) {
480                 my $dir = ElementDirection($_);
481                 my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]);
482                 $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */";
483         }
484
485         $self->fn_declare("$fn_str($fn_args)");
486         $self->pidl("{");
487         $self->indent;
488         $self->pidl("struct tevent_req *req;");
489         $self->pidl("$state_str *state;");
490         $self->pidl("struct tevent_req *subreq;");
491         $self->pidl("");
492         $self->pidl("req = tevent_req_create(mem_ctx, &state,");
493         $self->pidl("\t\t\t$state_str);");
494         $self->pidl("if (req == NULL) {");
495         $self->indent;
496         $self->pidl("return NULL;");
497         $self->deindent;
498         $self->pidl("}");
499         $self->pidl("state->out_mem_ctx = NULL;");
500         $self->pidl("");
501
502         $self->pidl("/* In parameters */");
503         foreach my $e (@{$fn->{ELEMENTS}}) {
504                 next unless (grep(/in/, @{$e->{DIRECTION}}));
505
506                 $self->ParseCopyArgument($fn, $e, "state->orig.in.", "_");
507         }
508         $self->pidl("");
509
510         my $out_params = 0;
511         $self->pidl("/* Out parameters */");
512         foreach my $e (@{$fn->{ELEMENTS}}) {
513                 next unless grep(/out/, @{$e->{DIRECTION}});
514
515                 $self->ParseCopyArgument($fn, $e, "state->orig.out.", "_");
516                 $out_params++;
517         }
518         $self->pidl("");
519
520         if (defined($fn->{RETURN_TYPE})) {
521                 $self->pidl("/* Result */");
522                 $self->pidl("ZERO_STRUCT(state->orig.out.result);");
523                 $self->pidl("");
524         }
525
526         if ($out_params > 0) {
527                 $self->pidl("state->out_mem_ctx = talloc_named_const(state, 0,");
528                 $self->pidl("\t\t     \"$out_mem_ctx\");");
529                 $self->pidl("if (tevent_req_nomem(state->out_mem_ctx, req)) {");
530                 $self->indent;
531                 $self->pidl("return tevent_req_post(req, ev);");
532                 $self->deindent;
533                 $self->pidl("}");
534                 $self->pidl("");
535         }
536
537         $self->pidl("/* make a temporary copy, that we pass to the dispatch function */");
538         $self->pidl("state->tmp = state->orig;");
539         $self->pidl("");
540
541         $self->pidl("subreq = dcerpc_$name\_r_send(state, ev, h, &state->tmp);");
542         $self->pidl("if (tevent_req_nomem(subreq, req)) {");
543         $self->indent;
544         $self->pidl("return tevent_req_post(req, ev);");
545         $self->deindent;
546         $self->pidl("}");
547         $self->pidl("tevent_req_set_callback(subreq, $done_fn, req);");
548         $self->pidl("return req;");
549         $self->deindent;
550         $self->pidl("}");
551         $self->pidl("");
552 }
553
554 sub ParseFunction_Done($$$$)
555 {
556         my ($self, $if, $fn, $name) = @_;
557
558         my $state_str = "struct dcerpc_$name\_state";
559         my $done_fn = "dcerpc_$name\_done";
560
561         $self->pidl("static void $done_fn(struct tevent_req *subreq)");
562         $self->pidl("{");
563         $self->indent;
564         $self->pidl("struct tevent_req *req = tevent_req_callback_data(");
565         $self->pidl("\tsubreq, struct tevent_req);");
566         $self->pidl("$state_str *state = tevent_req_data(");
567         $self->pidl("\treq, $state_str);");
568         $self->pidl("NTSTATUS status;");
569         $self->pidl("TALLOC_CTX *mem_ctx;");
570         $self->pidl("");
571
572         $self->pidl("if (state->out_mem_ctx) {");
573         $self->indent;
574         $self->pidl("mem_ctx = state->out_mem_ctx;");
575         $self->deindent;
576         $self->pidl("} else {");
577         $self->indent;
578         $self->pidl("mem_ctx = state;");
579         $self->deindent;
580         $self->pidl("}");
581         $self->pidl("");
582
583         $self->pidl("status = dcerpc_$name\_r_recv(subreq, mem_ctx);");
584         $self->pidl("TALLOC_FREE(subreq);");
585         $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
586         $self->indent;
587         $self->pidl("tevent_req_nterror(req, status);");
588         $self->pidl("return;");
589         $self->deindent;
590         $self->pidl("}");
591         $self->pidl("");
592
593         $self->pidl("/* Copy out parameters */");
594         foreach my $e (@{$fn->{ELEMENTS}}) {
595                 next unless (grep(/out/, @{$e->{DIRECTION}}));
596
597                 $self->ParseOutputArgument($fn, $e,
598                                            "state->tmp.",
599                                            "state->orig.out.",
600                                            "async");
601         }
602         $self->pidl("");
603
604         if (defined($fn->{RETURN_TYPE})) {
605                 $self->pidl("/* Copy result */");
606                 $self->pidl("state->orig.out.result = state->tmp.out.result;");
607                 $self->pidl("");
608         }
609
610         $self->pidl("/* Reset temporary structure */");
611         $self->pidl("ZERO_STRUCT(state->tmp);");
612         $self->pidl("");
613
614         $self->pidl("tevent_req_done(req);");
615         $self->deindent;
616         $self->pidl("}");
617         $self->pidl("");
618 }
619
620 sub ParseFunction_Recv($$$$)
621 {
622         my ($self, $if, $fn, $name) = @_;
623
624         my $fn_args = "";
625         my $state_str = "struct dcerpc_$name\_state";
626         my $fn_str = "NTSTATUS dcerpc_$name\_recv";
627         my $pad = genpad($fn_str);
628
629         $fn_args .= "struct tevent_req *req,\n" . $pad . "TALLOC_CTX *mem_ctx";
630
631         if (defined($fn->{RETURN_TYPE})) {
632                 $fn_args .= ",\n" . $pad . mapTypeName($fn->{RETURN_TYPE}). " *result";
633         }
634
635         $self->fn_declare("$fn_str($fn_args)");
636         $self->pidl("{");
637         $self->indent;
638         $self->pidl("$state_str *state = tevent_req_data(");
639         $self->pidl("\treq, $state_str);");
640         $self->pidl("NTSTATUS status;");
641         $self->pidl("");
642         $self->pidl("if (tevent_req_is_nterror(req, &status)) {");
643         $self->indent;
644         $self->pidl("tevent_req_received(req);");
645         $self->pidl("return status;");
646         $self->deindent;
647         $self->pidl("}");
648         $self->pidl("");
649
650         $self->pidl("/* Steal possible out parameters to the callers context */");
651         $self->pidl("talloc_steal(mem_ctx, state->out_mem_ctx);");
652         $self->pidl("");
653
654         if (defined($fn->{RETURN_TYPE})) {
655                 $self->pidl("/* Return result */");
656                 $self->pidl("*result = state->orig.out.result;");
657                 $self->pidl("");
658         }
659
660         $self->pidl("tevent_req_received(req);");
661         $self->pidl("return NT_STATUS_OK;");
662         $self->deindent;
663         $self->pidl("}");
664         $self->pidl("");
665 }
666
667 sub ParseFunction_Sync($$$$)
668 {
669         my ($self, $if, $fn, $name) = @_;
670
671         my $uname = uc $name;
672         my $fn_args = "";
673         my $fn_str = "NTSTATUS dcerpc_$name";
674         my $pad = genpad($fn_str);
675
676         $fn_args .= "struct dcerpc_binding_handle *h,\n" . $pad . "TALLOC_CTX *mem_ctx";
677
678         foreach (@{$fn->{ELEMENTS}}) {
679                 my $dir = ElementDirection($_);
680                 my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]);
681                 $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */";
682         }
683
684         if (defined($fn->{RETURN_TYPE})) {
685                 $fn_args .= ",\n" . $pad . mapTypeName($fn->{RETURN_TYPE}). " *result";
686         }
687
688         $self->pidl_hdr("#ifndef DCERPC_CALL_$uname\_COMPAT");
689         $self->fn_declare("$fn_str($fn_args)");
690         $self->pidl_hdr("#endif /* DCERPC_CALL_$uname\_COMPAT */");
691         $self->pidl("{");
692         $self->indent;
693         $self->pidl("struct $name r;");
694         $self->pidl("NTSTATUS status;");
695         $self->pidl("");
696
697         $self->pidl("/* In parameters */");
698         foreach my $e (@{$fn->{ELEMENTS}}) {
699                 next unless (grep(/in/, @{$e->{DIRECTION}}));
700
701                 $self->ParseCopyArgument($fn, $e, "r.in.", "_");
702         }
703         $self->pidl("");
704
705         $self->pidl("status = dcerpc_$name\_r(h, mem_ctx, &r);");
706         $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
707         $self->indent;
708         $self->pidl("return status;");
709         $self->deindent;
710         $self->pidl("}");
711         $self->pidl("");
712
713         $self->pidl("/* Return variables */");
714         foreach my $e (@{$fn->{ELEMENTS}}) {
715                 next unless (grep(/out/, @{$e->{DIRECTION}}));
716
717                 $self->ParseOutputArgument($fn, $e, "r.", "_", "sync");
718         }
719         $self->pidl("");
720
721         $self->pidl("/* Return result */");
722         if ($fn->{RETURN_TYPE}) {
723                 $self->pidl("*result = r.out.result;");
724         }
725         $self->pidl("");
726
727         $self->pidl("return NT_STATUS_OK;");
728
729         $self->deindent;
730         $self->pidl("}");
731         $self->pidl("");
732 }
733
734 #####################################################################
735 # parse a function
736 sub ParseFunction($$$)
737 {
738         my ($self, $if, $fn) = @_;
739
740         $self->ParseFunction_r_State($if, $fn, $fn->{NAME});
741         $self->ParseFunction_r_Send($if, $fn, $fn->{NAME});
742         $self->ParseFunction_r_Done($if, $fn, $fn->{NAME});
743         $self->ParseFunction_r_Recv($if, $fn, $fn->{NAME});
744         $self->ParseFunction_r_Sync($if, $fn, $fn->{NAME});
745
746         $self->ParseFunction_Compat_Sync($if, $fn, $fn->{NAME});
747
748         foreach my $e (@{$fn->{ELEMENTS}}) {
749                 next unless (grep(/out/, @{$e->{DIRECTION}}));
750
751                 my $reason = "is not a pointer or array";
752
753                 # TODO: make this fatal at NDR level
754                 if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") {
755                         if ($e->{LEVELS}[1]->{TYPE} eq "DATA" and
756                             $e->{LEVELS}[1]->{DATA_TYPE} eq "string") {
757                                 $reason = "is a pointer to type 'string'";
758                         } elsif ($e->{LEVELS}[1]->{TYPE} eq "ARRAY" and
759                                  $e->{LEVELS}[1]->{IS_ZERO_TERMINATED}) {
760                                 next;
761                                 $reason = "is a pointer to an unsized array";
762                         } elsif ($e->{LEVELS}[1]->{TYPE} eq "ARRAY" and
763                                  not defined($e->{LEVELS}[1]->{SIZE_IS})) {
764                                 $reason = "is a pointer to an unsized array";
765                         } else {
766                                 next;
767                         }
768                 }
769                 if ($e->{LEVELS}[0]->{TYPE} eq "ARRAY") {
770                         if (not defined($e->{LEVELS}[0]->{SIZE_IS})) {
771                                 $reason = "is an unsized array";
772                         } else {
773                                 next;
774                         }
775                 }
776
777                 $self->pidl_both("/*");
778                 $self->pidl_both(" * The following functions are skipped because");
779                 $self->pidl_both(" * an [out] argument $e->{NAME} $reason:");
780                 $self->pidl_both(" *");
781                 $self->pidl_both(" * dcerpc_$fn->{NAME}_send()");
782                 $self->pidl_both(" * dcerpc_$fn->{NAME}_recv()");
783                 $self->pidl_both(" * dcerpc_$fn->{NAME}()");
784                 $self->pidl_both(" */");
785                 $self->pidl_both("");
786
787                 error($e->{ORIGINAL}, "$fn->{NAME}: [out] argument '$e->{NAME}' $reason, skip client functions");
788                 return;
789         }
790
791         $self->ParseFunction_State($if, $fn, $fn->{NAME});
792         $self->ParseFunction_Send($if, $fn, $fn->{NAME});
793         $self->ParseFunction_Done($if, $fn, $fn->{NAME});
794         $self->ParseFunction_Recv($if, $fn, $fn->{NAME});
795         $self->ParseFunction_Sync($if, $fn, $fn->{NAME});
796
797         $self->pidl_hdr("");
798 }
799
800 my %done;
801
802 #####################################################################
803 # parse the interface definitions
804 sub ParseInterface($$)
805 {
806         my ($self, $if) = @_;
807         my $ifu = uc($if->{NAME});
808
809         $self->pidl_hdr("#ifndef _HEADER_RPC_$if->{NAME}");
810         $self->pidl_hdr("#define _HEADER_RPC_$if->{NAME}");
811         $self->pidl_hdr("");
812
813         if (defined $if->{PROPERTIES}->{uuid}) {
814                 $self->pidl_hdr("extern const struct ndr_interface_table ndr_table_$if->{NAME};");
815                 $self->pidl_hdr("");
816         }
817
818         $self->pidl_hdr("#ifdef DCERPC_IFACE_$ifu\_COMPAT");
819         foreach my $fn (@{$if->{FUNCTIONS}}) {
820                 next if has_property($fn, "noopnum");
821                 next if has_property($fn, "todo");
822                 my $fnu = uc($fn->{NAME});
823                 $self->pidl_hdr("#define DCERPC_CALL_$fnu\_COMPAT 1");
824         }
825         $self->pidl_hdr("#endif /* DCERPC_IFACE_$ifu\_COMPAT */");
826         $self->pidl_hdr("");
827
828         $self->pidl("/* $if->{NAME} - client functions generated by pidl */");
829         $self->pidl("");
830
831         foreach my $fn (@{$if->{FUNCTIONS}}) {
832                 next if defined($done{$fn->{NAME}});
833                 next if has_property($fn, "noopnum");
834                 next if has_property($fn, "todo");
835                 $self->ParseFunction($if, $fn);
836                 $done{$fn->{NAME}} = 1;
837         }
838
839         $self->pidl_hdr("#endif /* _HEADER_RPC_$if->{NAME} */");
840 }
841
842 sub Parse($$$$$$)
843 {
844         my($self,$ndr,$header,$ndr_header,$client_header) = @_;
845
846         $self->pidl("/* client functions auto-generated by pidl */");
847         $self->pidl("");
848         if (is_intree()) {
849                 $self->pidl("#include \"includes.h\"");
850         } else {
851                 $self->pidl("#ifndef _GNU_SOURCE");
852                 $self->pidl("#define _GNU_SOURCE");
853                 $self->pidl("#endif");
854                 $self->pidl("#include <stdio.h>");
855                 $self->pidl("#include <stdbool.h>");
856                 $self->pidl("#include <stdlib.h>");
857                 $self->pidl("#include <stdint.h>");
858                 $self->pidl("#include <stdarg.h>");
859                 $self->pidl("#include <string.h>");
860                 $self->pidl("#include <core/ntstatus.h>");
861         }
862         $self->pidl("#include <tevent.h>");
863         $self->pidl(choose_header("lib/util/tevent_ntstatus.h", "util/tevent_ntstatus.h")."");
864         $self->pidl("#include \"$ndr_header\"");
865         $self->pidl("#include \"$client_header\"");
866         $self->pidl("");
867
868         $self->pidl_hdr(choose_header("librpc/rpc/dcerpc.h", "dcerpc.h")."");
869         $self->pidl_hdr("#include \"$header\"");
870
871         foreach my $x (@{$ndr}) {
872                 ($x->{TYPE} eq "INTERFACE") && $self->ParseInterface($x);
873         }
874
875         return ($self->{res},$self->{res_hdr});
876 }
877
878 1;