pidl:Samba3/ClientNDR: implement rpccli_ stubs on top of dcerpc_ stubs
[samba.git] / pidl / lib / Parse / Pidl / Samba3 / ClientNDR.pm
1 ###################################################
2 # Samba3 client generator for IDL structures
3 # on top of Samba4 style NDR functions
4 # Copyright jelmer@samba.org 2005-2006
5 # Copyright gd@samba.org 2008
6 # released under the GNU GPL
7
8 package Parse::Pidl::Samba3::ClientNDR;
9
10 use Exporter;
11 @ISA = qw(Exporter);
12 @EXPORT_OK = qw(ParseFunction $res $res_hdr ParseOutputArgument);
13
14 use strict;
15 use Parse::Pidl qw(fatal warning error);
16 use Parse::Pidl::Util qw(has_property ParseExpr);
17 use Parse::Pidl::Typelist qw(mapTypeName);
18 use Parse::Pidl::Samba4 qw(DeclLong);
19 use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv);
20
21 use vars qw($VERSION);
22 $VERSION = '0.01';
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 fn_declare($$) { my ($self,$n) = @_; $self->pidl($n); $self->pidl_hdr("$n;"); }
29
30 sub genpad($)
31 {
32         my ($s) = @_;
33         my $nt = int((length($s)+1)/8);
34         my $lt = ($nt*8)-1;
35         my $ns = (length($s)-$lt);
36         return "\t"x($nt)." "x($ns);
37 }
38
39 sub new($)
40 {
41         my ($class) = shift;
42         my $self = { res => "", res_hdr => "", tabs => "" };
43         bless($self, $class);
44 }
45
46 sub ElementDirection($)
47 {
48         my ($e) = @_;
49
50         return "[in,out]" if (has_property($e, "in") and has_property($e, "out"));
51         return "[in]" if (has_property($e, "in"));
52         return "[out]" if (has_property($e, "out"));
53         return "[in,out]";
54 }
55
56 sub HeaderProperties($$)
57 {
58         my($props,$ignores) = @_;
59         my $ret = "";
60
61         foreach my $d (keys %{$props}) {
62                 next if (grep(/^$d$/, @$ignores));
63                 if($props->{$d} ne "1") {
64                         $ret.= "$d($props->{$d}),";
65                 } else {
66                         $ret.="$d,";
67                 }
68         }
69
70         if ($ret) {
71                 return "[" . substr($ret, 0, -1) . "]";
72         }
73 }
74
75 sub ParseInvalidResponse($$)
76 {
77         my ($self, $type) = @_;
78
79         if ($type eq "sync") {
80                 $self->pidl("return NT_STATUS_INVALID_NETWORK_RESPONSE;");
81         } elsif ($type eq "async") {
82                 $self->pidl("tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);");
83                 $self->pidl("return;");
84         } else {
85                 die("ParseInvalidResponse($type)");
86         }
87 }
88
89 sub ParseOutputArgument($$$;$$$)
90 {
91         my ($self, $fn, $e, $r, $o, $invalid_response_type) = @_;
92         my $level = 0;
93         $r = "r." unless defined($r);
94         $o = "" unless defined($o);
95         $invalid_response_type = "sync" unless defined($invalid_response_type);
96
97         if ($e->{LEVELS}[0]->{TYPE} ne "POINTER" and $e->{LEVELS}[0]->{TYPE} ne "ARRAY") {
98                 $self->pidl("return NT_STATUS_NOT_SUPPORTED;");
99                 error($e->{ORIGINAL}, "[out] argument is not a pointer or array");
100                 return;
101         }
102
103         if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") {
104                 $level = 1;
105                 if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") {
106                         $self->pidl("if ($o$e->{NAME} && ${r}out.$e->{NAME}) {");
107                         $self->indent;
108                 }
109         }
110
111         if ($e->{LEVELS}[$level]->{TYPE} eq "ARRAY") {
112                 # This is a call to GenerateFunctionInEnv intentionally. 
113                 # Since the data is being copied into a user-provided data 
114                 # structure, the user should be able to know the size beforehand 
115                 # to allocate a structure of the right size.
116                 my $in_env = GenerateFunctionInEnv($fn, $r);
117                 my $out_env = GenerateFunctionOutEnv($fn, $r);
118                 my $l = $e->{LEVELS}[$level];
119                 unless (defined($l->{SIZE_IS})) {
120                         $self->pidl('#error No size known for [out] array `$e->{NAME}');
121                         error($e->{ORIGINAL}, "no size known for [out] array `$e->{NAME}'");
122                 } else {
123                         my $in_size_is = ParseExpr($l->{SIZE_IS}, $in_env, $e->{ORIGINAL});
124                         my $out_size_is = ParseExpr($l->{SIZE_IS}, $out_env, $e->{ORIGINAL});
125                         my $out_length_is = $out_size_is;
126                         if (defined($l->{LENGTH_IS})) {
127                                 $out_length_is = ParseExpr($l->{LENGTH_IS}, $out_env, $e->{ORIGINAL});
128                         }
129                         if ($out_size_is ne $in_size_is) {
130                                 $self->pidl("if (($out_size_is) > ($in_size_is)) {");
131                                 $self->indent;
132                                 $self->ParseInvalidResponse($invalid_response_type);
133                                 $self->deindent;
134                                 $self->pidl("}");
135                         }
136                         if ($out_length_is ne $out_size_is) {
137                                 $self->pidl("if (($out_length_is) > ($out_size_is)) {");
138                                 $self->indent;
139                                 $self->ParseInvalidResponse($invalid_response_type);
140                                 $self->deindent;
141                                 $self->pidl("}");
142                         }
143                         if (has_property($e, "charset")) {
144                                 $self->pidl("memcpy(discard_const_p(uint8_t *, $o$e->{NAME}), ${r}out.$e->{NAME}, ($out_length_is) * sizeof(*$o$e->{NAME}));");
145                         } else {
146                                 $self->pidl("memcpy($o$e->{NAME}, ${r}out.$e->{NAME}, ($out_length_is) * sizeof(*$o$e->{NAME}));");
147                         }
148                 }
149         } else {
150                 $self->pidl("*$o$e->{NAME} = *${r}out.$e->{NAME};");
151         }
152
153         if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") {
154                 if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") {
155                         $self->deindent;
156                         $self->pidl("}");
157                 }
158         }
159 }
160
161 sub ParseFunctionAsyncState($$$)
162 {
163         my ($self, $if, $fn) = @_;
164
165         my $state_str = "struct rpccli_$fn->{NAME}_state";
166         my $done_fn = "rpccli_$fn->{NAME}_done";
167
168         $self->pidl("$state_str {");
169         $self->indent;
170         $self->pidl("TALLOC_CTX *out_mem_ctx;");
171         if (defined($fn->{RETURN_TYPE})) {
172                 $self->pidl(mapTypeName($fn->{RETURN_TYPE}). " result;");
173         }
174         $self->deindent;
175         $self->pidl("};");
176         $self->pidl("");
177         $self->pidl("static void $done_fn(struct tevent_req *subreq);");
178         $self->pidl("");
179 }
180
181 sub ParseFunctionAsyncSend($$$)
182 {
183         my ($self, $if, $fn) = @_;
184
185         my $fn_args = "";
186         my $uif = uc($if);
187         my $ufn = "NDR_".uc($fn->{NAME});
188         my $state_str = "struct rpccli_$fn->{NAME}_state";
189         my $done_fn = "rpccli_$fn->{NAME}_done";
190         my $out_mem_ctx = "rpccli_$fn->{NAME}_out_memory";
191         my $fn_str = "struct tevent_req *rpccli_$fn->{NAME}_send";
192         my $pad = genpad($fn_str);
193
194         $fn_args .= "TALLOC_CTX *mem_ctx";
195         $fn_args .= ",\n" . $pad . "struct tevent_context *ev";
196         $fn_args .= ",\n" . $pad . "struct rpc_pipe_client *cli";
197
198         foreach (@{$fn->{ELEMENTS}}) {
199                 my $dir = ElementDirection($_);
200                 my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]);
201                 $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */";
202         }
203
204         $self->fn_declare("$fn_str($fn_args)");
205         $self->pidl("{");
206         $self->indent;
207         $self->pidl("struct tevent_req *req;");
208         $self->pidl("$state_str *state;");
209         $self->pidl("struct tevent_req *subreq;");
210         $self->pidl("");
211         $self->pidl("req = tevent_req_create(mem_ctx, &state,");
212         $self->pidl("\t\t\t$state_str);");
213         $self->pidl("if (req == NULL) {");
214         $self->indent;
215         $self->pidl("return NULL;");
216         $self->deindent;
217         $self->pidl("}");
218         $self->pidl("state->out_mem_ctx = NULL;");
219         $self->pidl("");
220
221         my $out_params = 0;
222         foreach (@{$fn->{ELEMENTS}}) {
223                 if (grep(/out/, @{$_->{DIRECTION}})) {
224                         $out_params++;
225                 }
226         }
227
228         if ($out_params > 0) {
229                 $self->pidl("state->out_mem_ctx = talloc_named_const(state, 0,");
230                 $self->pidl("\t\t     \"$out_mem_ctx\");");
231                 $self->pidl("if (tevent_req_nomem(state->out_mem_ctx, req)) {");
232                 $self->indent;
233                 $self->pidl("return tevent_req_post(req, ev);");
234                 $self->deindent;
235                 $self->pidl("}");
236                 $self->pidl("");
237         }
238
239         $fn_str = "subreq = dcerpc_$fn->{NAME}_send";
240         $pad = "\t" . genpad($fn_str);
241         $fn_args = "state,\n" . $pad . "ev,\n" . $pad . "cli->binding_handle";
242         foreach (@{$fn->{ELEMENTS}}) {
243                 $fn_args .= ",\n" . $pad . "_". $_->{NAME};
244         }
245
246         $self->pidl("$fn_str($fn_args);");
247         $self->pidl("if (tevent_req_nomem(subreq, req)) {");
248         $self->indent;
249         $self->pidl("return tevent_req_post(req, ev);");
250         $self->deindent;
251         $self->pidl("}");
252         $self->pidl("tevent_req_set_callback(subreq, $done_fn, req);");
253         $self->pidl("return req;");
254         $self->deindent;
255         $self->pidl("}");
256         $self->pidl("");
257 }
258
259 sub ParseFunctionAsyncDone($$$)
260 {
261         my ($self, $if, $fn) = @_;
262
263         my $state_str = "struct rpccli_$fn->{NAME}_state";
264         my $done_fn = "rpccli_$fn->{NAME}_done";
265
266         $self->pidl("static void $done_fn(struct tevent_req *subreq)");
267         $self->pidl("{");
268         $self->indent;
269         $self->pidl("struct tevent_req *req = tevent_req_callback_data(");
270         $self->pidl("\tsubreq, struct tevent_req);");
271         $self->pidl("$state_str *state = tevent_req_data(");
272         $self->pidl("\treq, $state_str);");
273         $self->pidl("NTSTATUS status;");
274         $self->pidl("TALLOC_CTX *mem_ctx;");
275         $self->pidl("");
276
277         $self->pidl("if (state->out_mem_ctx) {");
278         $self->indent;
279         $self->pidl("mem_ctx = state->out_mem_ctx;");
280         $self->deindent;
281         $self->pidl("} else {");
282         $self->indent;
283         $self->pidl("mem_ctx = state;");
284         $self->deindent;
285         $self->pidl("}");
286         $self->pidl("");
287
288         my $fn_str = "status = dcerpc_$fn->{NAME}_recv";
289         my $pad = "\t" . genpad($fn_str);
290         my $fn_args = "subreq,\n" . $pad . "mem_ctx";
291         if (defined($fn->{RETURN_TYPE})) {
292                 $fn_args .= ",\n" . $pad . "&state->result";
293         }
294
295         $self->pidl("$fn_str($fn_args);");
296         $self->pidl("TALLOC_FREE(subreq);");
297         $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
298         $self->indent;
299         $self->pidl("tevent_req_nterror(req, status);");
300         $self->pidl("return;");
301         $self->deindent;
302         $self->pidl("}");
303         $self->pidl("");
304
305         $self->pidl("tevent_req_done(req);");
306         $self->deindent;
307         $self->pidl("}");
308         $self->pidl("");
309 }
310
311 sub ParseFunctionAsyncRecv($$$)
312 {
313         my ($self, $if, $fn) = @_;
314
315         my $fn_args = "";
316         my $state_str = "struct rpccli_$fn->{NAME}_state";
317         my $fn_str = "NTSTATUS rpccli_$fn->{NAME}_recv";
318         my $pad = genpad($fn_str);
319
320         $fn_args .= "struct tevent_req *req,\n" . $pad . "TALLOC_CTX *mem_ctx";
321
322         if (defined($fn->{RETURN_TYPE})) {
323                 $fn_args .= ",\n" . $pad . "$fn->{RETURN_TYPE} *result";
324         }
325
326         $self->fn_declare("$fn_str($fn_args)");
327         $self->pidl("{");
328         $self->indent;
329         $self->pidl("$state_str *state = tevent_req_data(");
330         $self->pidl("\treq, $state_str);");
331         $self->pidl("NTSTATUS status;");
332         $self->pidl("");
333         $self->pidl("if (tevent_req_is_nterror(req, &status)) {");
334         $self->indent;
335         $self->pidl("tevent_req_received(req);");
336         $self->pidl("return status;");
337         $self->deindent;
338         $self->pidl("}");
339         $self->pidl("");
340
341         $self->pidl("/* Steal possbile out parameters to the callers context */");
342         $self->pidl("talloc_steal(mem_ctx, state->out_mem_ctx);");
343         $self->pidl("");
344
345         if (defined($fn->{RETURN_TYPE})) {
346                 $self->pidl("/* Return result */");
347                 $self->pidl("*result = state->result;");
348                 $self->pidl("");
349         }
350
351         $self->pidl("tevent_req_received(req);");
352         $self->pidl("return NT_STATUS_OK;");
353         $self->deindent;
354         $self->pidl("}");
355         $self->pidl("");
356 }
357
358 sub ParseFunctionSync($$$)
359 {
360         my ($self, $if, $fn) = @_;
361
362         my $fn_args = "";
363         my $uif = uc($if);
364         my $ufn = "NDR_".uc($fn->{NAME});
365         my $fn_str = "NTSTATUS rpccli_$fn->{NAME}";
366         my $pad = genpad($fn_str);
367
368         $fn_args .= "struct rpc_pipe_client *cli,\n" . $pad . "TALLOC_CTX *mem_ctx";
369
370         foreach (@{$fn->{ELEMENTS}}) {
371                 my $dir = ElementDirection($_);
372                 my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]);
373                 $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */";
374         }
375
376         if (defined($fn->{RETURN_TYPE}) && ($fn->{RETURN_TYPE} eq "WERROR")) {
377                 $fn_args .= ",\n" . $pad . "WERROR *werror";
378         }
379
380         $self->fn_declare("$fn_str($fn_args)");
381         $self->pidl("{");
382         $self->indent;
383         if (defined($fn->{RETURN_TYPE})) {
384                 $self->pidl(mapTypeName($fn->{RETURN_TYPE})." result;");
385         }
386         $self->pidl("NTSTATUS status;");
387         $self->pidl("");
388
389         $fn_str = "status = dcerpc_$fn->{NAME}";
390         $pad = "\t" . genpad($fn_str);
391         $fn_args = "cli->binding_handle,\n" . $pad . "mem_ctx";
392         foreach (@{$fn->{ELEMENTS}}) {
393                 $fn_args .= ",\n" . $pad . "_". $_->{NAME};
394         }
395         if (defined($fn->{RETURN_TYPE})) {
396                 $fn_args .= ",\n" . $pad . "&result";
397         }
398
399         $self->pidl("$fn_str($fn_args);");
400         $self->pidl("if (!NT_STATUS_IS_OK(status)) {");
401         $self->indent;
402         $self->pidl("return status;");
403         $self->deindent;
404         $self->pidl("}");
405         $self->pidl("");
406
407         $self->pidl("/* Return result */");
408         if (not $fn->{RETURN_TYPE}) {
409                 $self->pidl("return NT_STATUS_OK;");
410         } elsif ($fn->{RETURN_TYPE} eq "NTSTATUS") {
411                 $self->pidl("return result;");
412         } elsif ($fn->{RETURN_TYPE} eq "WERROR") {
413                 $self->pidl("if (werror) {");
414                 $self->indent;
415                 $self->pidl("*werror = result;");
416                 $self->deindent;
417                 $self->pidl("}");
418                 $self->pidl("");
419                 $self->pidl("return werror_to_ntstatus(result);");
420         } else {
421                 warning($fn->{ORIGINAL}, "Unable to convert $fn->{RETURN_TYPE} to NTSTATUS");
422                 $self->pidl("return NT_STATUS_OK;");
423         }
424
425         $self->deindent;
426         $self->pidl("}");
427         $self->pidl("");
428 }
429
430 sub ParseFunction($$$)
431 {
432         my ($self, $if, $fn) = @_;
433
434         $self->ParseFunctionAsyncState($if, $fn);
435         $self->ParseFunctionAsyncSend($if, $fn);
436         $self->ParseFunctionAsyncDone($if, $fn);
437         $self->ParseFunctionAsyncRecv($if, $fn);
438
439         $self->ParseFunctionSync($if, $fn);
440 }
441
442 sub ParseInterface($$)
443 {
444         my ($self, $if) = @_;
445
446         my $uif = uc($if->{NAME});
447
448         $self->pidl_hdr("#ifndef __CLI_$uif\__");
449         $self->pidl_hdr("#define __CLI_$uif\__");
450         foreach my $fn (@{$if->{FUNCTIONS}}) {
451                 next if has_property($fn, "noopnum");
452                 next if has_property($fn, "todo");
453                 $self->ParseFunction($if->{NAME}, $fn);
454         }
455         $self->pidl_hdr("#endif /* __CLI_$uif\__ */");
456 }
457
458 sub Parse($$$$)
459 {
460         my($self,$ndr,$header,$c_header) = @_;
461
462         $self->pidl("/*");
463         $self->pidl(" * Unix SMB/CIFS implementation.");
464         $self->pidl(" * client auto-generated by pidl. DO NOT MODIFY!");
465         $self->pidl(" */");
466         $self->pidl("");
467         $self->pidl("#include \"includes.h\"");
468         $self->pidl("#include \"$header\"");
469         $self->pidl_hdr("#include \"$c_header\"");
470         $self->pidl("");
471         
472         foreach (@$ndr) {
473                 $self->ParseInterface($_) if ($_->{TYPE} eq "INTERFACE");
474         }
475
476         return ($self->{res}, $self->{res_hdr});
477 }
478
479 1;