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