From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- pidl/lib/Parse/Pidl/Samba4/COM/Header.pm | 161 ++ pidl/lib/Parse/Pidl/Samba4/COM/Proxy.pm | 226 ++ pidl/lib/Parse/Pidl/Samba4/COM/Stub.pm | 322 +++ pidl/lib/Parse/Pidl/Samba4/Header.pm | 538 ++++ pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm | 877 ++++++ pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm | 3386 ++++++++++++++++++++++++ pidl/lib/Parse/Pidl/Samba4/NDR/Server.pm | 358 +++ pidl/lib/Parse/Pidl/Samba4/NDR/ServerCompat.pm | 624 +++++ pidl/lib/Parse/Pidl/Samba4/Python.pm | 2504 ++++++++++++++++++ pidl/lib/Parse/Pidl/Samba4/TDR.pm | 278 ++ pidl/lib/Parse/Pidl/Samba4/Template.pm | 104 + 11 files changed, 9378 insertions(+) create mode 100644 pidl/lib/Parse/Pidl/Samba4/COM/Header.pm create mode 100644 pidl/lib/Parse/Pidl/Samba4/COM/Proxy.pm create mode 100644 pidl/lib/Parse/Pidl/Samba4/COM/Stub.pm create mode 100644 pidl/lib/Parse/Pidl/Samba4/Header.pm create mode 100644 pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm create mode 100644 pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm create mode 100644 pidl/lib/Parse/Pidl/Samba4/NDR/Server.pm create mode 100644 pidl/lib/Parse/Pidl/Samba4/NDR/ServerCompat.pm create mode 100644 pidl/lib/Parse/Pidl/Samba4/Python.pm create mode 100644 pidl/lib/Parse/Pidl/Samba4/TDR.pm create mode 100644 pidl/lib/Parse/Pidl/Samba4/Template.pm (limited to 'pidl/lib/Parse/Pidl/Samba4') diff --git a/pidl/lib/Parse/Pidl/Samba4/COM/Header.pm b/pidl/lib/Parse/Pidl/Samba4/COM/Header.pm new file mode 100644 index 0000000..159f417 --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/COM/Header.pm @@ -0,0 +1,161 @@ +# COM Header generation +# (C) 2005 Jelmer Vernooij + +package Parse::Pidl::Samba4::COM::Header; + +use Parse::Pidl::Typelist qw(mapTypeName); +use Parse::Pidl::Util qw(has_property is_constant); + +use vars qw($VERSION); +$VERSION = '0.01'; + +use strict; +use warnings; + +sub GetArgumentProtoList($) +{ + my $f = shift; + my $res = ""; + + foreach my $a (@{$f->{ELEMENTS}}) { + + $res .= ", " . mapTypeName($a->{TYPE}) . " "; + + my $l = $a->{POINTERS}; + $l-- if (Parse::Pidl::Typelist::scalar_is_reference($a->{TYPE})); + foreach my $i (1..$l) { + $res .= "*"; + } + + if (defined $a->{ARRAY_LEN}[0] && !is_constant($a->{ARRAY_LEN}[0]) && + !$a->{POINTERS}) { + $res .= "*"; + } + $res .= $a->{NAME}; + if (defined $a->{ARRAY_LEN}[0] && is_constant($a->{ARRAY_LEN}[0])) { + $res .= "[$a->{ARRAY_LEN}[0]]"; + } + } + + return $res; +} + +sub GetArgumentList($) +{ + my $f = shift; + my $res = ""; + + foreach (@{$f->{ELEMENTS}}) { $res .= ", $_->{NAME}"; } + + return $res; +} + +##################################################################### +# generate vtable structure for COM interface +sub HeaderVTable($) +{ + my $interface = shift; + my $res; + $res .= "#define " . uc($interface->{NAME}) . "_METHODS \\\n"; + if (defined($interface->{BASE})) { + $res .= "\t" . uc($interface->{BASE} . "_METHODS") . "\\\n"; + } + + my $data = $interface->{DATA}; + foreach my $d (@{$data}) { + $res .= "\t" . mapTypeName($d->{RETURN_TYPE}) . " (*$d->{NAME}) (struct $interface->{NAME} *d, TALLOC_CTX *mem_ctx" . GetArgumentProtoList($d) . ");\\\n" if ($d->{TYPE} eq "FUNCTION"); + } + $res .= "\n"; + $res .= "struct $interface->{NAME}_vtable {\n"; + $res .= "\tstruct GUID iid;\n"; + $res .= "\t" . uc($interface->{NAME}) . "_METHODS\n"; + $res .= "};\n\n"; + + return $res; +} + +sub ParseInterface($) +{ + my $if = shift; + my $res; + + $res .= "\n#ifndef _$if->{NAME}_\n"; + $res .= "#define _$if->{NAME}_\n"; + + $res .="\n\n/* $if->{NAME} */\n"; + + $res .="#define COM_" . uc($if->{NAME}) . "_UUID $if->{PROPERTIES}->{uuid}\n\n"; + + $res .="struct $if->{NAME}_vtable;\n\n"; + + $res .="struct $if->{NAME} { + struct OBJREF obj; + struct com_context *ctx; + struct $if->{NAME}_vtable *vtable; + void *object_data; +};\n\n"; + + $res.=HeaderVTable($if); + + foreach my $d (@{$if->{DATA}}) { + next if ($d->{TYPE} ne "FUNCTION"); + + $res .= "#define $if->{NAME}_$d->{NAME}(interface, mem_ctx" . GetArgumentList($d) . ") "; + + $res .= "((interface)->vtable->$d->{NAME}(interface, mem_ctx" . GetArgumentList($d) . "))"; + + $res .="\n"; + } + + $res .= "#endif\n"; + + return $res; +} + +sub ParseCoClass($) +{ + my ($c) = @_; + my $res = ""; + $res .= "#define CLSID_" . uc($c->{NAME}) . " $c->{PROPERTIES}->{uuid}\n"; + if (has_property($c, "progid")) { + $res .= "#define PROGID_" . uc($c->{NAME}) . " $c->{PROPERTIES}->{progid}\n"; + } + $res .= "\n"; + return $res; +} + +sub Parse($$) +{ + my ($idl,$ndr_header) = @_; + my $res = ""; + my $has_obj = 0; + + $res .= "#include \"librpc/gen_ndr/orpc.h\"\n" . + "#include \"$ndr_header\"\n\n"; + + foreach (@{$idl}) + { + if ($_->{TYPE} eq "INTERFACE" && has_property($_, "object")) { + $res .="struct $_->{NAME};\n"; + $has_obj = 1; + } + } + + foreach (@{$idl}) + { + if ($_->{TYPE} eq "INTERFACE" && has_property($_, "object")) { + $res.=ParseInterface($_); + $has_obj = 1; + } + + if ($_->{TYPE} eq "COCLASS") { + $res.=ParseCoClass($_); + $has_obj = 1; + } + } + + return $res if ($has_obj); + return undef; +} + +1; diff --git a/pidl/lib/Parse/Pidl/Samba4/COM/Proxy.pm b/pidl/lib/Parse/Pidl/Samba4/COM/Proxy.pm new file mode 100644 index 0000000..1630cf2 --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/COM/Proxy.pm @@ -0,0 +1,226 @@ +################################################### +# DCOM parser for Samba +# Basically the glue between COM and DCE/RPC with NDR +# Copyright jelmer@samba.org 2003-2005 +# released under the GNU GPL + +package Parse::Pidl::Samba4::COM::Proxy; + +use Parse::Pidl::Samba4::COM::Header; +use Parse::Pidl::Typelist qw(mapTypeName); +use Parse::Pidl::Util qw(has_property); + +use vars qw($VERSION); +$VERSION = '0.01'; + +use strict; +use warnings; + +my($res); + +sub ParseVTable($$) +{ + my ($interface, $name) = @_; + + # Generate the vtable + $res .="\tstruct $interface->{NAME}_vtable $name = {"; + + if (defined($interface->{BASE})) { + $res .= "\n\t\t{},"; + } + + my $data = $interface->{DATA}; + + foreach my $d (@{$data}) { + if ($d->{TYPE} eq "FUNCTION") { + $res .= "\n\t\tdcom_proxy_$interface->{NAME}_$d->{NAME}"; + $res .= ","; + } + } + + $res .= "\n\t};\n\n"; +} + +sub ParseRegFunc($) +{ + my $interface = shift; + + $res .= "static NTSTATUS dcom_proxy_$interface->{NAME}_init(TALLOC_CTX *ctx) +{ + struct $interface->{NAME}_vtable *proxy_vtable = talloc(ctx, struct $interface->{NAME}_vtable); +"; + + if (defined($interface->{BASE})) { + $res.= " + struct GUID base_iid; + const void *base_vtable; + + base_iid = ndr_table_$interface->{BASE}.syntax_id.uuid; + + base_vtable = dcom_proxy_vtable_by_iid(&base_iid); + if (base_vtable == NULL) { + DEBUG(0, (\"No proxy registered for base interface '$interface->{BASE}'\\n\")); + return NT_STATUS_FOOBAR; + } + + memcpy(&proxy_vtable, base_vtable, sizeof(struct $interface->{BASE}_vtable)); + +"; + } + foreach my $x (@{$interface->{DATA}}) { + next unless ($x->{TYPE} eq "FUNCTION"); + + $res .= "\tproxy_vtable->$x->{NAME} = dcom_proxy_$interface->{NAME}_$x->{NAME};\n"; + } + + $res.= " + proxy_vtable->iid = ndr_table_$interface->{NAME}.syntax_id.uuid; + + return dcom_register_proxy(ctx, (struct IUnknown_vtable *)proxy_vtable); +}\n\n"; +} + +##################################################################### +# parse a function +sub ParseFunction($$) +{ + my ($interface, $fn) = @_; + my $name = $fn->{NAME}; + my $uname = uc $name; + + my $tn = mapTypeName($fn->{RETURN_TYPE}); + + $res.=" +static $tn dcom_proxy_$interface->{NAME}_$name(struct $interface->{NAME} *d, TALLOC_CTX *mem_ctx" . Parse::Pidl::Samba4::COM::Header::GetArgumentProtoList($fn) . ") +{ + struct dcerpc_pipe *p; + NTSTATUS status = dcom_get_pipe(d, &p); + struct $name r; + struct rpc_request *req; + + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + NDR_ZERO_STRUCT(r.in.ORPCthis); + r.in.ORPCthis.version.MajorVersion = COM_MAJOR_VERSION; + r.in.ORPCthis.version.MinorVersion = COM_MINOR_VERSION; +"; + + # Put arguments into r + foreach my $a (@{$fn->{ELEMENTS}}) { + next unless (has_property($a, "in")); + if (Parse::Pidl::Typelist::typeIs($a->{TYPE}, "INTERFACE")) { + $res .="\tNDR_CHECK(dcom_OBJREF_from_IUnknown(mem_ctx, &r.in.$a->{NAME}.obj, $a->{NAME}));\n"; + } else { + $res .= "\tr.in.$a->{NAME} = $a->{NAME};\n"; + } + } + + $res .=" + if (p->conn->flags & DCERPC_DEBUG_PRINT_IN) { + NDR_PRINT_IN_DEBUG($name, &r); + } + + status = dcerpc_ndr_request(p, &d->ipid, &ndr_table_$interface->{NAME}, NDR_$uname, mem_ctx, &r); + + if (NT_STATUS_IS_OK(status) && (p->conn->flags & DCERPC_DEBUG_PRINT_OUT)) { + NDR_PRINT_OUT_DEBUG($name, r); + } + +"; + + # Put r info back into arguments + foreach my $a (@{$fn->{ELEMENTS}}) { + next unless (has_property($a, "out")); + + if (Parse::Pidl::Typelist::typeIs($a->{TYPE}, "INTERFACE")) { + $res .="\tNDR_CHECK(dcom_IUnknown_from_OBJREF(d->ctx, &$a->{NAME}, r.out.$a->{NAME}.obj));\n"; + } else { + $res .= "\t*$a->{NAME} = r.out.$a->{NAME};\n"; + } + + } + + if ($fn->{RETURN_TYPE} eq "NTSTATUS") { + $res .= "\tif (NT_STATUS_IS_OK(status)) status = r.out.result;\n"; + } + + $res .= + " + return r.out.result; +}\n\n"; +} + +##################################################################### +# parse the interface definitions +sub ParseInterface($) +{ + my($interface) = shift; + my($data) = $interface->{DATA}; + $res = "/* DCOM proxy for $interface->{NAME} generated by pidl */\n\n"; + foreach my $d (@{$data}) { + ($d->{TYPE} eq "FUNCTION") && + ParseFunction($interface, $d); + } + + ParseRegFunc($interface); +} + +sub RegistrationFunction($$) +{ + my $idl = shift; + my $basename = shift; + + my $res = "\n\nNTSTATUS dcom_$basename\_init(void)\n"; + $res .= "{\n"; + $res .="\tNTSTATUS status = NT_STATUS_OK;\n"; + foreach my $interface (@{$idl}) { + next if $interface->{TYPE} ne "INTERFACE"; + next if not has_property($interface, "object"); + + my $data = $interface->{DATA}; + my $count = 0; + foreach my $d (@{$data}) { + if ($d->{TYPE} eq "FUNCTION") { $count++; } + } + + next if ($count == 0); + + $res .= "\tstatus = dcom_$interface->{NAME}_init();\n"; + $res .= "\tif (NT_STATUS_IS_ERR(status)) {\n"; + $res .= "\t\treturn status;\n"; + $res .= "\t}\n\n"; + } + $res .= "\treturn status;\n"; + $res .= "}\n\n"; + + return $res; +} + +sub Parse($$) +{ + my ($pidl,$comh_filename) = @_; + my $res = ""; + my $has_obj = 0; + + $res .= "#include \"includes.h\"\n" . + "#include \"lib/com/dcom/dcom.h\"\n" . + "#include \"$comh_filename\"\n" . + "#include \"librpc/rpc/dcerpc.h\"\n"; + + foreach (@{$pidl}) { + next if ($_->{TYPE} ne "INTERFACE"); + next if has_property($_, "local"); + next unless has_property($_, "object"); + + $res .= ParseInterface($_); + + $has_obj = 1; + } + + return $res if ($has_obj); + return undef; +} + +1; diff --git a/pidl/lib/Parse/Pidl/Samba4/COM/Stub.pm b/pidl/lib/Parse/Pidl/Samba4/COM/Stub.pm new file mode 100644 index 0000000..7198038 --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/COM/Stub.pm @@ -0,0 +1,322 @@ +################################################### +# DCOM stub boilerplate generator +# Copyright jelmer@samba.org 2004-2005 +# Copyright tridge@samba.org 2003 +# Copyright metze@samba.org 2004 +# released under the GNU GPL + +package Parse::Pidl::Samba4::COM::Stub; + +use Parse::Pidl::Util qw(has_property); +use strict; +use warnings; + +use vars qw($VERSION); +$VERSION = '0.01'; + +my($res); + +sub pidl($) +{ + $res .= shift; +} + +##################################################### +# generate the switch statement for function dispatch +sub gen_dispatch_switch($) +{ + my $data = shift; + + my $count = 0; + foreach my $d (@{$data}) { + next if ($d->{TYPE} ne "FUNCTION"); + + pidl "\tcase $count: {\n"; + if ($d->{RETURN_TYPE} && $d->{RETURN_TYPE} ne "void") { + pidl "\t\tNTSTATUS result;\n"; + } + pidl "\t\tstruct $d->{NAME} *r2 = r;\n"; + pidl "\t\tif (DEBUGLEVEL > 10) {\n"; + pidl "\t\t\tNDR_PRINT_FUNCTION_DEBUG($d->{NAME}, NDR_IN, r2);\n"; + pidl "\t\t}\n"; + if ($d->{RETURN_TYPE} && $d->{RETURN_TYPE} ne "void") { + pidl "\t\tresult = vtable->$d->{NAME}(iface, mem_ctx, r2);\n"; + } else { + pidl "\t\tvtable->$d->{NAME}(iface, mem_ctx, r2);\n"; + } + pidl "\t\tif (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {\n"; + pidl "\t\t\tDEBUG(5,(\"function $d->{NAME} will reply async\\n\"));\n"; + pidl "\t\t}\n"; + pidl "\t\tbreak;\n\t}\n"; + $count++; + } +} + +##################################################### +# generate the switch statement for function reply +sub gen_reply_switch($) +{ + my $data = shift; + + my $count = 0; + foreach my $d (@{$data}) { + next if ($d->{TYPE} ne "FUNCTION"); + + pidl "\tcase $count: {\n"; + pidl "\t\tstruct $d->{NAME} *r2 = r;\n"; + pidl "\t\tif (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {\n"; + pidl "\t\t\tDEBUG(5,(\"function $d->{NAME} replied async\\n\"));\n"; + pidl "\t\t}\n"; + pidl "\t\tif (DEBUGLEVEL > 10 && dce_call->fault_code == 0) {\n"; + pidl "\t\t\tNDR_PRINT_FUNCTION_DEBUG($d->{NAME}, NDR_OUT | NDR_SET_VALUES, r2);\n"; + pidl "\t\t}\n"; + pidl "\t\tif (dce_call->fault_code != 0) {\n"; + pidl "\t\t\tDEBUG(2,(\"dcerpc_fault %s in $d->{NAME}\\n\", dcerpc_errstr(mem_ctx, dce_call->fault_code)));\n"; + pidl "\t\t}\n"; + pidl "\t\tbreak;\n\t}\n"; + $count++; + } +} + +##################################################################### +# produce boilerplate code for a interface +sub Boilerplate_Iface($) +{ + my($interface) = shift; + my($data) = $interface->{DATA}; + my $name = $interface->{NAME}; + my $uname = uc $name; + my $uuid = Parse::Pidl::Util::make_str($interface->{PROPERTIES}->{uuid}); + my $if_version = $interface->{PROPERTIES}->{version}; + + pidl " +static NTSTATUS $name\__op_bind(struct dcesrv_call_state *dce_call, const struct dcesrv_interface *iface, uint32_t if_version) +{ +#ifdef DCESRV_INTERFACE_$uname\_BIND + return DCESRV_INTERFACE_$uname\_BIND(dce_call,iface); +#else + return NT_STATUS_OK; +#endif +} + +static void $name\__op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface) +{ +#ifdef DCESRV_INTERFACE_$uname\_UNBIND + DCESRV_INTERFACE_$uname\_UNBIND(context, iface); +#else + return; +#endif +} + +static NTSTATUS $name\__op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_pull *pull, void **r) +{ + NTSTATUS status; + uint16_t opnum = dce_call->pkt.u.request.opnum; + + dce_call->fault_code = 0; + + if (opnum >= dcerpc_table_$name.num_calls) { + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NET_WRITE_FAULT; + } + + *r = talloc_size(mem_ctx, dcerpc_table_$name.calls[opnum].struct_size); + NT_STATUS_HAVE_NO_MEMORY(*r); + + /* unravel the NDR for the packet */ + status = dcerpc_table_$name.calls[opnum].ndr_pull(pull, NDR_IN, *r); + if (!NT_STATUS_IS_OK(status)) { + dce_call->fault_code = DCERPC_FAULT_NDR; + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static NTSTATUS $name\__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16_t opnum = dce_call->pkt.u.request.opnum; + struct GUID ipid = dce_call->pkt.u.request.object.object; + struct dcom_interface_p *iface = dcom_get_local_iface_p(&ipid); + const struct dcom_$name\_vtable *vtable = iface->vtable; + + switch (opnum) { +"; + gen_dispatch_switch($data); + +pidl " + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static NTSTATUS $name\__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16_t opnum = dce_call->pkt.u.request.opnum; + + switch (opnum) { +"; + gen_reply_switch($data); + +pidl " + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static NTSTATUS $name\__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, const void *r) +{ + NTSTATUS status; + uint16_t opnum = dce_call->pkt.u.request.opnum; + + status = dcerpc_table_$name.calls[opnum].ndr_push(push, NDR_OUT, r); + if (!NT_STATUS_IS_OK(status)) { + dce_call->fault_code = DCERPC_FAULT_NDR; + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static const struct dcesrv_interface $name\_interface = { + .name = \"$name\", + .uuid = $uuid, + .if_version = $if_version, + .bind = $name\__op_bind, + .unbind = $name\__op_unbind, + .ndr_pull = $name\__op_ndr_pull, + .dispatch = $name\__op_dispatch, + .reply = $name\__op_reply, + .ndr_push = $name\__op_ndr_push +}; + +"; +} + +##################################################################### +# produce boilerplate code for an endpoint server +sub Boilerplate_Ep_Server($) +{ + my($interface) = shift; + my $name = $interface->{NAME}; + my $uname = uc $name; + + pidl " +static NTSTATUS $name\__op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server) +{ + int i; + + for (i=0;icount;i++) { + NTSTATUS ret; + const char *name = dcerpc_table_$name.endpoints->names[i]; + + ret = dcesrv_interface_register(dce_ctx, name, &$name\_interface, NULL); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1,(\"$name\_op_init_server: failed to register endpoint \'%s\'\\n\",name)); + return ret; + } + } + + return NT_STATUS_OK; +} + +static BOOL $name\__op_interface_by_uuid(struct dcesrv_interface *iface, const char *uuid, uint32_t if_version) +{ + if (dcerpc_table_$name.if_version == if_version && + strcmp(dcerpc_table_$name.uuid, uuid)==0) { + memcpy(iface,&dcerpc_table_$name, sizeof(*iface)); + return True; + } + + return False; +} + +static BOOL $name\__op_interface_by_name(struct dcesrv_interface *iface, const char *name) +{ + if (strcmp(dcerpc_table_$name.name, name)==0) { + memcpy(iface,&dcerpc_table_$name, sizeof(*iface)); + return True; + } + + return False; +} + +NTSTATUS dcerpc_server_$name\_init(void) +{ + NTSTATUS ret; + struct dcesrv_endpoint_server ep_server; + + /* fill in our name */ + ep_server.name = \"$name\"; + + /* fill in all the operations */ + ep_server.init_server = $name\__op_init_server; + + ep_server.interface_by_uuid = $name\__op_interface_by_uuid; + ep_server.interface_by_name = $name\__op_interface_by_name; + + /* register ourselves with the DCERPC subsystem. */ + ret = dcerpc_register_ep_server(&ep_server); + + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,(\"Failed to register \'$name\' endpoint server!\\n\")); + return ret; + } + + return ret; +} + +"; +} + +##################################################################### +# dcom interface stub from a parsed IDL structure +sub ParseInterface($) +{ + my($interface) = shift; + + return "" if has_property($interface, "local"); + + my($data) = $interface->{DATA}; + my $count = 0; + + $res = ""; + + if (!defined $interface->{PROPERTIES}->{uuid}) { + return $res; + } + + if (!defined $interface->{PROPERTIES}->{version}) { + $interface->{PROPERTIES}->{version} = "0.0"; + } + + foreach my $d (@{$data}) { + if ($d->{TYPE} eq "FUNCTION") { $count++; } + } + + if ($count == 0) { + return $res; + } + + $res = "/* dcom interface stub generated by pidl */\n\n"; + Boilerplate_Iface($interface); + Boilerplate_Ep_Server($interface); + + return $res; +} + +1; diff --git a/pidl/lib/Parse/Pidl/Samba4/Header.pm b/pidl/lib/Parse/Pidl/Samba4/Header.pm new file mode 100644 index 0000000..a0b002f --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/Header.pm @@ -0,0 +1,538 @@ +################################################### +# create C header files for an IDL structure +# Copyright tridge@samba.org 2000 +# Copyright jelmer@samba.org 2005 +# released under the GNU GPL + +package Parse::Pidl::Samba4::Header; +require Exporter; + +@ISA = qw(Exporter); +@EXPORT_OK = qw(GenerateFunctionInEnv GenerateFunctionOutEnv EnvSubstituteValue GenerateStructEnv); + +use strict; +use warnings; +use Parse::Pidl qw(fatal); +use Parse::Pidl::Typelist qw(mapTypeName scalar_is_reference); +use Parse::Pidl::Util qw(has_property is_constant unmake_str ParseExpr); +use Parse::Pidl::Samba4 qw(is_intree ElementStars ArrayBrackets choose_header); + +use vars qw($VERSION); +$VERSION = '0.01'; + +my($res); +my($tab_depth); + +sub pidl($) { $res .= shift; } + +sub tabs() +{ + my $res = ""; + $res .="\t" foreach (1..$tab_depth); + return $res; +} + +##################################################################### +# parse a properties list +sub HeaderProperties($$) +{ + my($props,$ignores) = @_; + my $ret = ""; + + foreach my $d (sort(keys %{$props})) { + next if (grep(/^$d$/, @$ignores)); + if($props->{$d} ne "1") { + $ret.= "$d($props->{$d}),"; + } else { + $ret.="$d,"; + } + } + + if ($ret) { + pidl "/* [" . substr($ret, 0, -1) . "] */"; + } +} + +##################################################################### +# parse a structure element +sub HeaderElement($) +{ + my($element) = shift; + + pidl tabs(); + if (has_property($element, "represent_as")) { + pidl mapTypeName($element->{PROPERTIES}->{represent_as})." "; + } else { + if (ref($element->{TYPE}) eq "HASH") { + HeaderType($element, $element->{TYPE}, $element->{TYPE}->{NAME}); + } else { + HeaderType($element, $element->{TYPE}, ""); + } + pidl " ".ElementStars($element); + } + pidl $element->{NAME}; + pidl ArrayBrackets($element); + + pidl ";"; + if (defined $element->{PROPERTIES}) { + HeaderProperties($element->{PROPERTIES}, ["in", "out"]); + } + pidl "\n"; +} + +##################################################################### +# parse a struct +sub HeaderStruct($$;$) +{ + my($struct,$name,$tail) = @_; + pidl "struct $name"; + pidl $tail if defined($tail) and not defined($struct->{ELEMENTS}); + return if (not defined($struct->{ELEMENTS})); + pidl " {\n"; + $tab_depth++; + my $el_count=0; + foreach (@{$struct->{ELEMENTS}}) { + HeaderElement($_); + $el_count++; + } + if ($el_count == 0) { + # some compilers can't handle empty structures + pidl tabs()."char _empty_;\n"; + } + $tab_depth--; + pidl tabs()."}"; + if (defined $struct->{PROPERTIES}) { + HeaderProperties($struct->{PROPERTIES}, []); + } + pidl $tail if defined($tail); +} + +##################################################################### +# parse a enum +sub HeaderEnum($$;$) +{ + my($enum,$name,$tail) = @_; + my $first = 1; + + pidl "enum $name"; + if (defined($enum->{ELEMENTS})) { + pidl "\n#ifndef USE_UINT_ENUMS\n"; + pidl " {\n"; + $tab_depth++; + foreach my $e (@{$enum->{ELEMENTS}}) { + my @enum_els = (); + unless ($first) { pidl ",\n"; } + $first = 0; + pidl tabs(); + @enum_els = split(/=/, $e); + if (@enum_els == 2) { + pidl $enum_els[0]; + pidl "=(int)"; + pidl "("; + pidl $enum_els[1]; + pidl ")"; + } else { + pidl $e; + } + } + pidl "\n"; + $tab_depth--; + pidl "}"; + pidl "\n"; + pidl "#else\n"; + my $count = 0; + my $with_val = 0; + my $without_val = 0; + pidl " { __do_not_use_enum_$name=INT_MAX}\n"; + foreach my $e (@{$enum->{ELEMENTS}}) { + my $t = "$e"; + my $name; + my $value; + if ($t =~ /(.*)=(.*)/) { + $name = $1; + $value = $2; + $with_val = 1; + fatal($e->{ORIGINAL}, "you can't mix enum member with values and without values!") + unless ($without_val == 0); + } else { + $name = $t; + $value = $count++; + $without_val = 1; + fatal($e->{ORIGINAL}, "you can't mix enum member with values and without values!") + unless ($with_val == 0); + } + pidl "#define $name ( $value )\n"; + } + pidl "#endif\n"; + } + pidl $tail if defined($tail); +} + +##################################################################### +# parse a bitmap +sub HeaderBitmap($$) +{ + my($bitmap,$name) = @_; + + return unless defined($bitmap->{ELEMENTS}); + + pidl "/* bitmap $name */\n"; + pidl "#define $_\n" foreach (@{$bitmap->{ELEMENTS}}); + pidl "\n"; +} + +##################################################################### +# parse a union +sub HeaderUnion($$;$) +{ + my($union,$name,$tail) = @_; + my %done = (); + + pidl "union $name"; + pidl $tail if defined($tail) and not defined($union->{ELEMENTS}); + return if (not defined($union->{ELEMENTS})); + pidl " {\n"; + $tab_depth++; + my $needed = 0; + foreach my $e (@{$union->{ELEMENTS}}) { + if ($e->{TYPE} ne "EMPTY") { + if (! defined $done{$e->{NAME}}) { + HeaderElement($e); + } + $done{$e->{NAME}} = 1; + $needed++; + } + } + if (!$needed) { + # sigh - some compilers don't like empty structures + pidl tabs()."int _dummy_element;\n"; + } + $tab_depth--; + pidl "}"; + + if (defined $union->{PROPERTIES}) { + HeaderProperties($union->{PROPERTIES}, []); + } + pidl $tail if defined($tail); +} + +##################################################################### +# parse a pipe +sub HeaderPipe($$;$) +{ + my($pipe,$name,$tail) = @_; + + my $struct = $pipe->{DATA}; + my $e = $struct->{ELEMENTS}[1]; + + pidl "struct $name;\n"; + pidl "struct $struct->{NAME} {\n"; + $tab_depth++; + pidl tabs()."uint32_t count;\n"; + pidl tabs().mapTypeName($e->{TYPE})." *array;\n"; + $tab_depth--; + pidl "}"; + + if (defined $struct->{PROPERTIES}) { + HeaderProperties($struct->{PROPERTIES}, []); + } + + pidl $tail if defined($tail); +} + +##################################################################### +# parse a type +sub HeaderType($$$;$) +{ + my($e,$data,$name,$tail) = @_; + if (ref($data) eq "HASH") { + ($data->{TYPE} eq "ENUM") && HeaderEnum($data, $name, $tail); + ($data->{TYPE} eq "BITMAP") && HeaderBitmap($data, $name); + ($data->{TYPE} eq "STRUCT") && HeaderStruct($data, $name, $tail); + ($data->{TYPE} eq "UNION") && HeaderUnion($data, $name, $tail); + ($data->{TYPE} eq "PIPE") && HeaderPipe($data, $name, $tail); + return; + } + + if (has_property($e, "charset")) { + pidl "const char"; + } else { + pidl mapTypeName($e->{TYPE}); + } + pidl $tail if defined($tail); +} + +##################################################################### +# parse a typedef +sub HeaderTypedef($;$) +{ + my($typedef,$tail) = @_; + # Don't print empty "enum foo;", since some compilers don't like it. + return if ($typedef->{DATA}->{TYPE} eq "ENUM" and not defined($typedef->{DATA}->{ELEMENTS})); + HeaderType($typedef, $typedef->{DATA}, $typedef->{NAME}, $tail) if defined ($typedef->{DATA}); +} + +##################################################################### +# parse a const +sub HeaderConst($) +{ + my($const) = shift; + if (!defined($const->{ARRAY_LEN}[0])) { + pidl "#define $const->{NAME}\t( $const->{VALUE} )\n"; + } else { + pidl "#define $const->{NAME}\t $const->{VALUE}\n"; + } +} + +sub ElementDirection($) +{ + my ($e) = @_; + + return "inout" if (has_property($e, "in") and has_property($e, "out")); + return "in" if (has_property($e, "in")); + return "out" if (has_property($e, "out")); + return "inout"; +} + +##################################################################### +# parse a function +sub HeaderFunctionInOut($$) +{ + my($fn,$prop) = @_; + + return unless defined($fn->{ELEMENTS}); + + foreach my $e (@{$fn->{ELEMENTS}}) { + HeaderElement($e) if (ElementDirection($e) eq $prop); + } +} + +##################################################################### +# determine if we need an "in" or "out" section +sub HeaderFunctionInOut_needed($$) +{ + my($fn,$prop) = @_; + + return 1 if ($prop eq "out" && defined($fn->{RETURN_TYPE})); + + return undef unless defined($fn->{ELEMENTS}); + + foreach my $e (@{$fn->{ELEMENTS}}) { + return 1 if (ElementDirection($e) eq $prop); + } + + return undef; +} + +my %headerstructs; + +##################################################################### +# parse a function +sub HeaderFunction($) +{ + my($fn) = shift; + + return if ($headerstructs{$fn->{NAME}}); + + $headerstructs{$fn->{NAME}} = 1; + + pidl "\nstruct $fn->{NAME} {\n"; + $tab_depth++; + my $needed = 0; + + if (HeaderFunctionInOut_needed($fn, "in") or + HeaderFunctionInOut_needed($fn, "inout")) { + pidl tabs()."struct {\n"; + $tab_depth++; + HeaderFunctionInOut($fn, "in"); + HeaderFunctionInOut($fn, "inout"); + $tab_depth--; + pidl tabs()."} in;\n\n"; + $needed++; + } + + if (HeaderFunctionInOut_needed($fn, "out") or + HeaderFunctionInOut_needed($fn, "inout")) { + pidl tabs()."struct {\n"; + $tab_depth++; + HeaderFunctionInOut($fn, "out"); + HeaderFunctionInOut($fn, "inout"); + if (defined($fn->{RETURN_TYPE})) { + pidl tabs().mapTypeName($fn->{RETURN_TYPE}) . " result;\n"; + } + $tab_depth--; + pidl tabs()."} out;\n\n"; + $needed++; + } + + if (!$needed) { + # sigh - some compilers don't like empty structures + pidl tabs()."int _dummy_element;\n"; + } + + $tab_depth--; + pidl "};\n\n"; +} + +sub HeaderImport +{ + my @imports = @_; + foreach my $import (@imports) { + $import = unmake_str($import); + $import =~ s/\.idl$//; + pidl choose_header("librpc/gen_ndr/$import\.h", "gen_ndr/$import.h") . "\n"; + } +} + +sub HeaderInclude +{ + my @includes = @_; + foreach (@includes) { + pidl "#include $_\n"; + } +} + +##################################################################### +# parse the interface definitions +sub HeaderInterface($) +{ + my($interface) = shift; + + pidl "#ifndef _HEADER_$interface->{NAME}\n"; + pidl "#define _HEADER_$interface->{NAME}\n\n"; + + foreach my $c (@{$interface->{CONSTS}}) { + HeaderConst($c); + } + + foreach my $t (@{$interface->{TYPES}}) { + HeaderTypedef($t, ";\n\n") if ($t->{TYPE} eq "TYPEDEF"); + HeaderStruct($t, $t->{NAME}, ";\n\n") if ($t->{TYPE} eq "STRUCT"); + HeaderUnion($t, $t->{NAME}, ";\n\n") if ($t->{TYPE} eq "UNION"); + HeaderEnum($t, $t->{NAME}, ";\n\n") if ($t->{TYPE} eq "ENUM"); + HeaderBitmap($t, $t->{NAME}) if ($t->{TYPE} eq "BITMAP"); + HeaderPipe($t, $t->{NAME}, "\n\n") if ($t->{TYPE} eq "PIPE"); + } + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + HeaderFunction($fn); + } + + pidl "#endif /* _HEADER_$interface->{NAME} */\n"; +} + +sub HeaderQuote($) +{ + my($quote) = shift; + + pidl unmake_str($quote->{DATA}) . "\n"; +} + +##################################################################### +# parse a parsed IDL into a C header +sub Parse($) +{ + my($ndr) = shift; + $tab_depth = 0; + + $res = ""; + %headerstructs = (); + pidl "/* header auto-generated by pidl */\n\n"; + + my $ifacename = ""; + + # work out a unique interface name + foreach (@{$ndr}) { + if ($_->{TYPE} eq "INTERFACE") { + $ifacename = $_->{NAME}; + last; + } + } + + pidl "#ifndef _PIDL_HEADER_$ifacename\n"; + pidl "#define _PIDL_HEADER_$ifacename\n\n"; + + if (!is_intree()) { + pidl "#include \n"; + } + pidl "#include \n"; + pidl "\n"; + # FIXME: Include this only if NTSTATUS was actually used + pidl choose_header("libcli/util/ntstatus.h", "core/ntstatus.h") . "\n"; + pidl "\n"; + + foreach (@{$ndr}) { + ($_->{TYPE} eq "CPP_QUOTE") && HeaderQuote($_); + ($_->{TYPE} eq "INTERFACE") && HeaderInterface($_); + ($_->{TYPE} eq "IMPORT") && HeaderImport(@{$_->{PATHS}}); + ($_->{TYPE} eq "INCLUDE") && HeaderInclude(@{$_->{PATHS}}); + } + + pidl "#endif /* _PIDL_HEADER_$ifacename */\n"; + + return $res; +} + +sub GenerateStructEnv($$) +{ + my ($x, $v) = @_; + my %env; + + foreach my $e (@{$x->{ELEMENTS}}) { + $env{$e->{NAME}} = "$v->$e->{NAME}"; + } + + $env{"this"} = $v; + + return \%env; +} + +sub EnvSubstituteValue($$) +{ + my ($env,$s) = @_; + + # Substitute the value() values in the env + foreach my $e (@{$s->{ELEMENTS}}) { + next unless (defined(my $v = has_property($e, "value"))); + + $env->{$e->{NAME}} = ParseExpr($v, $env, $e); + } + + return $env; +} + +sub GenerateFunctionInEnv($;$) +{ + my ($fn, $base) = @_; + my %env; + + $base = "r->" unless defined($base); + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep (/in/, @{$e->{DIRECTION}})) { + $env{$e->{NAME}} = $base."in.$e->{NAME}"; + } + } + + return \%env; +} + +sub GenerateFunctionOutEnv($;$) +{ + my ($fn, $base) = @_; + my %env; + + $base = "r->" unless defined($base); + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep (/out/, @{$e->{DIRECTION}})) { + $env{$e->{NAME}} = $base."out.$e->{NAME}"; + } elsif (grep (/in/, @{$e->{DIRECTION}})) { + $env{$e->{NAME}} = $base."in.$e->{NAME}"; + } + } + + return \%env; +} + +1; diff --git a/pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm b/pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm new file mode 100644 index 0000000..84e2beb --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/NDR/Client.pm @@ -0,0 +1,877 @@ +################################################### +# client calls generator +# Copyright tridge@samba.org 2003 +# Copyright jelmer@samba.org 2005-2006 +# released under the GNU GPL + +package Parse::Pidl::Samba4::NDR::Client; +use parent Parse::Pidl::Base; + +use Parse::Pidl qw(fatal warning error); +use Parse::Pidl::Util qw(has_property ParseExpr genpad); +use Parse::Pidl::NDR qw(ContainsPipe); +use Parse::Pidl::Typelist qw(mapTypeName); +use Parse::Pidl::Samba4 qw(choose_header is_intree DeclLong); +use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv); + + +use vars qw($VERSION); +$VERSION = '0.01'; + +use strict; +use warnings; + +sub fn_declare($$) { my ($self,$n) = @_; $self->pidl($n); $self->pidl_hdr("$n;"); } +sub new($) +{ + my ($class) = shift; + my $self = { res => "", res_hdr => "", tabs => "" }; + bless($self, $class); +} + +sub ParseFunctionHasPipes($$) +{ + my ($self, $fn) = @_; + + foreach my $e (@{$fn->{ELEMENTS}}) { + return 1 if ContainsPipe($e, $e->{LEVELS}[0]); + } + + return 0; +} + +sub ParseFunction_r_State($$$$) +{ + my ($self, $if, $fn, $name) = @_; + my $uname = uc $name; + + $self->pidl("struct dcerpc_$name\_r_state {"); + $self->indent; + $self->pidl("TALLOC_CTX *out_mem_ctx;"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + $self->pidl("static void dcerpc_$name\_r_done(struct tevent_req *subreq);"); + $self->pidl(""); +} + +sub ParseFunction_r_Send($$$$) +{ + my ($self, $if, $fn, $name) = @_; + my $uname = uc $name; + + my $proto = "struct tevent_req *dcerpc_$name\_r_send(TALLOC_CTX *mem_ctx,\n"; + $proto .= "\tstruct tevent_context *ev,\n", + $proto .= "\tstruct dcerpc_binding_handle *h,\n", + $proto .= "\tstruct $name *r)"; + + $self->fn_declare($proto); + + $self->pidl("{"); + $self->indent; + + $self->pidl("struct tevent_req *req;"); + $self->pidl("struct dcerpc_$name\_r_state *state;"); + $self->pidl("struct tevent_req *subreq;"); + $self->pidl(""); + + $self->pidl("req = tevent_req_create(mem_ctx, &state,"); + $self->pidl("\t\t\tstruct dcerpc_$name\_r_state);"); + $self->pidl("if (req == NULL) {"); + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + my $out_params = 0; + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless grep(/out/, @{$e->{DIRECTION}}); + next if ContainsPipe($e, $e->{LEVELS}[0]); + $out_params++; + + } + + my $submem; + if ($out_params > 0) { + $self->pidl("state->out_mem_ctx = talloc_new(state);"); + $self->pidl("if (tevent_req_nomem(state->out_mem_ctx, req)) {"); + $self->indent; + $self->pidl("return tevent_req_post(req, ev);"); + $self->deindent; + $self->pidl("}"); + $submem = "state->out_mem_ctx"; + } else { + $self->pidl("state->out_mem_ctx = NULL;"); + $submem = "state"; + } + $self->pidl(""); + + $self->pidl("subreq = dcerpc_binding_handle_call_send(state, ev, h,"); + $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},"); + $self->pidl("\t\tNDR_$uname, $submem, r);"); + $self->pidl("if (tevent_req_nomem(subreq, req)) {"); + $self->indent; + $self->pidl("return tevent_req_post(req, ev);"); + $self->deindent; + $self->pidl("}"); + $self->pidl("tevent_req_set_callback(subreq, dcerpc_$name\_r_done, req);"); + $self->pidl(""); + + $self->pidl("return req;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseFunction_r_Done($$$$) +{ + my ($self, $if, $fn, $name) = @_; + my $uname = uc $name; + + my $proto = "static void dcerpc_$name\_r_done(struct tevent_req *subreq)"; + + $self->pidl("$proto"); + $self->pidl("{"); + $self->indent; + + $self->pidl("struct tevent_req *req ="); + $self->pidl("\ttevent_req_callback_data(subreq,"); + $self->pidl("\tstruct tevent_req);"); + $self->pidl("NTSTATUS status;"); + $self->pidl(""); + + $self->pidl("status = dcerpc_binding_handle_call_recv(subreq);"); + $self->pidl("TALLOC_FREE(subreq);"); + $self->pidl("if (tevent_req_nterror(req, status)) {"); + $self->indent; + $self->pidl("return;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("tevent_req_done(req);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseFunction_r_Recv($$$$) +{ + my ($self, $if, $fn, $name) = @_; + my $uname = uc $name; + + my $proto = "NTSTATUS dcerpc_$name\_r_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx)"; + + $self->fn_declare($proto); + + $self->pidl("{"); + $self->indent; + + $self->pidl("struct dcerpc_$name\_r_state *state ="); + $self->pidl("\ttevent_req_data(req,"); + $self->pidl("\tstruct dcerpc_$name\_r_state);"); + $self->pidl("NTSTATUS status;"); + $self->pidl(""); + + $self->pidl("if (tevent_req_is_nterror(req, &status)) {"); + $self->indent; + $self->pidl("tevent_req_received(req);"); + $self->pidl("return status;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("talloc_steal(mem_ctx, state->out_mem_ctx);"); + $self->pidl(""); + + $self->pidl("tevent_req_received(req);"); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseFunction_r_Sync($$$$) +{ + my ($self, $if, $fn, $name) = @_; + my $uname = uc $name; + + if ($self->ParseFunctionHasPipes($fn)) { + $self->pidl_both("/*"); + $self->pidl_both(" * The following function is skipped because"); + $self->pidl_both(" * it uses pipes:"); + $self->pidl_both(" *"); + $self->pidl_both(" * dcerpc_$name\_r()"); + $self->pidl_both(" */"); + $self->pidl_both(""); + return; + } + + my $proto = "NTSTATUS dcerpc_$name\_r(struct dcerpc_binding_handle *h, TALLOC_CTX *mem_ctx, struct $name *r)"; + + $self->fn_declare($proto); + + $self->pidl("{"); + $self->indent; + $self->pidl("NTSTATUS status;"); + $self->pidl(""); + + $self->pidl("status = dcerpc_binding_handle_call(h,"); + $self->pidl("\t\tNULL, &ndr_table_$if->{NAME},"); + $self->pidl("\t\tNDR_$uname, mem_ctx, r);"); + $self->pidl(""); + $self->pidl("return status;"); + + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ElementDirection($) +{ + my ($e) = @_; + + return "[in,out]" if (has_property($e, "in") and has_property($e, "out")); + return "[in]" if (has_property($e, "in")); + return "[out]" if (has_property($e, "out")); + return "[in,out]"; +} + +sub HeaderProperties($$) +{ + my($props,$ignores) = @_; + my $ret = ""; + + foreach my $d (sort(keys %{$props})) { + next if (grep(/^$d$/, @$ignores)); + if($props->{$d} ne "1") { + $ret.= "$d($props->{$d}),"; + } else { + $ret.="$d,"; + } + } + + if ($ret) { + return "[" . substr($ret, 0, -1) . "]"; + } +} + +sub ParseCopyArgument($$$$$) +{ + my ($self, $fn, $e, $r, $i) = @_; + my $l = $e->{LEVELS}[0]; + + if ($l->{TYPE} eq "ARRAY" and $l->{IS_FIXED} == 1) { + $self->pidl("memcpy(${r}$e->{NAME}, ${i}$e->{NAME}, sizeof(${r}$e->{NAME}));"); + } else { + $self->pidl("${r}$e->{NAME} = ${i}$e->{NAME};"); + } +} + +sub ParseInvalidResponse($$) +{ + my ($self, $type) = @_; + + if ($type eq "sync") { + $self->pidl("return NT_STATUS_INVALID_NETWORK_RESPONSE;"); + } elsif ($type eq "async") { + $self->pidl("tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);"); + $self->pidl("return;"); + } else { + die("ParseInvalidResponse($type)"); + } +} + +sub ParseOutputArgument($$$$$$) +{ + my ($self, $fn, $e, $r, $o, $invalid_response_type) = @_; + my $level = 0; + + if ($e->{LEVELS}[0]->{TYPE} ne "POINTER" and $e->{LEVELS}[0]->{TYPE} ne "ARRAY") { + fatal($e->{ORIGINAL}, "[out] argument is not a pointer or array"); + return; + } + + if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") { + $level = 1; + if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($o$e->{NAME} && ${r}out.$e->{NAME}) {"); + $self->indent; + } + } + + if ($e->{LEVELS}[$level]->{TYPE} eq "ARRAY") { + # This is a call to GenerateFunctionInEnv intentionally. + # Since the data is being copied into a user-provided data + # structure, the user should be able to know the size beforehand + # to allocate a structure of the right size. + my $in_env = GenerateFunctionInEnv($fn, $r); + my $out_env = GenerateFunctionOutEnv($fn, $r); + my $l = $e->{LEVELS}[$level]; + + my $in_var = undef; + if (grep(/in/, @{$e->{DIRECTION}})) { + $in_var = ParseExpr($e->{NAME}, $in_env, $e->{ORIGINAL}); + } + my $out_var = ParseExpr($e->{NAME}, $out_env, $e->{ORIGINAL}); + + my $in_size_is = undef; + my $out_size_is = undef; + my $out_length_is = undef; + + my $avail_len = undef; + my $needed_len = undef; + + $self->pidl("{"); + $self->indent; + my $copy_len_var = "_copy_len_$e->{NAME}"; + $self->pidl("size_t $copy_len_var;"); + + if (not defined($l->{SIZE_IS})) { + if (not $l->{IS_ZERO_TERMINATED}) { + fatal($e->{ORIGINAL}, "no size known for [out] array `$e->{NAME}'"); + } + if (has_property($e, "charset")) { + $avail_len = "ndr_charset_length($in_var, CH_UNIX)"; + $needed_len = "ndr_charset_length($out_var, CH_UNIX)"; + } else { + $avail_len = "ndr_string_length($in_var, sizeof(*$in_var))"; + $needed_len = "ndr_string_length($out_var, sizeof(*$out_var))"; + } + $in_size_is = ""; + $out_size_is = ""; + $out_length_is = ""; + } else { + $in_size_is = ParseExpr($l->{SIZE_IS}, $in_env, $e->{ORIGINAL}); + $out_size_is = ParseExpr($l->{SIZE_IS}, $out_env, $e->{ORIGINAL}); + $out_length_is = $out_size_is; + if (defined($l->{LENGTH_IS})) { + $out_length_is = ParseExpr($l->{LENGTH_IS}, $out_env, $e->{ORIGINAL}); + } + if (has_property($e, "charset")) { + if (defined($in_var)) { + $avail_len = "ndr_charset_length($in_var, CH_UNIX)"; + } else { + $avail_len = $out_length_is; + } + $needed_len = "ndr_charset_length($out_var, CH_UNIX)"; + } + } + + if ($out_size_is ne $in_size_is) { + $self->pidl("if (($out_size_is) > ($in_size_is)) {"); + $self->indent; + $self->ParseInvalidResponse($invalid_response_type); + $self->deindent; + $self->pidl("}"); + } + if ($out_length_is ne $out_size_is) { + $self->pidl("if (($out_length_is) > ($out_size_is)) {"); + $self->indent; + $self->ParseInvalidResponse($invalid_response_type); + $self->deindent; + $self->pidl("}"); + } + if (defined($needed_len)) { + $self->pidl("$copy_len_var = $needed_len;"); + $self->pidl("if ($copy_len_var > $avail_len) {"); + $self->indent; + $self->ParseInvalidResponse($invalid_response_type); + $self->deindent; + $self->pidl("}"); + } else { + $self->pidl("$copy_len_var = $out_length_is;"); + } + + my $dest_ptr = "$o$e->{NAME}"; + my $elem_size = "sizeof(*$dest_ptr)"; + $self->pidl("if ($dest_ptr != $out_var) {"); + $self->indent; + if (has_property($e, "charset")) { + $dest_ptr = "discard_const_p(uint8_t *, $dest_ptr)"; + } + $self->pidl("memcpy($dest_ptr, $out_var, $copy_len_var * $elem_size);"); + $self->deindent; + $self->pidl("}"); + + $self->deindent; + $self->pidl("}"); + } else { + $self->pidl("*$o$e->{NAME} = *${r}out.$e->{NAME};"); + } + + if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") { + if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref") { + $self->deindent; + $self->pidl("}"); + } + } +} + +sub ParseFunction_State($$$$) +{ + my ($self, $if, $fn, $name) = @_; + + my $state_str = "struct dcerpc_$name\_state"; + my $done_fn = "dcerpc_$name\_done"; + + $self->pidl("$state_str {"); + $self->indent; + $self->pidl("struct $name orig;"); + $self->pidl("struct $name tmp;"); + $self->pidl("TALLOC_CTX *out_mem_ctx;"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + $self->pidl("static void $done_fn(struct tevent_req *subreq);"); + $self->pidl(""); +} + +sub ParseFunction_Send($$$$) +{ + my ($self, $if, $fn, $name) = @_; + + my $fn_args = ""; + my $state_str = "struct dcerpc_$name\_state"; + my $done_fn = "dcerpc_$name\_done"; + my $out_mem_ctx = "dcerpc_$name\_out_memory"; + my $fn_str = "struct tevent_req *dcerpc_$name\_send"; + my $pad = genpad($fn_str); + + $fn_args .= "TALLOC_CTX *mem_ctx"; + $fn_args .= ",\n" . $pad . "struct tevent_context *ev"; + $fn_args .= ",\n" . $pad . "struct dcerpc_binding_handle *h"; + + foreach (@{$fn->{ELEMENTS}}) { + my $dir = ElementDirection($_); + my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]); + $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */"; + } + + $self->fn_declare("$fn_str($fn_args)"); + $self->pidl("{"); + $self->indent; + $self->pidl("struct tevent_req *req;"); + $self->pidl("$state_str *state;"); + $self->pidl("struct tevent_req *subreq;"); + $self->pidl(""); + $self->pidl("req = tevent_req_create(mem_ctx, &state,"); + $self->pidl("\t\t\t$state_str);"); + $self->pidl("if (req == NULL) {"); + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("state->out_mem_ctx = NULL;"); + $self->pidl(""); + + $self->pidl("/* In parameters */"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/, @{$e->{DIRECTION}})); + + $self->ParseCopyArgument($fn, $e, "state->orig.in.", "_"); + } + $self->pidl(""); + + my $out_params = 0; + $self->pidl("/* Out parameters */"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless grep(/out/, @{$e->{DIRECTION}}); + + $self->ParseCopyArgument($fn, $e, "state->orig.out.", "_"); + + next if ContainsPipe($e, $e->{LEVELS}[0]); + + $out_params++; + } + $self->pidl(""); + + if (defined($fn->{RETURN_TYPE})) { + $self->pidl("/* Result */"); + $self->pidl("NDR_ZERO_STRUCT(state->orig.out.result);"); + $self->pidl(""); + } + + if ($out_params > 0) { + $self->pidl("state->out_mem_ctx = talloc_named_const(state, 0,"); + $self->pidl("\t\t \"$out_mem_ctx\");"); + $self->pidl("if (tevent_req_nomem(state->out_mem_ctx, req)) {"); + $self->indent; + $self->pidl("return tevent_req_post(req, ev);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + } + + $self->pidl("/* make a temporary copy, that we pass to the dispatch function */"); + $self->pidl("state->tmp = state->orig;"); + $self->pidl(""); + + $self->pidl("subreq = dcerpc_$name\_r_send(state, ev, h, &state->tmp);"); + $self->pidl("if (tevent_req_nomem(subreq, req)) {"); + $self->indent; + $self->pidl("return tevent_req_post(req, ev);"); + $self->deindent; + $self->pidl("}"); + $self->pidl("tevent_req_set_callback(subreq, $done_fn, req);"); + $self->pidl("return req;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseFunction_Done($$$$) +{ + my ($self, $if, $fn, $name) = @_; + + my $state_str = "struct dcerpc_$name\_state"; + my $done_fn = "dcerpc_$name\_done"; + + $self->pidl("static void $done_fn(struct tevent_req *subreq)"); + $self->pidl("{"); + $self->indent; + $self->pidl("struct tevent_req *req = tevent_req_callback_data("); + $self->pidl("\tsubreq, struct tevent_req);"); + $self->pidl("$state_str *state = tevent_req_data("); + $self->pidl("\treq, $state_str);"); + $self->pidl("NTSTATUS status;"); + $self->pidl("TALLOC_CTX *mem_ctx;"); + $self->pidl(""); + + $self->pidl("if (state->out_mem_ctx) {"); + $self->indent; + $self->pidl("mem_ctx = state->out_mem_ctx;"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $self->pidl("mem_ctx = state;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("status = dcerpc_$name\_r_recv(subreq, mem_ctx);"); + $self->pidl("TALLOC_FREE(subreq);"); + $self->pidl("if (tevent_req_nterror(req, status)) {"); + $self->indent; + $self->pidl("return;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("/* Copy out parameters */"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next if ContainsPipe($e, $e->{LEVELS}[0]); + next unless (grep(/out/, @{$e->{DIRECTION}})); + + $self->ParseOutputArgument($fn, $e, + "state->tmp.", + "state->orig.out.", + "async"); + } + $self->pidl(""); + + if (defined($fn->{RETURN_TYPE})) { + $self->pidl("/* Copy result */"); + $self->pidl("state->orig.out.result = state->tmp.out.result;"); + $self->pidl(""); + } + + $self->pidl("/* Reset temporary structure */"); + $self->pidl("NDR_ZERO_STRUCT(state->tmp);"); + $self->pidl(""); + + $self->pidl("tevent_req_done(req);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseFunction_Recv($$$$) +{ + my ($self, $if, $fn, $name) = @_; + + my $fn_args = ""; + my $state_str = "struct dcerpc_$name\_state"; + my $fn_str = "NTSTATUS dcerpc_$name\_recv"; + my $pad = genpad($fn_str); + + $fn_args .= "struct tevent_req *req,\n" . $pad . "TALLOC_CTX *mem_ctx"; + + if (defined($fn->{RETURN_TYPE})) { + $fn_args .= ",\n" . $pad . mapTypeName($fn->{RETURN_TYPE}). " *result"; + } + + $self->fn_declare("$fn_str($fn_args)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$state_str *state = tevent_req_data("); + $self->pidl("\treq, $state_str);"); + $self->pidl("NTSTATUS status;"); + $self->pidl(""); + $self->pidl("if (tevent_req_is_nterror(req, &status)) {"); + $self->indent; + $self->pidl("tevent_req_received(req);"); + $self->pidl("return status;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("/* Steal possible out parameters to the callers context */"); + $self->pidl("talloc_steal(mem_ctx, state->out_mem_ctx);"); + $self->pidl(""); + + if (defined($fn->{RETURN_TYPE})) { + $self->pidl("/* Return result */"); + $self->pidl("*result = state->orig.out.result;"); + $self->pidl(""); + } + + $self->pidl("tevent_req_received(req);"); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseFunction_Sync($$$$) +{ + my ($self, $if, $fn, $name) = @_; + + if ($self->ParseFunctionHasPipes($fn)) { + $self->pidl_both("/*"); + $self->pidl_both(" * The following function is skipped because"); + $self->pidl_both(" * it uses pipes:"); + $self->pidl_both(" *"); + $self->pidl_both(" * dcerpc_$name()"); + $self->pidl_both(" */"); + $self->pidl_both(""); + return; + } + + my $uname = uc $name; + my $fn_args = ""; + my $fn_str = "NTSTATUS dcerpc_$name"; + my $pad = genpad($fn_str); + + $fn_args .= "struct dcerpc_binding_handle *h,\n" . $pad . "TALLOC_CTX *mem_ctx"; + + foreach (@{$fn->{ELEMENTS}}) { + my $dir = ElementDirection($_); + my $prop = HeaderProperties($_->{PROPERTIES}, ["in", "out"]); + $fn_args .= ",\n" . $pad . DeclLong($_, "_") . " /* $dir $prop */"; + } + + if (defined($fn->{RETURN_TYPE})) { + $fn_args .= ",\n" . $pad . mapTypeName($fn->{RETURN_TYPE}). " *result"; + } + + $self->fn_declare("$fn_str($fn_args)"); + $self->pidl("{"); + $self->indent; + $self->pidl("struct $name r;"); + $self->pidl("NTSTATUS status;"); + $self->pidl(""); + + $self->pidl("/* In parameters */"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/, @{$e->{DIRECTION}})); + + $self->ParseCopyArgument($fn, $e, "r.in.", "_"); + } + $self->pidl(""); + + $self->pidl("/* Out parameters */"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless grep(/out/, @{$e->{DIRECTION}}); + + $self->ParseCopyArgument($fn, $e, "r.out.", "_"); + } + $self->pidl(""); + + if (defined($fn->{RETURN_TYPE})) { + $self->pidl("/* Result */"); + $self->pidl("NDR_ZERO_STRUCT(r.out.result);"); + $self->pidl(""); + } + + $self->pidl("status = dcerpc_$name\_r(h, mem_ctx, &r);"); + $self->pidl("if (!NT_STATUS_IS_OK(status)) {"); + $self->indent; + $self->pidl("return status;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("/* Return variables */"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next if ContainsPipe($e, $e->{LEVELS}[0]); + next unless (grep(/out/, @{$e->{DIRECTION}})); + + $self->ParseOutputArgument($fn, $e, "r.", "_", "sync"); + } + $self->pidl(""); + + $self->pidl("/* Return result */"); + if ($fn->{RETURN_TYPE}) { + $self->pidl("*result = r.out.result;"); + } + $self->pidl(""); + + $self->pidl("return NT_STATUS_OK;"); + + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +##################################################################### +# parse a function +sub ParseFunction($$$) +{ + my ($self, $if, $fn) = @_; + + if ($self->ParseFunctionHasPipes($fn)) { + $self->pidl_both("/*"); + $self->pidl_both(" * The following function is skipped because"); + $self->pidl_both(" * it uses pipes:"); + $self->pidl_both(" *"); + $self->pidl_both(" * dcerpc_$fn->{NAME}_r_send()"); + $self->pidl_both(" * dcerpc_$fn->{NAME}_r_recv()"); + $self->pidl_both(" * dcerpc_$fn->{NAME}_r()"); + $self->pidl_both(" *"); + $self->pidl_both(" * dcerpc_$fn->{NAME}_send()"); + $self->pidl_both(" * dcerpc_$fn->{NAME}_recv()"); + $self->pidl_both(" * dcerpc_$fn->{NAME}()"); + $self->pidl_both(" */"); + $self->pidl_both(""); + warning($fn->{ORIGINAL}, "$fn->{NAME}: dcerpc client does not support pipe yet"); + return; + } + + $self->ParseFunction_r_State($if, $fn, $fn->{NAME}); + $self->ParseFunction_r_Send($if, $fn, $fn->{NAME}); + $self->ParseFunction_r_Done($if, $fn, $fn->{NAME}); + $self->ParseFunction_r_Recv($if, $fn, $fn->{NAME}); + $self->ParseFunction_r_Sync($if, $fn, $fn->{NAME}); + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/out/, @{$e->{DIRECTION}})); + + my $reason = "is not a pointer or array"; + + # TODO: make this fatal at NDR level + if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") { + if ($e->{LEVELS}[1]->{TYPE} eq "DATA" and + Parse::Pidl::Typelist::is_string_type($e->{LEVELS}[1]->{DATA_TYPE})) { + $reason = "is a pointer to a string type"; + } elsif ($e->{LEVELS}[1]->{TYPE} eq "ARRAY" and + $e->{LEVELS}[1]->{IS_ZERO_TERMINATED}) { + next; + } elsif ($e->{LEVELS}[1]->{TYPE} eq "ARRAY" and + not defined($e->{LEVELS}[1]->{SIZE_IS})) { + $reason = "is a pointer to an unsized array"; + } else { + next; + } + } + if ($e->{LEVELS}[0]->{TYPE} eq "ARRAY") { + if (not defined($e->{LEVELS}[0]->{SIZE_IS})) { + $reason = "is an unsized array"; + } else { + next; + } + } + + $self->pidl_both("/*"); + $self->pidl_both(" * The following functions are skipped because"); + $self->pidl_both(" * an [out] argument $e->{NAME} $reason:"); + $self->pidl_both(" *"); + $self->pidl_both(" * dcerpc_$fn->{NAME}_send()"); + $self->pidl_both(" * dcerpc_$fn->{NAME}_recv()"); + $self->pidl_both(" * dcerpc_$fn->{NAME}()"); + $self->pidl_both(" */"); + $self->pidl_both(""); + + error($e->{ORIGINAL}, "$fn->{NAME}: [out] argument '$e->{NAME}' $reason, skip client functions"); + return; + } + + $self->ParseFunction_State($if, $fn, $fn->{NAME}); + $self->ParseFunction_Send($if, $fn, $fn->{NAME}); + $self->ParseFunction_Done($if, $fn, $fn->{NAME}); + $self->ParseFunction_Recv($if, $fn, $fn->{NAME}); + $self->ParseFunction_Sync($if, $fn, $fn->{NAME}); + + $self->pidl_hdr(""); +} + +my %done; + +##################################################################### +# parse the interface definitions +sub ParseInterface($$) +{ + my ($self, $if) = @_; + my $ifu = uc($if->{NAME}); + + $self->pidl_hdr("#ifndef _HEADER_RPC_$if->{NAME}"); + $self->pidl_hdr("#define _HEADER_RPC_$if->{NAME}"); + $self->pidl_hdr(""); + + if (defined $if->{PROPERTIES}->{uuid}) { + $self->pidl_hdr("extern const struct ndr_interface_table ndr_table_$if->{NAME};"); + $self->pidl_hdr(""); + } + + $self->pidl("/* $if->{NAME} - client functions generated by pidl */"); + $self->pidl(""); + + foreach my $fn (@{$if->{FUNCTIONS}}) { + next if defined($done{$fn->{NAME}}); + next if has_property($fn, "noopnum"); + next if has_property($fn, "todo"); + $self->ParseFunction($if, $fn); + $done{$fn->{NAME}} = 1; + } + + $self->pidl_hdr("#endif /* _HEADER_RPC_$if->{NAME} */"); +} + +sub Parse($$$$$$) +{ + my($self,$ndr,$header,$ndr_header,$client_header) = @_; + + $self->pidl("/* client functions auto-generated by pidl */"); + $self->pidl(""); + if (is_intree()) { + $self->pidl("#include \"includes.h\""); + } else { + $self->pidl("#ifndef _GNU_SOURCE"); + $self->pidl("#define _GNU_SOURCE"); + $self->pidl("#endif"); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + } + $self->pidl("#include "); + $self->pidl(choose_header("lib/util/tevent_ntstatus.h", "util/tevent_ntstatus.h").""); + $self->pidl("#include \"$ndr_header\""); + $self->pidl("#include \"$client_header\""); + $self->pidl(""); + + $self->pidl_hdr(choose_header("librpc/rpc/dcerpc.h", "dcerpc.h").""); + $self->pidl_hdr("#include \"$header\""); + + foreach my $x (@{$ndr}) { + ($x->{TYPE} eq "INTERFACE") && $self->ParseInterface($x); + } + + return ($self->{res},$self->{res_hdr}); +} + +1; diff --git a/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm b/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm new file mode 100644 index 0000000..d7386d5 --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/NDR/Parser.pm @@ -0,0 +1,3386 @@ +################################################### +# Samba4 NDR parser generator for IDL structures +# Copyright tridge@samba.org 2000-2003 +# Copyright tpot@samba.org 2001 +# Copyright jelmer@samba.org 2004-2006 +# released under the GNU GPL + +package Parse::Pidl::Samba4::NDR::Parser; +use parent Parse::Pidl::Base; + +require Exporter; +push @ISA, qw(Exporter); +@EXPORT_OK = qw(check_null_pointer NeededFunction NeededElement NeededType $res NeededInterface TypeFunctionName ParseElementPrint); + +use strict; +use warnings; +use Parse::Pidl::Typelist qw(hasType getType mapTypeName mapTypeSpecifier typeHasBody); +use Parse::Pidl::Util qw(has_property + ParseExpr + ParseExprExt + print_uuid + unmake_str + parse_int + parse_range); +use Parse::Pidl::CUtil qw(get_pointer_to get_value_of get_array_element); +use Parse::Pidl::NDR qw(GetPrevLevel GetNextLevel ContainsDeferred ContainsPipe is_charset_array); +use Parse::Pidl::Samba4 qw(is_intree choose_header ArrayDynamicallyAllocated); +use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv EnvSubstituteValue GenerateStructEnv); +use Parse::Pidl qw(warning); + +use vars qw($VERSION); +$VERSION = '0.01'; + +# list of known types +my %typefamily; + +sub new($$) { + my ($class) = @_; + my $self = { res => "", res_hdr => "", deferred => [], tabs => "", defer_tabs => "" }; + bless($self, $class); +} + +sub get_typefamily($) +{ + my $n = shift; + return $typefamily{$n}; +} + +sub append_prefix($$) +{ + my ($e, $var_name) = @_; + my $pointers = 0; + my $arrays = 0; + + foreach my $l (@{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER") { + $pointers++; + } elsif ($l->{TYPE} eq "ARRAY") { + $arrays++; + if (($pointers == 0) and + (not $l->{IS_FIXED}) and + (not $l->{IS_INLINE})) { + return get_value_of($var_name); + } + } elsif ($l->{TYPE} eq "DATA") { + if (Parse::Pidl::Typelist::scalar_is_reference($l->{DATA_TYPE})) { + return get_value_of($var_name) unless ($pointers or $arrays); + } + } + } + + return $var_name; +} + +sub has_fast_array($$) +{ + my ($e,$l) = @_; + + return 0 if ($l->{TYPE} ne "ARRAY"); + + my $nl = GetNextLevel($e,$l); + return 0 unless ($nl->{TYPE} eq "DATA"); + return 0 unless (hasType($nl->{DATA_TYPE})); + + my $t = getType($nl->{DATA_TYPE}); + + # Only uint8 has a fast array function at the moment + return ($t->{NAME} eq "uint8"); +} + +sub is_public_struct +{ + my ($d) = @_; + if (!has_property($d, "public")) { + return 0; + } + my $t = $d; + if ($d->{TYPE} eq "TYPEDEF") { + $t = $d->{DATA}; + } + return $t->{TYPE} eq "STRUCT"; +} + +#################################### +# defer() is like pidl(), but adds to +# a deferred buffer which is then added to the +# output buffer at the end of the structure/union/function +# This is needed to cope with code that must be pushed back +# to the end of a block of elements +sub defer_indent($) { my ($self) = @_; $self->{defer_tabs}.="\t"; } +sub defer_deindent($) { my ($self) = @_; $self->{defer_tabs}=substr($self->{defer_tabs}, 0, -1); } + +sub defer($$) +{ + my ($self, $d) = @_; + if ($d) { + push(@{$self->{deferred}}, $self->{defer_tabs}.$d); + } +} + +######################################## +# add the deferred content to the current +# output +sub add_deferred($) +{ + my ($self) = @_; + $self->pidl($_) foreach (@{$self->{deferred}}); + $self->{deferred} = []; + $self->{defer_tabs} = ""; +} + +##################################################################### +# declare a function public or static, depending on its attributes +sub fn_declare($$$$) +{ + my ($self,$type,$fn,$decl) = @_; + + if (has_property($fn, "no$type")) { + $self->pidl_hdr("$decl;"); + return 0; + } + + if (has_property($fn, "public")) { + $self->pidl_hdr("$decl;"); + $self->pidl("_PUBLIC_ $decl"); + } else { + $self->pidl("static $decl"); + } + + return 1; +} + +################################################################### +# setup any special flags for an element or structure +sub start_flags($$$) +{ + my ($self, $e, $ndr) = @_; + my $flags = has_property($e, "flag"); + if (defined $flags) { + $self->pidl("{"); + $self->indent; + $self->pidl("libndr_flags _flags_save_$e->{TYPE} = $ndr->flags;"); + $self->pidl("ndr_set_flags(&$ndr->flags, $flags);"); + } +} + +################################################################### +# end any special flags for an element or structure +sub end_flags($$$) +{ + my ($self, $e, $ndr) = @_; + my $flags = has_property($e, "flag"); + if (defined $flags) { + $self->pidl("$ndr->flags = _flags_save_$e->{TYPE};"); + $self->deindent; + $self->pidl("}"); + } +} + +##################################################################### +# parse the data of an array - push side +sub ParseArrayPushHeader($$$$$$) +{ + my ($self,$e,$l,$ndr,$var_name,$env) = @_; + + my $size; + my $length; + + if ($l->{IS_ZERO_TERMINATED}) { + if (has_property($e, "charset")) { + $size = $length = "ndr_charset_length($var_name, CH_$e->{PROPERTIES}->{charset})"; + } else { + $size = $length = "ndr_string_length($var_name, sizeof(*$var_name))"; + } + if (defined($l->{SIZE_IS})) { + $size = ParseExpr($l->{SIZE_IS}, $env, $e); + } + if (defined($l->{LENGTH_IS})) { + $length = ParseExpr($l->{LENGTH_IS}, $env, $e); + } + } else { + $size = ParseExpr($l->{SIZE_IS}, $env, $e); + $length = ParseExpr($l->{LENGTH_IS}, $env, $e); + } + + if ((!$l->{IS_SURROUNDING}) and $l->{IS_CONFORMANT}) { + $self->pidl("NDR_CHECK(ndr_push_uint3264($ndr, NDR_SCALARS, $size));"); + } + + if ($l->{IS_VARYING}) { + $self->pidl("NDR_CHECK(ndr_push_uint3264($ndr, NDR_SCALARS, 0));"); # array offset + $self->pidl("NDR_CHECK(ndr_push_uint3264($ndr, NDR_SCALARS, $length));"); + } + + return $length; +} + +sub check_fully_dereferenced($$) +{ + my ($element, $env) = @_; + + return sub ($) { + my $origvar = shift; + my $check = 0; + + # Figure out the number of pointers in $ptr + my $expandedvar = $origvar; + $expandedvar =~ s/^(\**)//; + my $ptr = $1; + + my $var = undef; + foreach (keys %$env) { + if ($env->{$_} eq $expandedvar) { + $var = $_; + last; + } + } + + return($origvar) unless (defined($var)); + my $e; + foreach (@{$element->{PARENT}->{ELEMENTS}}) { + if ($_->{NAME} eq $var) { + $e = $_; + last; + } + } + + $e or die("Environment doesn't match siblings"); + + # See if pointer at pointer level $level + # needs to be checked. + my $nump = 0; + foreach (@{$e->{LEVELS}}) { + if ($_->{TYPE} eq "POINTER") { + $nump = $_->{POINTER_INDEX}+1; + } + } + warning($element->{ORIGINAL}, "Got pointer for `$e->{NAME}', expected fully dereferenced variable") if ($nump > length($ptr)); + return ($origvar); + } +} + +sub check_null_pointer($$$$) +{ + my ($element, $env, $print_fn, $return) = @_; + + return sub ($) { + my $expandedvar = shift; + my $check = 0; + + # Figure out the number of pointers in $ptr + $expandedvar =~ s/^(\**)//; + my $ptr = $1; + + my $var = undef; + foreach (keys %$env) { + if ($env->{$_} eq $expandedvar) { + $var = $_; + last; + } + } + + if (defined($var)) { + my $e; + # lookup ptr in $e + foreach (@{$element->{PARENT}->{ELEMENTS}}) { + if ($_->{NAME} eq $var) { + $e = $_; + last; + } + } + + $e or die("Environment doesn't match siblings"); + + # See if pointer at pointer level $level + # needs to be checked. + foreach my $l (@{$e->{LEVELS}}) { + if ($l->{TYPE} eq "POINTER" and + $l->{POINTER_INDEX} == length($ptr)) { + # No need to check ref pointers + $check = ($l->{POINTER_TYPE} ne "ref"); + last; + } + + if ($l->{TYPE} eq "DATA") { + warning($element, "too much dereferences for `$var'"); + } + } + } else { + warning($element, "unknown dereferenced expression `$expandedvar'"); + $check = 1; + } + + $print_fn->("if ($ptr$expandedvar == NULL) $return") if $check; + } +} + +sub is_deferred_switch_non_empty($) +{ + # 1 if there needs to be a deferred branch in an ndr_pull/push, + # 0 otherwise. + my ($e) = @_; + my $have_default = 0; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + if ($el->{TYPE} ne "EMPTY") { + if (ContainsDeferred($el, $el->{LEVELS}[0])) { + return 1; + } + } + } + return ! $have_default; +} + +sub ParseArrayPullGetSize($$$$$$) +{ + my ($self,$e,$l,$ndr,$var_name,$env) = @_; + + my $size; + + my $array_size = "size_$e->{NAME}_$l->{LEVEL_INDEX}"; + + if ($l->{IS_CONFORMANT} and (defined($l->{SIZE_IS}) or not $l->{IS_ZERO_TERMINATED})) { + $self->pidl("NDR_CHECK(ndr_get_array_size($ndr, (void*)" . get_pointer_to($var_name) . ", &$array_size));"); + + } elsif ($l->{IS_CONFORMANT}) { + # This will be the last use of the array_size token + $self->pidl("NDR_CHECK(ndr_steal_array_size($ndr, (void*)" . get_pointer_to($var_name) . ", &$array_size));"); + + } elsif ($l->{IS_ZERO_TERMINATED} and $l->{SIZE_IS} == 0 and $l->{LENGTH_IS} == 0) { # Noheader arrays + $size = "ndr_get_string_size($ndr, sizeof(*$var_name))"; + $self->pidl("$array_size = $size;"); + + } else { + $size = ParseExprExt($l->{SIZE_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, + "return ndr_pull_error($ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for size_is()\");"), + check_fully_dereferenced($e, $env)); + $self->pidl("$array_size = $size;"); + } + + if (my $range = has_property($e, "range")) { + my ($low, $high) = parse_range($range); + if ($low < 0) { + warning(0, "$low is invalid for the range of an array size"); + } + if ($low == 0) { + $self->pidl("if ($array_size > $high) {"); + } else { + $self->pidl("if ($array_size < $low || $array_size > $high) {"); + } + $self->pidl("\treturn ndr_pull_error($ndr, NDR_ERR_RANGE, \"value (%\"PRIu32\") out of range (%\"PRIu32\" - %\"PRIu32\")\", $array_size, (uint32_t)($low), (uint32_t)($high));"); + + $self->pidl("}"); + } + + return $array_size; +} + +##################################################################### +# parse an array - pull side +sub ParseArrayPullGetLength($$$$$$;$) +{ + my ($self,$e,$l,$ndr,$var_name,$env,$array_size) = @_; + + if (not defined($array_size)) { + $array_size = $self->ParseArrayPullGetSize($e, $l, $ndr, $var_name, $env); + } + + if (not $l->{IS_VARYING}) { + return $array_size; + } + + my $array_length = "length_$e->{NAME}_$l->{LEVEL_INDEX}"; + if ($l->{IS_VARYING} and (defined($l->{LENGTH_IS}) or not $l->{IS_ZERO_TERMINATED})) { + $self->pidl("NDR_CHECK(ndr_get_array_length($ndr, (void*)" . get_pointer_to($var_name) . ", &$array_length));"); + } else { + # This will be the last use of the array_length token + $self->pidl("NDR_CHECK(ndr_steal_array_length($ndr, (void*)" . get_pointer_to($var_name) . ", &$array_length));"); + } + + if (my $range = has_property($e, "range")) { + my ($low, $high) = parse_range($range); + if ($low < 0) { + warning(0, "$low is invalid for the range of an array size"); + } + if ($low == 0) { + $self->pidl("if ($array_length > $high) {"); + } else { + $self->pidl("if ($array_length < $low || $array_length > $high) {"); + } + $self->pidl("\treturn ndr_pull_error($ndr, NDR_ERR_RANGE, \"value (%\"PRIu32\") out of range (%\"PRIu32\" - %\"PRIu32\")\", $array_length, (uint32_t)($low), (uint32_t)($high));"); + $self->pidl("}"); + } + + return $array_length; +} + +##################################################################### +# parse an array - pull side +sub ParseArrayPullHeader($$$$$$) +{ + my ($self,$e,$l,$ndr,$var_name,$env) = @_; + + if ((!$l->{IS_SURROUNDING}) and $l->{IS_CONFORMANT}) { + $self->pidl("NDR_CHECK(ndr_pull_array_size($ndr, " . get_pointer_to($var_name) . "));"); + } + + if ($l->{IS_VARYING}) { + $self->pidl("NDR_CHECK(ndr_pull_array_length($ndr, " . get_pointer_to($var_name) . "));"); + } + + my $array_size = $self->ParseArrayPullGetSize($e, $l, $ndr, $var_name, $env); + my $array_length = $self->ParseArrayPullGetLength($e, $l, $ndr, $var_name, $env, $array_size); + + if ($array_length ne $array_size) { + $self->pidl("if ($array_length > $array_size) {"); + $self->indent; + $self->pidl("return ndr_pull_error($ndr, NDR_ERR_ARRAY_SIZE, \"Bad array size %\"PRIu32\": should exceed array length %\"PRIu32\"\", $array_size, $array_length);"); + $self->deindent; + $self->pidl("}"); + } + + if ($l->{IS_CONFORMANT} and (defined($l->{SIZE_IS}) or not $l->{IS_ZERO_TERMINATED})) { + $self->defer("if ($var_name) {"); + $self->defer_indent; + my $size = ParseExprExt($l->{SIZE_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->defer(shift); }, + "return ndr_pull_error($ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for size_is()\");"), + check_fully_dereferenced($e, $env)); + if (ContainsDeferred($e, $l)) { + # We will be needing the array_size token in + # the NDR_BUFFERS call, so don't steal it now + $self->defer("NDR_CHECK(ndr_check_array_size($ndr, (void*)" . get_pointer_to($var_name) . ", $size));"); + } else { + # This will be deferred until after the last ndr_get_array_size() + $self->defer("NDR_CHECK(ndr_check_steal_array_size($ndr, (void*)" . get_pointer_to($var_name) . ", $size));"); + } + $self->defer_deindent; + $self->defer("}"); + } + + if ($l->{IS_VARYING} and (defined($l->{LENGTH_IS}) or not $l->{IS_ZERO_TERMINATED})) { + $self->defer("if ($var_name) {"); + $self->defer_indent; + my $length = ParseExprExt($l->{LENGTH_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->defer(shift); }, + "return ndr_pull_error($ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for length_is()\");"), + check_fully_dereferenced($e, $env)); + # This will be deferred until after the last ndr_get_array_length() + $self->defer("NDR_CHECK(ndr_check_steal_array_length($ndr, (void*)" . get_pointer_to($var_name) . ", $length));"); + $self->defer_deindent; + $self->defer("}"); + } + + if (ArrayDynamicallyAllocated($e,$l) and not is_charset_array($e,$l)) { + $self->AllocateArrayLevel($e,$l,$ndr,$var_name,$array_size); + } + + return $array_length; +} + +sub compression_alg($$$) +{ + my ($e, $l, $env) = @_; + my ($alg, $clen, $dlen) = split(/,/, $l->{COMPRESSION}); + + return ParseExpr($alg, $env, $e->{ORIGINAL}); +} + +sub compression_clen($$$) +{ + my ($e, $l, $env) = @_; + my ($alg, $clen, $dlen) = split(/,/, $l->{COMPRESSION}); + + return ParseExpr($clen, $env, $e->{ORIGINAL}); +} + +sub compression_dlen($$$) +{ + my ($e,$l,$env) = @_; + my ($alg, $clen, $dlen) = split(/,/, $l->{COMPRESSION}); + + return ParseExpr($dlen, $env, $e->{ORIGINAL}); +} + +sub ParseCompressionPushStart($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $comndr = "$ndr\_compressed"; + my $alg = compression_alg($e, $l, $env); + + $self->pidl("{"); + $self->indent; + $self->pidl("struct ndr_push *$comndr;"); + $self->pidl("NDR_CHECK(ndr_push_compression_state_init($ndr, $alg));"); + $self->pidl("NDR_CHECK(ndr_push_compression_start($ndr, &$comndr));"); + + return $comndr; +} + +sub ParseCompressionPushEnd($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $comndr = "$ndr\_compressed"; + my $alg = compression_alg($e, $l, $env); + + $self->pidl("NDR_CHECK(ndr_push_compression_end($ndr, $comndr));"); + $self->pidl("TALLOC_FREE($ndr->cstate);"); + $self->deindent; + $self->pidl("}"); +} + +sub ParseCompressionPullStart($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $comndr = "$ndr\_compressed"; + my $alg = compression_alg($e, $l, $env); + my $dlen = compression_dlen($e, $l, $env); + my $clen = compression_clen($e, $l, $env); + + $self->pidl("{"); + $self->indent; + $self->pidl("struct ndr_pull *$comndr;"); + $self->pidl("NDR_CHECK(ndr_pull_compression_start($ndr, &$comndr, $alg, $dlen, $clen));"); + + return $comndr; +} + +sub ParseCompressionPullEnd($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $comndr = "$ndr\_compressed"; + my $alg = compression_alg($e, $l, $env); + my $dlen = compression_dlen($e, $l, $env); + + $self->pidl("NDR_CHECK(ndr_pull_compression_end($ndr, $comndr, $alg, $dlen));"); + $self->deindent; + $self->pidl("}"); +} + +sub ParseSubcontextPushStart($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $subndr = "_ndr_$e->{NAME}"; + my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); + + $self->pidl("{"); + $self->indent; + $self->pidl("struct ndr_push *$subndr;"); + $self->pidl("NDR_CHECK(ndr_push_subcontext_start($ndr, &$subndr, $l->{HEADER_SIZE}, $subcontext_size));"); + + if (defined $l->{COMPRESSION}) { + $subndr = $self->ParseCompressionPushStart($e, $l, $subndr, $env); + } + + return $subndr; +} + +sub ParseSubcontextPushEnd($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $subndr = "_ndr_$e->{NAME}"; + my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); + + if (defined $l->{COMPRESSION}) { + $self->ParseCompressionPushEnd($e, $l, $subndr, $env); + } + + $self->pidl("NDR_CHECK(ndr_push_subcontext_end($ndr, $subndr, $l->{HEADER_SIZE}, $subcontext_size));"); + $self->deindent; + $self->pidl("}"); +} + +sub ParseSubcontextPullStart($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $subndr = "_ndr_$e->{NAME}"; + my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); + + $self->pidl("{"); + $self->indent; + $self->pidl("struct ndr_pull *$subndr;"); + $self->pidl("ssize_t sub_size = $subcontext_size;"); + $self->pidl("NDR_CHECK(ndr_pull_subcontext_start($ndr, &$subndr, $l->{HEADER_SIZE}, sub_size));"); + + if (defined $l->{COMPRESSION}) { + $subndr = $self->ParseCompressionPullStart($e, $l, $subndr, $env); + } + + return $subndr; +} + +sub ParseSubcontextPullEnd($$$$$) +{ + my ($self,$e,$l,$ndr,$env) = @_; + my $subndr = "_ndr_$e->{NAME}"; + my $subcontext_size = ParseExpr($l->{SUBCONTEXT_SIZE}, $env, $e->{ORIGINAL}); + + if (defined $l->{COMPRESSION}) { + $self->ParseCompressionPullEnd($e, $l, $subndr, $env); + } + + $self->pidl("NDR_CHECK(ndr_pull_subcontext_end($ndr, $subndr, $l->{HEADER_SIZE}, sub_size));"); + $self->deindent; + $self->pidl("}"); +} + +sub ParseElementPushLevel +{ + my ($self,$e,$l,$ndr,$var_name,$env,$primitives,$deferred) = @_; + + my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); + + if ($l->{TYPE} eq "ARRAY" and ($l->{IS_CONFORMANT} or $l->{IS_VARYING})) { + $var_name = get_pointer_to($var_name); + } + + if (defined($ndr_flags)) { + if ($l->{TYPE} eq "SUBCONTEXT") { + my $subndr = $self->ParseSubcontextPushStart($e, $l, $ndr, $env); + $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $subndr, $var_name, $env, 1, 1); + $self->ParseSubcontextPushEnd($e, $l, $ndr, $env); + } elsif ($l->{TYPE} eq "POINTER") { + $self->ParsePtrPush($e, $l, $ndr, $var_name); + } elsif ($l->{TYPE} eq "ARRAY") { + my $length = $self->ParseArrayPushHeader($e, $l, $ndr, $var_name, $env); + + my $nl = GetNextLevel($e, $l); + + # Allow speedups for arrays of scalar types + if (is_charset_array($e,$l)) { + if ($l->{IS_TO_NULL}) { + $self->pidl("NDR_CHECK(ndr_push_charset_to_null($ndr, $ndr_flags, $var_name, $length, sizeof(" . mapTypeName($nl->{DATA_TYPE}) . "), CH_$e->{PROPERTIES}->{charset}));"); + } else { + $self->pidl("NDR_CHECK(ndr_push_charset($ndr, $ndr_flags, $var_name, $length, sizeof(" . mapTypeName($nl->{DATA_TYPE}) . "), CH_$e->{PROPERTIES}->{charset}));"); + } + return; + } elsif (has_fast_array($e,$l)) { + $self->pidl("NDR_CHECK(ndr_push_array_$nl->{DATA_TYPE}($ndr, $ndr_flags, $var_name, $length));"); + return; + } + } elsif ($l->{TYPE} eq "DATA") { + $self->ParseDataPush($e, $l, $ndr, $var_name, $primitives, $deferred); + } elsif ($l->{TYPE} eq "TYPEDEF") { + $typefamily{$e->{DATA}->{TYPE}}->{PUSH_FN_BODY}->($self, $e->{DATA}, $ndr, $var_name); + } + } + + if ($l->{TYPE} eq "POINTER" and $l->{POINTER_TYPE} eq "ignore") { + $self->pidl("/* [ignore] '$e->{NAME}' */"); + } elsif ($l->{TYPE} eq "POINTER" and $deferred) { + my $rel_var_name = $var_name; + if ($l->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($var_name) {"); + $self->indent; + if ($l->{POINTER_TYPE} eq "relative") { + $self->pidl("NDR_CHECK(ndr_push_relative_ptr2_start($ndr, $rel_var_name));"); + } + if ($l->{POINTER_TYPE} eq "relative_short") { + $self->pidl("NDR_CHECK(ndr_push_short_relative_ptr2($ndr, $var_name));"); + } + } + $var_name = get_value_of($var_name); + $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 1, 1); + + if ($l->{POINTER_TYPE} ne "ref") { + if ($l->{POINTER_TYPE} eq "relative") { + $self->pidl("NDR_CHECK(ndr_push_relative_ptr2_end($ndr, $rel_var_name));"); + } + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "ARRAY" and not has_fast_array($e,$l) and + not is_charset_array($e, $l)) { + my $length = ParseExpr($l->{LENGTH_IS}, $env, $e->{ORIGINAL}); + my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}"; + + my $array_pointless = ($length eq "0"); + + if ($array_pointless) { + warning($e->{ORIGINAL}, "pointless array `$e->{NAME}' will always have size 0"); + } + + $var_name = get_array_element($var_name, $counter); + + if ((($primitives and not $l->{IS_DEFERRED}) or ($deferred and $l->{IS_DEFERRED})) and not $array_pointless) { + $self->pidl("for ($counter = 0; $counter < ($length); $counter++) {"); + $self->indent; + $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 1, 0); + $self->deindent; + $self->pidl("}"); + } + + if ($deferred and ContainsDeferred($e, $l) and not $array_pointless) { + $self->pidl("for ($counter = 0; $counter < ($length); $counter++) {"); + $self->indent; + $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, 0, 1); + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "SWITCH") { + my $nl = GetNextLevel($e,$l); + my $needs_deferred_switch = is_deferred_switch_non_empty($nl); + + # Avoid setting a switch value if it will not be + # consumed again in the NDR_BUFFERS pull + if ($needs_deferred_switch or !$deferred) { + $self->ParseSwitchPush($e, $l, $ndr, $var_name, $env); + } + $self->ParseElementPushLevel($e, GetNextLevel($e, $l), $ndr, $var_name, $env, $primitives, $deferred); + } +} + +##################################################################### +# parse scalars in a structure element +sub ParseElementPush($$$$$$) +{ + my ($self,$e,$ndr,$env,$primitives,$deferred) = @_; + my $subndr = undef; + + my $var_name = $env->{$e->{NAME}}; + + if (has_property($e, "skip") or has_property($e, "skip_noinit")) { + $self->pidl("/* [skip] '$var_name' */"); + return; + } + + return if ContainsPipe($e, $e->{LEVELS}[0]); + + return unless $primitives or ($deferred and ContainsDeferred($e, $e->{LEVELS}[0])); + + # Representation type is different from transmit_as + if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { + $self->pidl("{"); + $self->indent; + my $transmit_name = "_transmit_$e->{NAME}"; + $self->pidl(mapTypeName($e->{TYPE}) ." $transmit_name;"); + $self->pidl("NDR_CHECK(ndr_$e->{REPRESENTATION_TYPE}_to_$e->{TYPE}($var_name, " . get_pointer_to($transmit_name) . "));"); + $var_name = $transmit_name; + } + + $var_name = append_prefix($e, $var_name); + + $self->start_flags($e, $ndr); + + if (defined(my $value = has_property($e, "value"))) { + $var_name = ParseExpr($value, $env, $e->{ORIGINAL}); + } + + $self->ParseElementPushLevel($e, $e->{LEVELS}[0], $ndr, $var_name, $env, $primitives, $deferred); + + $self->end_flags($e, $ndr); + + if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { + $self->deindent; + $self->pidl("}"); + } +} + +##################################################################### +# parse a pointer in a struct element or function +sub ParsePtrPush($$$$$) +{ + my ($self,$e,$l,$ndr,$var_name) = @_; + + if ($l->{POINTER_TYPE} eq "ref") { + if ($l->{LEVEL_INDEX} > 0) { + $self->pidl("if ($var_name == NULL) {"); + $self->indent; + $self->pidl("return ndr_push_error($ndr, NDR_ERR_INVALID_POINTER, \"NULL [ref] pointer\");"); + $self->deindent; + $self->pidl("}"); + } + if ($l->{LEVEL} eq "EMBEDDED") { + $self->pidl("NDR_CHECK(ndr_push_ref_ptr(ndr)); /* $var_name */"); + } + } elsif ($l->{POINTER_TYPE} eq "relative") { + $self->pidl("NDR_CHECK(ndr_push_relative_ptr1($ndr, $var_name));"); + } elsif ($l->{POINTER_TYPE} eq "relative_short") { + $self->pidl("NDR_CHECK(ndr_push_short_relative_ptr1($ndr, $var_name));"); + } elsif ($l->{POINTER_TYPE} eq "unique") { + $self->pidl("NDR_CHECK(ndr_push_unique_ptr($ndr, $var_name));"); + } elsif ($l->{POINTER_TYPE} eq "full") { + $self->pidl("NDR_CHECK(ndr_push_full_ptr($ndr, $var_name));"); + } elsif ($l->{POINTER_TYPE} eq "ignore") { + # We don't want this pointer to appear on the wire at all + $self->pidl("NDR_CHECK(ndr_push_uint3264(ndr, NDR_SCALARS, 0));"); + } else { + die("Unhandled pointer type $l->{POINTER_TYPE}"); + } +} + +sub need_pointer_to($$$) +{ + my ($e, $l, $scalar_only) = @_; + + my $t; + if (ref($l->{DATA_TYPE})) { + $t = "$l->{DATA_TYPE}->{TYPE}_$l->{DATA_TYPE}->{NAME}"; + } else { + $t = $l->{DATA_TYPE}; + } + + if (not Parse::Pidl::Typelist::is_scalar($t)) { + return 1 if $scalar_only; + } + + my $arrays = 0; + + foreach my $tl (@{$e->{LEVELS}}) { + last if $l == $tl; + if ($tl->{TYPE} eq "ARRAY") { + $arrays++; + } + } + + if (Parse::Pidl::Typelist::scalar_is_reference($t)) { + return 1 unless $arrays; + } + + return 0; +} + +sub ParseDataPrint($$$$$) +{ + my ($self, $e, $l, $ndr, $var_name) = @_; + + if (not ref($l->{DATA_TYPE}) or defined($l->{DATA_TYPE}->{NAME})) { + + if (need_pointer_to($e, $l, 1)) { + $var_name = get_pointer_to($var_name); + } + + $self->pidl(TypeFunctionName("ndr_print", $l->{DATA_TYPE})."($ndr, \"$e->{NAME}\", $var_name);"); + } else { + $self->ParseTypePrint($l->{DATA_TYPE}, $ndr, $var_name); + } +} + +##################################################################### +# print scalars in a structure element +sub ParseElementPrint($$$$$) +{ + my($self, $e, $ndr, $var_name, $env) = @_; + + return if (has_property($e, "noprint")); + my $cur_depth = 0; + my $ignore_depth = 0xFFFF; + + $self->start_flags($e, $ndr); + if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { + $self->pidl("ndr_print_$e->{REPRESENTATION_TYPE}($ndr, \"$e->{NAME}\", $var_name);"); + $self->end_flags($e, $ndr); + return; + } + + $var_name = append_prefix($e, $var_name); + + if (defined(my $value = has_property($e, "value"))) { + $var_name = "($ndr->flags & LIBNDR_PRINT_SET_VALUES)?" . ParseExpr($value,$env, $e->{ORIGINAL}) . ":$var_name"; + } + + foreach my $l (@{$e->{LEVELS}}) { + $cur_depth += 1; + + if ($cur_depth > $ignore_depth) { + next; + } + + if ($l->{TYPE} eq "POINTER") { + $self->pidl("ndr_print_ptr($ndr, \"$e->{NAME}\", $var_name);"); + if ($l->{POINTER_TYPE} eq "ignore") { + $self->pidl("/* [ignore] '$e->{NAME}' */"); + $ignore_depth = $cur_depth; + last; + } + $self->pidl("$ndr->depth++;"); + if ($l->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($var_name) {"); + $self->indent; + } + $var_name = get_value_of($var_name); + } elsif ($l->{TYPE} eq "ARRAY") { + my $length; + + if ($l->{IS_CONFORMANT} or $l->{IS_VARYING}) { + $var_name = get_pointer_to($var_name); + } + + if ($l->{IS_ZERO_TERMINATED} and not defined($l->{LENGTH_IS})) { + $length = "ndr_string_length($var_name, sizeof(*$var_name))"; + } else { + $length = ParseExprExt($l->{LENGTH_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, "return;"), check_fully_dereferenced($e, $env)); + } + + if (is_charset_array($e,$l)) { + $self->pidl("ndr_print_string($ndr, \"$e->{NAME}\", $var_name);"); + last; + } elsif (has_fast_array($e, $l)) { + my $nl = GetNextLevel($e, $l); + $self->pidl("ndr_print_array_$nl->{DATA_TYPE}($ndr, \"$e->{NAME}\", $var_name, $length);"); + last; + } else { + my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}"; + + $self->pidl("$ndr->print($ndr, \"%s: ARRAY(%\"PRIu32\")\", \"$e->{NAME}\", (uint32_t)($length));"); + $self->pidl("$ndr->depth++;"); + $self->pidl("for ($counter = 0; $counter < ($length); $counter++) {"); + $self->indent; + + $var_name = get_array_element($var_name, $counter); + } + } elsif ($l->{TYPE} eq "DATA") { + $self->ParseDataPrint($e, $l, $ndr, $var_name); + } elsif ($l->{TYPE} eq "SWITCH") { + my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, "return;"), check_fully_dereferenced($e, $env)); + $self->pidl("ndr_print_set_switch_value($ndr, " . get_pointer_to($var_name) . ", $switch_var);"); + } + } + + foreach my $l (reverse @{$e->{LEVELS}}) { + $cur_depth -= 1; + + if ($cur_depth > $ignore_depth) { + next; + } + + if ($l->{TYPE} eq "POINTER") { + if ($l->{POINTER_TYPE} eq "ignore") { + next; + } + + if ($l->{POINTER_TYPE} ne "ref") { + $self->deindent; + $self->pidl("}"); + } + $self->pidl("$ndr->depth--;"); + } elsif (($l->{TYPE} eq "ARRAY") + and not is_charset_array($e,$l) + and not has_fast_array($e,$l)) { + $self->deindent; + $self->pidl("}"); + $self->pidl("$ndr->depth--;"); + } + } + + $self->end_flags($e, $ndr); +} + +##################################################################### +# parse scalars in a structure element - pull size +sub ParseSwitchPull($$$$$$) +{ + my($self,$e,$l,$ndr,$var_name,$env) = @_; + my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, + "return ndr_pull_error($ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for switch_is()\");"), + check_fully_dereferenced($e, $env)); + + $var_name = get_pointer_to($var_name); + $self->pidl("NDR_CHECK(ndr_pull_set_switch_value($ndr, $var_name, $switch_var));"); +} + +##################################################################### +# push switch element +sub ParseSwitchPush($$$$$$) +{ + my($self,$e,$l,$ndr,$var_name,$env) = @_; + my $switch_var = ParseExprExt($l->{SWITCH_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, + "return ndr_push_error($ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for switch_is()\");"), + check_fully_dereferenced($e, $env)); + + $var_name = get_pointer_to($var_name); + $self->pidl("NDR_CHECK(ndr_push_set_switch_value($ndr, $var_name, $switch_var));"); +} + +sub ParseDataPull($$$$$$$) +{ + my ($self,$e,$l,$ndr,$var_name,$primitives,$deferred) = @_; + + if (not ref($l->{DATA_TYPE}) or defined($l->{DATA_TYPE}->{NAME})) { + + my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); + + if (need_pointer_to($e, $l, 0)) { + $var_name = get_pointer_to($var_name); + } + + $var_name = get_pointer_to($var_name); + + if (my $depth = has_property($e, "max_recursion")) { + my $d = parse_int($depth); + $self->pidl("NDR_RECURSION_CHECK($ndr, $d);"); + } + $self->pidl("NDR_CHECK(".TypeFunctionName("ndr_pull", $l->{DATA_TYPE})."($ndr, $ndr_flags, $var_name));"); + if (has_property($e, "max_recursion")) { + $self->pidl("NDR_RECURSION_UNWIND($ndr);"); + } + + my $pl = GetPrevLevel($e, $l); + + my $range = has_property($e, "range"); + if ($range and (not $pl or $pl->{TYPE} ne "ARRAY")) { + $var_name = get_value_of($var_name); + my $signed = Parse::Pidl::Typelist::is_signed($l->{DATA_TYPE}); + my ($low, $high) = parse_range($range); + if ($low < 0 and not $signed) { + warning(0, "$low is invalid for the range of an unsigned type"); + } + if ($low == 0 and not $signed) { + $self->pidl("if ($var_name > $high) {"); + } else { + $self->pidl("if ($var_name < $low || $var_name > $high) {"); + } + + my $data_type = mapTypeName($l->{DATA_TYPE}); + my $fmt = mapTypeSpecifier($data_type); + + if (!defined($fmt)) { + if (getType($l->{DATA_TYPE})->{DATA}->{TYPE} eq "ENUM") { + $data_type = "int"; + $fmt = "d"; + } else { + die("Format ($data_type) not supported"); + } + } + + $self->pidl("\treturn ndr_pull_error($ndr, NDR_ERR_RANGE, \"value (%$fmt) out of range (%$fmt - %$fmt)\", ($data_type)($var_name), ($data_type)($low), ($data_type)($high));"); + $self->pidl("}"); + } + } else { + $self->ParseTypePull($l->{DATA_TYPE}, $ndr, $var_name, $primitives, $deferred); + } +} + +sub ParseDataPush($$$$$$$) +{ + my ($self,$e,$l,$ndr,$var_name,$primitives,$deferred) = @_; + + if (not ref($l->{DATA_TYPE}) or defined($l->{DATA_TYPE}->{NAME})) { + + my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); + + # strings are passed by value rather than reference + if (need_pointer_to($e, $l, 1)) { + $var_name = get_pointer_to($var_name); + } + + $self->pidl("NDR_CHECK(".TypeFunctionName("ndr_push", $l->{DATA_TYPE})."($ndr, $ndr_flags, $var_name));"); + } else { + $self->ParseTypePush($l->{DATA_TYPE}, $ndr, $var_name, $primitives, $deferred); + } +} + +sub CalcNdrFlags($$$) +{ + my ($l,$primitives,$deferred) = @_; + + my $scalars = 0; + my $buffers = 0; + + # Add NDR_SCALARS if this one is deferred + # and deferreds may be pushed + $scalars = 1 if ($l->{IS_DEFERRED} and $deferred); + + # Add NDR_SCALARS if this one is not deferred and + # primitives may be pushed + $scalars = 1 if (!$l->{IS_DEFERRED} and $primitives); + + # Add NDR_BUFFERS if this one contains deferred stuff + # and deferreds may be pushed + $buffers = 1 if ($l->{CONTAINS_DEFERRED} and $deferred); + + return "NDR_SCALARS|NDR_BUFFERS" if ($scalars and $buffers); + return "NDR_SCALARS" if ($scalars); + return "NDR_BUFFERS" if ($buffers); + return undef; +} + +sub ParseMemCtxPullFlags($$$$) +{ + my ($self, $e, $l) = @_; + + return undef unless ($l->{TYPE} eq "POINTER" or $l->{TYPE} eq "ARRAY"); + return undef if (($l->{TYPE} eq "POINTER") and ($l->{POINTER_TYPE} eq "ignore")); + + return undef unless ($l->{TYPE} ne "ARRAY" or ArrayDynamicallyAllocated($e,$l)); + return undef if has_fast_array($e, $l); + return undef if is_charset_array($e, $l); + + my $mem_flags = "0"; + + if (($l->{TYPE} eq "POINTER") and ($l->{POINTER_TYPE} eq "ref")) { + my $nl = GetNextLevel($e, $l); + return undef if ($nl->{TYPE} eq "PIPE"); + return undef if ($nl->{TYPE} eq "ARRAY"); + return undef if (($nl->{TYPE} eq "DATA") and (Parse::Pidl::Typelist::is_string_type($nl->{DATA_TYPE}))); + + if ($l->{LEVEL} eq "TOP") { + $mem_flags = "LIBNDR_FLAG_REF_ALLOC"; + } + } + + return $mem_flags; +} + +sub ParseMemCtxPullStart($$$$$) +{ + my ($self, $e, $l, $ndr, $ptr_name) = @_; + + my $mem_r_ctx = "_mem_save_$e->{NAME}_$l->{LEVEL_INDEX}"; + my $mem_c_ctx = $ptr_name; + my $mem_c_flags = $self->ParseMemCtxPullFlags($e, $l); + + return unless defined($mem_c_flags); + + $self->pidl("$mem_r_ctx = NDR_PULL_GET_MEM_CTX($ndr);"); + $self->pidl("NDR_PULL_SET_MEM_CTX($ndr, $mem_c_ctx, $mem_c_flags);"); +} + +sub ParseMemCtxPullEnd($$$$) +{ + my ($self, $e, $l, $ndr) = @_; + + my $mem_r_ctx = "_mem_save_$e->{NAME}_$l->{LEVEL_INDEX}"; + my $mem_r_flags = $self->ParseMemCtxPullFlags($e, $l); + + return unless defined($mem_r_flags); + + $self->pidl("NDR_PULL_SET_MEM_CTX($ndr, $mem_r_ctx, $mem_r_flags);"); +} + +sub CheckStringTerminator($$$$$) +{ + my ($self,$ndr,$e,$l,$length) = @_; + my $nl = GetNextLevel($e, $l); + + # Make sure last element is zero! + $self->pidl("NDR_CHECK(ndr_check_string_terminator($ndr, $length, sizeof($nl->{DATA_TYPE}_t)));"); +} + +sub ParseElementPullLevel +{ + my($self,$e,$l,$ndr,$var_name,$env,$primitives,$deferred) = @_; + + my $ndr_flags = CalcNdrFlags($l, $primitives, $deferred); + my $array_length = undef; + + if (has_property($e, "skip") or has_property($e, "skip_noinit")) { + $self->pidl("/* [skip] '$var_name' */"); + if (not has_property($e, "skip_noinit")) { + $self->pidl("NDR_ZERO_STRUCT($var_name);"); + } + return; + } + + if ($l->{TYPE} eq "ARRAY" and ($l->{IS_VARYING} or $l->{IS_CONFORMANT})) { + $var_name = get_pointer_to($var_name); + } + + # Only pull something if there's actually something to be pulled + if (defined($ndr_flags)) { + if ($l->{TYPE} eq "SUBCONTEXT") { + my $subndr = $self->ParseSubcontextPullStart($e, $l, $ndr, $env); + $self->ParseElementPullLevel($e, GetNextLevel($e,$l), $subndr, $var_name, $env, 1, 1); + $self->ParseSubcontextPullEnd($e, $l, $ndr, $env); + } elsif ($l->{TYPE} eq "ARRAY") { + my $length = $self->ParseArrayPullHeader($e, $l, $ndr, $var_name, $env); + $array_length = $length; + + my $nl = GetNextLevel($e, $l); + + if (is_charset_array($e,$l)) { + if ($l->{IS_ZERO_TERMINATED}) { + $self->CheckStringTerminator($ndr, $e, $l, $length); + } + if ($l->{IS_TO_NULL}) { + $self->pidl("NDR_CHECK(ndr_pull_charset_to_null($ndr, $ndr_flags, ".get_pointer_to($var_name).", $length, sizeof(" . mapTypeName($nl->{DATA_TYPE}) . "), CH_$e->{PROPERTIES}->{charset}));"); + } else { + $self->pidl("NDR_CHECK(ndr_pull_charset($ndr, $ndr_flags, ".get_pointer_to($var_name).", $length, sizeof(" . mapTypeName($nl->{DATA_TYPE}) . "), CH_$e->{PROPERTIES}->{charset}));"); + } + return; + } elsif (has_fast_array($e, $l)) { + if ($l->{IS_ZERO_TERMINATED}) { + $self->CheckStringTerminator($ndr,$e,$l,$length); + } + $self->pidl("NDR_CHECK(ndr_pull_array_$nl->{DATA_TYPE}($ndr, $ndr_flags, $var_name, $length));"); + return; + } + } elsif ($l->{TYPE} eq "POINTER") { + $self->ParsePtrPull($e, $l, $ndr, $var_name); + } elsif ($l->{TYPE} eq "DATA") { + $self->ParseDataPull($e, $l, $ndr, $var_name, $primitives, $deferred); + } elsif ($l->{TYPE} eq "TYPEDEF") { + $typefamily{$e->{DATA}->{TYPE}}->{PULL_FN_BODY}->($self, $e->{DATA}, $ndr, $var_name); + } + } + + # add additional constructions + if ($l->{TYPE} eq "POINTER" and $l->{POINTER_TYPE} eq "ignore") { + $self->pidl("/* [ignore] '$e->{NAME}' */"); + } elsif ($l->{TYPE} eq "POINTER" and $deferred) { + if ($l->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($var_name) {"); + $self->indent; + + if ($l->{POINTER_TYPE} eq "relative" or $l->{POINTER_TYPE} eq "relative_short") { + $self->pidl("uint32_t _relative_save_offset;"); + $self->pidl("_relative_save_offset = $ndr->offset;"); + $self->pidl("NDR_CHECK(ndr_pull_relative_ptr2($ndr, $var_name));"); + } + } + + $self->ParseMemCtxPullStart($e, $l, $ndr, $var_name); + + $var_name = get_value_of($var_name); + $self->ParseElementPullLevel($e, GetNextLevel($e,$l), $ndr, $var_name, $env, 1, 1); + + $self->ParseMemCtxPullEnd($e, $l, $ndr); + + if ($l->{POINTER_TYPE} ne "ref") { + if ($l->{POINTER_TYPE} eq "relative" or $l->{POINTER_TYPE} eq "relative_short") { + $self->pidl("if ($ndr->offset > $ndr->relative_highest_offset) {"); + $self->indent; + $self->pidl("$ndr->relative_highest_offset = $ndr->offset;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("$ndr->offset = _relative_save_offset;"); + } + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "ARRAY" and + not has_fast_array($e,$l) and not is_charset_array($e, $l)) { + my $length = $array_length; + my $counter = "cntr_$e->{NAME}_$l->{LEVEL_INDEX}"; + my $array_name = $var_name; + + if (not defined($length)) { + $length = $self->ParseArrayPullGetLength($e, $l, $ndr, $var_name, $env); + } + + $var_name = get_array_element($var_name, $counter); + + $self->ParseMemCtxPullStart($e, $l, $ndr, $array_name); + + if (($primitives and not $l->{IS_DEFERRED}) or ($deferred and $l->{IS_DEFERRED})) { + my $nl = GetNextLevel($e,$l); + + if ($l->{IS_ZERO_TERMINATED}) { + $self->CheckStringTerminator($ndr,$e,$l,$length); + } + + $self->pidl("for ($counter = 0; $counter < ($length); $counter++) {"); + $self->indent; + $self->ParseElementPullLevel($e, $nl, $ndr, $var_name, $env, 1, 0); + $self->deindent; + $self->pidl("}"); + } + + if ($deferred and ContainsDeferred($e, $l)) { + $self->pidl("for ($counter = 0; $counter < ($length); $counter++) {"); + $self->defer("for ($counter = 0; $counter < ($length); $counter++) {"); + $self->defer_indent; + $self->indent; + $self->ParseElementPullLevel($e,GetNextLevel($e,$l), $ndr, $var_name, $env, 0, 1); + $self->deindent; + $self->defer_deindent; + $self->pidl("}"); + $self->defer("}"); + } + + $self->ParseMemCtxPullEnd($e, $l, $ndr); + + } elsif ($l->{TYPE} eq "SWITCH") { + my $nl = GetNextLevel($e,$l); + my $needs_deferred_switch = is_deferred_switch_non_empty($nl); + + # Avoid setting a switch value if it will not be + # consumed again in the NDR_BUFFERS pull + if ($needs_deferred_switch or !$deferred) { + $self->ParseSwitchPull($e, $l, $ndr, $var_name, $env); + } + $self->ParseElementPullLevel($e, $nl, $ndr, $var_name, $env, $primitives, $deferred); + } +} + +##################################################################### +# parse scalars in a structure element - pull size +sub ParseElementPull($$$$$$) +{ + my($self,$e,$ndr,$env,$primitives,$deferred) = @_; + + my $var_name = $env->{$e->{NAME}}; + my $represent_name; + my $transmit_name; + + return if ContainsPipe($e, $e->{LEVELS}[0]); + + return unless $primitives or ($deferred and ContainsDeferred($e, $e->{LEVELS}[0])); + + if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { + $self->pidl("{"); + $self->indent; + $represent_name = $var_name; + $transmit_name = "_transmit_$e->{NAME}"; + $var_name = $transmit_name; + $self->pidl(mapTypeName($e->{TYPE})." $var_name;"); + } + + $var_name = append_prefix($e, $var_name); + + $self->start_flags($e, $ndr); + + $self->ParseElementPullLevel($e,$e->{LEVELS}[0],$ndr,$var_name,$env,$primitives,$deferred); + + $self->end_flags($e, $ndr); + + # Representation type is different from transmit_as + if ($e->{REPRESENTATION_TYPE} ne $e->{TYPE}) { + $self->pidl("NDR_CHECK(ndr_$e->{TYPE}_to_$e->{REPRESENTATION_TYPE}($transmit_name, ".get_pointer_to($represent_name)."));"); + $self->deindent; + $self->pidl("}"); + } +} + +##################################################################### +# parse a pointer in a struct element or function +sub ParsePtrPull($$$$$) +{ + my($self, $e,$l,$ndr,$var_name) = @_; + + my $nl = GetNextLevel($e, $l); + my $next_is_array = ($nl->{TYPE} eq "ARRAY"); + my $next_is_string = (($nl->{TYPE} eq "DATA") and + (Parse::Pidl::Typelist::is_string_type($nl->{DATA_TYPE}))); + + if ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP") { + + if (!$next_is_array and !$next_is_string) { + $self->pidl("if ($ndr->flags & LIBNDR_FLAG_REF_ALLOC) {"); + $self->pidl("\tNDR_PULL_ALLOC($ndr, $var_name);"); + $self->pidl("}"); + } + + return; + } elsif ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "EMBEDDED") { + $self->pidl("NDR_CHECK(ndr_pull_ref_ptr($ndr, &_ptr_$e->{NAME}));"); + } elsif (($l->{POINTER_TYPE} eq "unique") or + ($l->{POINTER_TYPE} eq "relative") or + ($l->{POINTER_TYPE} eq "full")) { + $self->pidl("NDR_CHECK(ndr_pull_generic_ptr($ndr, &_ptr_$e->{NAME}));"); + } elsif ($l->{POINTER_TYPE} eq "relative_short") { + $self->pidl("NDR_CHECK(ndr_pull_relative_ptr_short($ndr, &_ptr_$e->{NAME}));"); + } elsif ($l->{POINTER_TYPE} eq "ignore") { + #We want to consume the pointer bytes, but ignore the pointer value + $self->pidl("NDR_CHECK(ndr_pull_uint3264(ndr, NDR_SCALARS, &_ptr_$e->{NAME}));"); + $self->pidl("_ptr_$e->{NAME} = 0;"); + } else { + die("Unhandled pointer type $l->{POINTER_TYPE}"); + } + + $self->pidl("if (_ptr_$e->{NAME}) {"); + $self->indent; + + if ($l->{POINTER_TYPE} eq "ignore") { + # Don't do anything, we don't want to do the + # allocation, as we forced it to NULL just above, and + # we may not know the declared type anyway. + } else { + # Don't do this for arrays, they're allocated at the actual level + # of the array + unless ($next_is_array or $next_is_string) { + $self->pidl("NDR_PULL_ALLOC($ndr, $var_name);"); + } else { + # FIXME: Yes, this is nasty. + # We allocate an array twice + # - once just to indicate that it's there, + # - then the real allocation... + $self->pidl("NDR_PULL_ALLOC($ndr, $var_name);"); + } + } + + #$self->pidl("memset($var_name, 0, sizeof($var_name));"); + if ($l->{POINTER_TYPE} eq "relative" or $l->{POINTER_TYPE} eq "relative_short") { + $self->pidl("NDR_CHECK(ndr_pull_relative_ptr1($ndr, $var_name, _ptr_$e->{NAME}));"); + } + $self->deindent; + $self->pidl("} else {"); + $self->pidl("\t$var_name = NULL;"); + $self->pidl("}"); +} + +sub CheckRefPtrs($$$$) +{ + my ($self,$e,$ndr,$env) = @_; + + return if ContainsPipe($e, $e->{LEVELS}[0]); + return if ($e->{LEVELS}[0]->{TYPE} ne "POINTER"); + return if ($e->{LEVELS}[0]->{POINTER_TYPE} ne "ref"); + + my $var_name = $env->{$e->{NAME}}; + $var_name = append_prefix($e, $var_name); + + $self->pidl("if ($var_name == NULL) {"); + $self->indent; + $self->pidl("return ndr_push_error($ndr, NDR_ERR_INVALID_POINTER, \"NULL [ref] pointer\");"); + $self->deindent; + $self->pidl("}"); +} + +sub ParseStructPushPrimitives($$$$$) +{ + my ($self, $struct, $ndr, $varname, $env) = @_; + + $self->CheckRefPtrs($_, $ndr, $env) foreach (@{$struct->{ELEMENTS}}); + + # see if the structure contains a conformant array. If it + # does, then it must be the last element of the structure, and + # we need to push the conformant length early, as it fits on + # the wire before the structure (and even before the structure + # alignment) + if (defined($struct->{SURROUNDING_ELEMENT})) { + my $e = $struct->{SURROUNDING_ELEMENT}; + + if (defined($e->{LEVELS}[0]) and + $e->{LEVELS}[0]->{TYPE} eq "ARRAY") { + my $size; + + if ($e->{LEVELS}[0]->{IS_ZERO_TERMINATED}) { + if (has_property($e, "charset")) { + $size = "ndr_charset_length($varname->$e->{NAME}, CH_$e->{PROPERTIES}->{charset})"; + } else { + $size = "ndr_string_length($varname->$e->{NAME}, sizeof(*$varname->$e->{NAME}))"; + } + if (defined($e->{LEVELS}[0]->{SIZE_IS})) { + $size = ParseExpr($e->{LEVELS}[0]->{SIZE_IS}, $env, $e->{ORIGINAL}); + } + } else { + $size = ParseExpr($e->{LEVELS}[0]->{SIZE_IS}, $env, $e->{ORIGINAL}); + } + + $self->pidl("NDR_CHECK(ndr_push_uint3264($ndr, NDR_SCALARS, $size));"); + } else { + $self->pidl("NDR_CHECK(ndr_push_uint3264($ndr, NDR_SCALARS, ndr_string_array_size($ndr, $varname->$e->{NAME})));"); + } + } + + $self->pidl("NDR_CHECK(ndr_push_align($ndr, $struct->{ALIGN}));"); + + if (defined($struct->{PROPERTIES}{relative_base})) { + # set the current offset as base for relative pointers + # and store it based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset1($ndr, $varname, $ndr->offset));"); + } + + $self->ParseElementPush($_, $ndr, $env, 1, 0) foreach (@{$struct->{ELEMENTS}}); + + $self->pidl("NDR_CHECK(ndr_push_trailer_align($ndr, $struct->{ALIGN}));"); +} + +sub ParseStructPushDeferred($$$$) +{ + my ($self, $struct, $ndr, $varname, $env) = @_; + if (defined($struct->{PROPERTIES}{relative_base})) { + # retrieve the current offset as base for relative pointers + # based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset2($ndr, $varname));"); + } + $self->ParseElementPush($_, $ndr, $env, 0, 1) foreach (@{$struct->{ELEMENTS}}); +} + +##################################################################### +# parse a struct +sub ParseStructPush($$$$) +{ + my ($self, $struct, $ndr, $varname) = @_; + + return unless defined($struct->{ELEMENTS}); + + my $env = GenerateStructEnv($struct, $varname); + + EnvSubstituteValue($env, $struct); + + $self->DeclareArrayVariablesNoZero($_, $env) foreach (@{$struct->{ELEMENTS}}); + + $self->start_flags($struct, $ndr); + + $self->pidl("NDR_PUSH_CHECK_FLAGS(ndr, ndr_flags);"); + $self->pidl("if (ndr_flags & NDR_SCALARS) {"); + $self->indent; + $self->ParseStructPushPrimitives($struct, $ndr, $varname, $env); + $self->deindent; + $self->pidl("}"); + + $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); + $self->indent; + $self->ParseStructPushDeferred($struct, $ndr, $varname, $env); + $self->deindent; + $self->pidl("}"); + + $self->end_flags($struct, $ndr); +} + +##################################################################### +# generate a push function for an enum +sub ParseEnumPush($$$$) +{ + my($self,$enum,$ndr,$varname) = @_; + my($type_fn) = $enum->{BASE_TYPE}; + + $self->start_flags($enum, $ndr); + $self->pidl("NDR_CHECK(ndr_push_enum_$type_fn($ndr, NDR_SCALARS, $varname));"); + $self->end_flags($enum, $ndr); +} + +##################################################################### +# generate a pull function for an enum +sub ParseEnumPull($$$$) +{ + my($self,$enum,$ndr,$varname) = @_; + my($type_fn) = $enum->{BASE_TYPE}; + my($type_v_decl) = mapTypeName($type_fn); + + $self->pidl("$type_v_decl v;"); + $self->start_flags($enum, $ndr); + $self->pidl("NDR_CHECK(ndr_pull_enum_$type_fn($ndr, NDR_SCALARS, &v));"); + $self->pidl("*$varname = v;"); + + $self->end_flags($enum, $ndr); +} + +##################################################################### +# generate a print function for an enum +sub ParseEnumPrint($$$$$) +{ + my($self,$enum,$ndr,$name,$varname) = @_; + + $self->pidl("const char *val = NULL;"); + $self->pidl(""); + + $self->start_flags($enum, $ndr); + + $self->pidl("switch ($varname) {"); + $self->indent; + my $els = \@{$enum->{ELEMENTS}}; + foreach my $i (0 .. $#{$els}) { + my $e = ${$els}[$i]; + chomp $e; + if ($e =~ /^(.*)=/) { + $e = $1; + } + $self->pidl("case $e: val = \"$e\"; break;"); + } + + $self->deindent; + $self->pidl("}"); + + $self->pidl("ndr_print_enum($ndr, name, \"$enum->{TYPE}\", val, $varname);"); + + $self->end_flags($enum, $ndr); +} + +sub DeclEnum($$$$) +{ + my ($e,$t,$name,$varname) = @_; + return "enum $name " . + ($t eq "pull"?"*":"") . $varname; +} + +$typefamily{ENUM} = { + DECL => \&DeclEnum, + PUSH_FN_BODY => \&ParseEnumPush, + PULL_FN_BODY => \&ParseEnumPull, + PRINT_FN_BODY => \&ParseEnumPrint, +}; + +##################################################################### +# generate a push function for a bitmap +sub ParseBitmapPush($$$$) +{ + my($self,$bitmap,$ndr,$varname) = @_; + my($type_fn) = $bitmap->{BASE_TYPE}; + + $self->start_flags($bitmap, $ndr); + + $self->pidl("NDR_CHECK(ndr_push_$type_fn($ndr, NDR_SCALARS, $varname));"); + + $self->end_flags($bitmap, $ndr); +} + +##################################################################### +# generate a pull function for an bitmap +sub ParseBitmapPull($$$$) +{ + my($self,$bitmap,$ndr,$varname) = @_; + my $type_fn = $bitmap->{BASE_TYPE}; + my($type_decl) = mapTypeName($bitmap->{BASE_TYPE}); + + $self->pidl("$type_decl v;"); + $self->start_flags($bitmap, $ndr); + $self->pidl("NDR_CHECK(ndr_pull_$type_fn($ndr, NDR_SCALARS, &v));"); + $self->pidl("*$varname = v;"); + + $self->end_flags($bitmap, $ndr); +} + +##################################################################### +# generate a print function for an bitmap +sub ParseBitmapPrintElement($$$$$$) +{ + my($self,$e,$bitmap,$ndr,$name,$varname) = @_; + my($type_decl) = mapTypeName($bitmap->{BASE_TYPE}); + my($type_fn) = $bitmap->{BASE_TYPE}; + my($flag); + + if ($e =~ /^(\w+) .*$/) { + $flag = "$1"; + } else { + die "Bitmap: \"$name\" invalid Flag: \"$e\""; + } + + $self->pidl("ndr_print_bitmap_flag($ndr, sizeof($type_decl), \"$flag\", $flag, $varname);"); +} + +##################################################################### +# generate a print function for an bitmap +sub ParseBitmapPrint($$$$$) +{ + my($self,$bitmap,$ndr,$name,$varname) = @_; + my($type_decl) = mapTypeName($bitmap->{TYPE}); + my($type_fn) = $bitmap->{BASE_TYPE}; + + $self->start_flags($bitmap, $ndr); + + $self->pidl("ndr_print_$type_fn($ndr, name, $varname);"); + + $self->pidl("$ndr->depth++;"); + foreach my $e (@{$bitmap->{ELEMENTS}}) { + $self->ParseBitmapPrintElement($e, $bitmap, $ndr, $name, $varname); + } + $self->pidl("$ndr->depth--;"); + + $self->end_flags($bitmap, $ndr); +} + +sub DeclBitmap($$$$) +{ + my ($e,$t,$name,$varname) = @_; + return mapTypeName(Parse::Pidl::Typelist::bitmap_type_fn($e)) . + ($t eq "pull"?" *":" ") . $varname; +} + +$typefamily{BITMAP} = { + DECL => \&DeclBitmap, + PUSH_FN_BODY => \&ParseBitmapPush, + PULL_FN_BODY => \&ParseBitmapPull, + PRINT_FN_BODY => \&ParseBitmapPrint, +}; + +##################################################################### +# generate a struct print function +sub ParseStructPrint($$$$$) +{ + my($self,$struct,$ndr,$name,$varname) = @_; + + return unless defined $struct->{ELEMENTS}; + + my $env = GenerateStructEnv($struct, $varname); + + $self->DeclareArrayVariables($_) foreach (@{$struct->{ELEMENTS}}); + + $self->pidl("ndr_print_struct($ndr, name, \"$name\");"); + $self->pidl("if (r == NULL) { ndr_print_null($ndr); return; }"); + + $self->start_flags($struct, $ndr); + + $self->pidl("$ndr->depth++;"); + + $self->ParseElementPrint($_, $ndr, $env->{$_->{NAME}}, $env) + foreach (@{$struct->{ELEMENTS}}); + $self->pidl("$ndr->depth--;"); + + $self->end_flags($struct, $ndr); +} + +sub DeclarePtrVariables($$) +{ + my ($self,$e) = @_; + + if (has_property($e, "skip") or has_property($e, "skip_noinit")) { + return; + } + + foreach my $l (@{$e->{LEVELS}}) { + my $size = 32; + if ($l->{TYPE} eq "POINTER" and + not ($l->{POINTER_TYPE} eq "ref" and $l->{LEVEL} eq "TOP")) { + if ($l->{POINTER_TYPE} eq "relative_short") { + $size = 16; + } + $self->pidl("uint${size}_t _ptr_$e->{NAME};"); + last; + } + } +} + +sub DeclareArrayVariables($$;$) +{ + my ($self,$e,$pull) = @_; + + if (has_property($e, "skip") or has_property($e, "skip_noinit")) { + return; + } + + foreach my $l (@{$e->{LEVELS}}) { + next if ($l->{TYPE} ne "ARRAY"); + if (defined($pull)) { + $self->pidl("uint32_t size_$e->{NAME}_$l->{LEVEL_INDEX} = 0;"); + if ($l->{IS_VARYING}) { + $self->pidl("uint32_t length_$e->{NAME}_$l->{LEVEL_INDEX} = 0;"); + } + } + next if has_fast_array($e,$l); + next if is_charset_array($e,$l); + $self->pidl("uint32_t cntr_$e->{NAME}_$l->{LEVEL_INDEX};"); + } +} + +sub DeclareArrayVariablesNoZero($$$) +{ + my ($self,$e,$env) = @_; + + if (has_property($e, "skip") or has_property($e, "skip_noinit")) { + return; + } + + foreach my $l (@{$e->{LEVELS}}) { + next if ($l->{TYPE} ne "ARRAY"); + next if has_fast_array($e,$l); + next if is_charset_array($e,$l); + my $length = ParseExpr($l->{LENGTH_IS}, $env, $e->{ORIGINAL}); + if ($length eq "0") { + warning($e->{ORIGINAL}, "pointless array cntr: 'cntr_$e->{NAME}_$l->{LEVEL_INDEX}': length=$length"); + } else { + $self->pidl("uint32_t cntr_$e->{NAME}_$l->{LEVEL_INDEX};"); + } + } +} + +sub DeclareMemCtxVariables($$) +{ + my ($self,$e) = @_; + + if (has_property($e, "skip") or has_property($e, "skip_noinit")) { + return; + } + + foreach my $l (@{$e->{LEVELS}}) { + my $mem_flags = $self->ParseMemCtxPullFlags($e, $l); + + if (($l->{TYPE} eq "POINTER") and ($l->{POINTER_TYPE} eq "ignore")) { + last; + } + + if (defined($mem_flags)) { + $self->pidl("TALLOC_CTX *_mem_save_$e->{NAME}_$l->{LEVEL_INDEX} = NULL;"); + } + } +} + +sub ParseStructPullPrimitives($$$$$) +{ + my($self,$struct,$ndr,$varname,$env) = @_; + + if (defined $struct->{SURROUNDING_ELEMENT}) { + $self->pidl("NDR_CHECK(ndr_pull_array_size($ndr, &$varname->$struct->{SURROUNDING_ELEMENT}->{NAME}));"); + } + + $self->pidl("NDR_CHECK(ndr_pull_align($ndr, $struct->{ALIGN}));"); + + if (defined($struct->{PROPERTIES}{relative_base})) { + # set the current offset as base for relative pointers + # and store it based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset1($ndr, $varname, $ndr->offset));"); + } + + $self->ParseElementPull($_, $ndr, $env, 1, 0) foreach (@{$struct->{ELEMENTS}}); + + $self->add_deferred(); + + $self->pidl("NDR_CHECK(ndr_pull_trailer_align($ndr, $struct->{ALIGN}));"); +} + +sub ParseStructPullDeferred($$$$$) +{ + my ($self,$struct,$ndr,$varname,$env) = @_; + + if (defined($struct->{PROPERTIES}{relative_base})) { + # retrieve the current offset as base for relative pointers + # based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset2($ndr, $varname));"); + } + foreach my $e (@{$struct->{ELEMENTS}}) { + $self->ParseElementPull($e, $ndr, $env, 0, 1); + } + + $self->add_deferred(); +} + +##################################################################### +# parse a struct - pull side +sub ParseStructPull($$$$) +{ + my($self,$struct,$ndr,$varname) = @_; + + return unless defined $struct->{ELEMENTS}; + + # declare any internal pointers we need + foreach my $e (@{$struct->{ELEMENTS}}) { + $self->DeclarePtrVariables($e); + $self->DeclareArrayVariables($e, "pull"); + $self->DeclareMemCtxVariables($e); + } + + $self->start_flags($struct, $ndr); + + my $env = GenerateStructEnv($struct, $varname); + + $self->pidl("NDR_PULL_CHECK_FLAGS(ndr, ndr_flags);"); + $self->pidl("if (ndr_flags & NDR_SCALARS) {"); + $self->indent; + $self->ParseStructPullPrimitives($struct,$ndr,$varname,$env); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); + $self->indent; + $self->ParseStructPullDeferred($struct,$ndr,$varname,$env); + $self->deindent; + $self->pidl("}"); + + $self->end_flags($struct, $ndr); +} + +##################################################################### +# calculate size of ndr struct +sub ParseStructNdrSize($$$$) +{ + my ($self,$t, $name, $varname) = @_; + my $sizevar; + + if (my $flags = has_property($t, "flag")) { + $self->pidl("flags |= $flags;"); + } + $self->pidl("return ndr_size_struct($varname, flags, (ndr_push_flags_fn_t)ndr_push_$name);"); +} + +sub DeclStruct($$$$) +{ + my ($e,$t,$name,$varname) = @_; + if ($t eq "base") { + return "struct $name $varname"; + } + return ($t ne "pull"?"const ":"") . "struct $name *$varname"; +} + +sub ArgsStructNdrSize($$$) +{ + my ($d, $name, $varname) = @_; + return "const struct $name *$varname, libndr_flags flags"; +} + +$typefamily{STRUCT} = { + PUSH_FN_BODY => \&ParseStructPush, + DECL => \&DeclStruct, + PULL_FN_BODY => \&ParseStructPull, + PRINT_FN_BODY => \&ParseStructPrint, + SIZE_FN_BODY => \&ParseStructNdrSize, + SIZE_FN_ARGS => \&ArgsStructNdrSize, +}; + +##################################################################### +# calculate size of ndr struct +sub ParseUnionNdrSize($$$) +{ + my ($self, $t, $name, $varname) = @_; + my $sizevar; + + if (my $flags = has_property($t, "flag")) { + $self->pidl("flags |= $flags;"); + } + + $self->pidl("return ndr_size_union($varname, flags, level, (ndr_push_flags_fn_t)ndr_push_$name);"); +} + +sub ParseUnionPushPrimitives($$$$) +{ + my ($self, $e, $ndr ,$varname) = @_; + + my $have_default = 0; + + if (defined($e->{SWITCH_TYPE})) { + if (defined($e->{ALIGN})) { + $self->pidl("NDR_CHECK(ndr_push_union_align($ndr, $e->{ALIGN}));"); + } + + $self->pidl("NDR_CHECK(ndr_push_$e->{SWITCH_TYPE}($ndr, NDR_SCALARS, level));"); + } + + if (defined($e->{ALIGN})) { + if ($e->{IS_MS_UNION}) { + $self->pidl("/* ms_union is always aligned to the largest union arm*/"); + $self->pidl("NDR_CHECK(ndr_push_align($ndr, $e->{ALIGN}));"); + } else { + $self->pidl("NDR_CHECK(ndr_push_union_align($ndr, $e->{ALIGN}));"); + } + } + + $self->pidl("switch (level) {"); + $self->indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + $self->pidl("$el->{CASE}: {"); + + if ($el->{TYPE} ne "EMPTY") { + $self->indent; + if (defined($e->{PROPERTIES}{relative_base})) { + $self->pidl("NDR_CHECK(ndr_push_align($ndr, $el->{ALIGN}));"); + # set the current offset as base for relative pointers + # and store it based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset1($ndr, $varname, $ndr->offset));"); + } + $self->DeclareArrayVariables($el); + my $el_env = {$el->{NAME} => "$varname->$el->{NAME}"}; + $self->CheckRefPtrs($el, $ndr, $el_env); + $self->ParseElementPush($el, $ndr, $el_env, 1, 0); + $self->deindent; + } + $self->pidl("break; }"); + $self->pidl(""); + } + if (! $have_default) { + $self->pidl("default:"); + $self->pidl("\treturn ndr_push_error($ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %\"PRIu32, level);"); + } + $self->deindent; + $self->pidl("}"); +} + +sub ParseUnionPushDeferred($$$$) +{ + my ($self,$e,$ndr,$varname) = @_; + + my $have_default = 0; + + if (defined($e->{PROPERTIES}{relative_base})) { + # retrieve the current offset as base for relative pointers + # based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_push_setup_relative_base_offset2($ndr, $varname));"); + } + $self->pidl("switch (level) {"); + $self->indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + + $self->pidl("$el->{CASE}:"); + if ($el->{TYPE} ne "EMPTY") { + $self->indent; + $self->ParseElementPush($el, $ndr, {$el->{NAME} => "$varname->$el->{NAME}"}, 0, 1); + $self->deindent; + } + $self->pidl("break;"); + $self->pidl(""); + } + if (! $have_default) { + $self->pidl("default:"); + $self->pidl("\treturn ndr_push_error($ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %\"PRIu32, level);"); + } + $self->deindent; + $self->pidl("}"); +} + +##################################################################### +# parse a union - push side +sub ParseUnionPush($$$$) +{ + my ($self,$e,$ndr,$varname) = @_; + my $have_default = 0; + + $self->pidl("uint32_t level;"); + $self->start_flags($e, $ndr); + + $self->pidl("NDR_PUSH_CHECK_FLAGS(ndr, ndr_flags);"); + $self->pidl("if (ndr_flags & NDR_SCALARS) {"); + $self->indent; + $self->pidl("/* This token is not used again (except perhaps below in the NDR_BUFFERS case) */"); + $self->pidl("NDR_CHECK(ndr_push_steal_switch_value($ndr, $varname, &level));"); + + $self->ParseUnionPushPrimitives($e, $ndr, $varname); + $self->deindent; + $self->pidl("}"); + if (is_deferred_switch_non_empty($e)) { + $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); + $self->indent; + # In case we had ndr_flags of NDR_SCALERS|NDR_BUFFERS + $self->pidl("if (!(ndr_flags & NDR_SCALARS)) {"); + $self->indent; + $self->pidl("/* We didn't get it above, and the token is not needed after this. */"); + $self->pidl("NDR_CHECK(ndr_push_steal_switch_value($ndr, $varname, &level));"); + $self->deindent; + $self->pidl("}"); + $self->ParseUnionPushDeferred($e, $ndr, $varname); + $self->deindent; + $self->pidl("}"); + } + $self->end_flags($e, $ndr); +} + +##################################################################### +# print a union +sub ParseUnionPrint($$$$$) +{ + my ($self,$e,$ndr,$name,$varname) = @_; + my $have_default = 0; + + $self->pidl("uint32_t level;"); + foreach my $el (@{$e->{ELEMENTS}}) { + $self->DeclareArrayVariables($el); + } + + $self->start_flags($e, $ndr); + + $self->pidl("level = ndr_print_steal_switch_value($ndr, $varname);"); + + $self->pidl("ndr_print_union($ndr, name, level, \"$name\");"); + + $self->pidl("switch (level) {"); + $self->indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + $self->pidl("$el->{CASE}:"); + if ($el->{TYPE} ne "EMPTY") { + $self->indent; + $self->ParseElementPrint($el, $ndr, "$varname->$el->{NAME}", {}); + $self->deindent; + } + $self->pidl("break;"); + $self->pidl(""); + } + if (! $have_default) { + $self->pidl("default:"); + $self->pidl("\tndr_print_bad_level($ndr, name, level);"); + } + $self->deindent; + $self->pidl("}"); + + $self->end_flags($e, $ndr); +} + +sub ParseUnionPullPrimitives($$$$$) +{ + my ($self,$e,$ndr,$varname,$switch_type) = @_; + my $have_default = 0; + + + if (defined($switch_type)) { + if (defined($e->{ALIGN})) { + $self->pidl("NDR_CHECK(ndr_pull_union_align($ndr, $e->{ALIGN}));"); + } + + my $data_type = mapTypeName($switch_type); + my $fmt = mapTypeSpecifier($data_type); + + if (!defined($fmt)) { + $data_type = "int"; + $fmt = "%d"; + } + + $self->pidl("NDR_CHECK(ndr_pull_$switch_type($ndr, NDR_SCALARS, &_level));"); + $self->pidl("if (_level != level) {"); + $self->pidl("\treturn ndr_pull_error($ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %$fmt for $varname at \%s\", ($data_type)_level, __location__);"); + $self->pidl("}"); + } + + if (defined($e->{ALIGN})) { + if ($e->{IS_MS_UNION}) { + $self->pidl("/* ms_union is always aligned to the largest union arm*/"); + $self->pidl("NDR_CHECK(ndr_pull_align($ndr, $e->{ALIGN}));"); + } else { + $self->pidl("NDR_CHECK(ndr_pull_union_align($ndr, $e->{ALIGN}));"); + } + } + + $self->pidl("switch (level) {"); + $self->indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + $self->pidl("$el->{CASE}: {"); + + if ($el->{TYPE} ne "EMPTY") { + $self->indent; + if (defined($e->{PROPERTIES}{relative_base})) { + $self->pidl("NDR_CHECK(ndr_pull_align($ndr, $el->{ALIGN}));"); + # set the current offset as base for relative pointers + # and store it based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset1($ndr, $varname, $ndr->offset));"); + } + $self->ParseElementPull($el, $ndr, {$el->{NAME} => "$varname->$el->{NAME}"}, 1, 0); + $self->deindent; + } + $self->pidl("break; }"); + $self->pidl(""); + } + if (! $have_default) { + $self->pidl("default:"); + $self->pidl("\treturn ndr_pull_error($ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %\"PRIu32\" at %s\", level, __location__);"); + } + $self->deindent; + $self->pidl("}"); +} + +sub ParseUnionPullDeferred($$$$) +{ + my ($self,$e,$ndr,$varname) = @_; + my $have_default = 0; + + $self->pidl("switch (level) {"); + $self->indent; + foreach my $el (@{$e->{ELEMENTS}}) { + if ($el->{CASE} eq "default") { + $have_default = 1; + } + + $self->pidl("$el->{CASE}:"); + if ($el->{TYPE} ne "EMPTY") { + $self->indent; + if (defined($e->{PROPERTIES}{relative_base})) { + # retrieve the current offset as base for relative pointers + # based on the toplevel struct/union + $self->pidl("NDR_CHECK(ndr_pull_setup_relative_base_offset2($ndr, $varname));"); + } + $self->ParseElementPull($el, $ndr, {$el->{NAME} => "$varname->$el->{NAME}"}, 0, 1); + $self->deindent; + } + $self->pidl("break;"); + $self->pidl(""); + } + if (! $have_default) { + $self->pidl("default:"); + $self->pidl("\treturn ndr_pull_error($ndr, NDR_ERR_BAD_SWITCH, \"Bad switch value %\"PRIu32\" at %s\", level, __location__);"); + } + $self->deindent; + $self->pidl("}"); + + +} + +##################################################################### +# parse a union - pull side +sub ParseUnionPull($$$$) +{ + my ($self,$e,$ndr,$varname) = @_; + my $switch_type = $e->{SWITCH_TYPE}; + my $needs_deferred_switch = is_deferred_switch_non_empty($e); + $self->pidl("uint32_t level;"); + if (defined($switch_type)) { + if (Parse::Pidl::Typelist::typeIs($switch_type, "ENUM")) { + $switch_type = Parse::Pidl::Typelist::enum_type_fn(getType($switch_type)->{DATA}); + } + $self->pidl(mapTypeName($switch_type) . " _level;"); + } + + my %double_cases = (); + foreach my $el (@{$e->{ELEMENTS}}) { + next if ($el->{TYPE} eq "EMPTY"); + next if ($double_cases{"$el->{NAME}"}); + $self->DeclareMemCtxVariables($el); + $self->DeclarePtrVariables($el); + $self->DeclareArrayVariables($el, "pull"); + $double_cases{"$el->{NAME}"} = 1; + } + + $self->start_flags($e, $ndr); + + $self->pidl("NDR_PULL_CHECK_FLAGS(ndr, ndr_flags);"); + $self->pidl("if (ndr_flags & NDR_SCALARS) {"); + $self->indent; + $self->pidl("/* This token is not used again (except perhaps below in the NDR_BUFFERS case) */"); + $self->pidl("NDR_CHECK(ndr_pull_steal_switch_value($ndr, $varname, &level));"); + $self->ParseUnionPullPrimitives($e,$ndr,$varname,$switch_type); + $self->deindent; + $self->pidl("}"); + if ($needs_deferred_switch) { + $self->pidl("if (ndr_flags & NDR_BUFFERS) {"); + $self->indent; + # In case we had ndr_flags of NDR_SCALERS|NDR_BUFFERS + $self->pidl("if (!(ndr_flags & NDR_SCALARS)) {"); + $self->indent; + $self->pidl("/* We didn't get it above, and the token is not needed after this. */"); + $self->pidl("NDR_CHECK(ndr_pull_steal_switch_value($ndr, $varname, &level));"); + $self->deindent; + $self->pidl("}"); + $self->ParseUnionPullDeferred($e,$ndr,$varname); + $self->deindent; + $self->pidl("}"); + } + $self->add_deferred(); + + $self->end_flags($e, $ndr); +} + +sub DeclUnion($$$$) +{ + my ($e,$t,$name,$varname) = @_; + if ($t eq "base") { + return "union $name $varname"; + } + return ($t ne "pull"?"const ":"") . "union $name *$varname"; +} + +sub ArgsUnionNdrSize($$) +{ + my ($d,$name) = @_; + return "const union $name *r, uint32_t level, libndr_flags flags"; +} + +$typefamily{UNION} = { + PUSH_FN_BODY => \&ParseUnionPush, + DECL => \&DeclUnion, + PULL_FN_BODY => \&ParseUnionPull, + PRINT_FN_BODY => \&ParseUnionPrint, + SIZE_FN_ARGS => \&ArgsUnionNdrSize, + SIZE_FN_BODY => \&ParseUnionNdrSize, +}; + +##################################################################### +# parse a typedef - push side +sub ParseTypedefPush($$$$) +{ + my($self,$e,$ndr,$varname) = @_; + + my $env; + + $env->{$e->{NAME}} = $varname; + + $self->ParseElementPushLevel($e, $e->{LEVELS}[0], $ndr, $varname, $env, 1, 1); +} + +##################################################################### +# parse a typedef - pull side +sub ParseTypedefPull($$$$) +{ + my($self,$e,$ndr,$varname) = @_; + + my $env; + + $env->{$e->{NAME}} = $varname; + + $self->ParseElementPullLevel($e, $e->{LEVELS}[0], $ndr, $varname, $env, 1, 1); +} + +##################################################################### +# parse a typedef - print side +sub ParseTypedefPrint($$$$$) +{ + my($self,$e,$ndr,$name,$varname) = @_; + + $typefamily{$e->{DATA}->{TYPE}}->{PRINT_FN_BODY}->($self, $e->{DATA}, $ndr, $name, $varname); +} + +##################################################################### +## calculate the size of a structure +sub ParseTypedefNdrSize($$$$) +{ + my($self,$t,$name,$varname) = @_; + + $typefamily{$t->{DATA}->{TYPE}}->{SIZE_FN_BODY}->($self, $t->{DATA}, $name, $varname); +} + +sub DeclTypedef($$$$) +{ + my ($e, $t, $name, $varname) = @_; + + return $typefamily{$e->{DATA}->{TYPE}}->{DECL}->($e->{DATA}, $t, $name, $varname); +} + +sub ArgsTypedefNdrSize($$$) +{ + my ($d, $name, $varname) = @_; + return $typefamily{$d->{DATA}->{TYPE}}->{SIZE_FN_ARGS}->($d->{DATA}, $name, $varname); +} + +$typefamily{TYPEDEF} = { + PUSH_FN_BODY => \&ParseTypedefPush, + DECL => \&DeclTypedef, + PULL_FN_BODY => \&ParseTypedefPull, + PRINT_FN_BODY => \&ParseTypedefPrint, + SIZE_FN_ARGS => \&ArgsTypedefNdrSize, + SIZE_FN_BODY => \&ParseTypedefNdrSize, +}; + +sub ParsePipePushChunk($$) +{ + my ($self, $t) = @_; + + my $pipe = $t; + $pipe = $t->{DATA} if ($t->{TYPE} eq "TYPEDEF"); + my $struct = $pipe->{DATA}; + + my $name = "$struct->{NAME}"; + my $ndr = "ndr"; + my $varname = "r"; + + my $args = $typefamily{$struct->{TYPE}}->{DECL}->($struct, "push", $name, $varname); + + $self->fn_declare("push", $struct, "enum ndr_err_code ndr_push_$name(struct ndr_push *$ndr, ndr_flags_type ndr_flags, $args)") or return; + + return if has_property($t, "nopush"); + + $self->pidl("{"); + $self->indent; + + $self->ParseStructPush($struct, $ndr, $varname); + $self->pidl(""); + + $self->pidl("NDR_CHECK(ndr_push_pipe_chunk_trailer(ndr, ndr_flags, $varname->count));"); + $self->pidl(""); + + $self->pidl("return NDR_ERR_SUCCESS;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParsePipePullChunk($$) +{ + my ($self, $t) = @_; + + my $pipe = $t; + $pipe = $t->{DATA} if ($t->{TYPE} eq "TYPEDEF"); + my $struct = $pipe->{DATA}; + + my $name = "$struct->{NAME}"; + my $ndr = "ndr"; + my $varname = "r"; + + my $args = $typefamily{$struct->{TYPE}}->{DECL}->($struct, "pull", $name, $varname); + + $self->fn_declare("pull", $struct, "enum ndr_err_code ndr_pull_$name(struct ndr_pull *$ndr, ndr_flags_type ndr_flags, $args)") or return; + + return if has_property($struct, "nopull"); + + $self->pidl("{"); + $self->indent; + + $self->ParseStructPull($struct, $ndr, $varname); + $self->pidl(""); + + $self->pidl("NDR_CHECK(ndr_check_pipe_chunk_trailer($ndr, ndr_flags, $varname->count));"); + $self->pidl(""); + + $self->pidl("return NDR_ERR_SUCCESS;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParsePipePrintChunk($$) +{ + my ($self, $t) = @_; + + my $pipe = $t; + $pipe = $t->{DATA} if ($t->{TYPE} eq "TYPEDEF"); + my $struct = $pipe->{DATA}; + + my $name = "$struct->{NAME}"; + my $ndr = "ndr"; + my $varname = "r"; + + my $args = $typefamily{$struct->{TYPE}}->{DECL}->($struct, "print", $name, $varname); + + $self->pidl_hdr("void ndr_print_$name(struct ndr_print *ndr, const char *name, $args);"); + + return if (has_property($t, "noprint")); + + $self->pidl("_PUBLIC_ void ndr_print_$name(struct ndr_print *$ndr, const char *name, $args)"); + $self->pidl("{"); + $self->indent; + $self->ParseTypePrint($struct, $ndr, $varname); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +##################################################################### +# parse a function - print side +sub ParseFunctionPrint($$) +{ + my($self, $fn) = @_; + my $ndr = "ndr"; + + $self->pidl_hdr("void ndr_print_$fn->{NAME}(struct ndr_print *$ndr, const char *name, ndr_flags_type flags, const struct $fn->{NAME} *r);"); + + return if has_property($fn, "noprint"); + + $self->pidl("_PUBLIC_ void ndr_print_$fn->{NAME}(struct ndr_print *$ndr, const char *name, ndr_flags_type flags, const struct $fn->{NAME} *r)"); + $self->pidl("{"); + $self->indent; + + foreach my $e (@{$fn->{ELEMENTS}}) { + $self->DeclareArrayVariables($e); + } + + $self->pidl("ndr_print_struct($ndr, name, \"$fn->{NAME}\");"); + $self->pidl("if (r == NULL) { ndr_print_null($ndr); return; }"); + $self->pidl("$ndr->depth++;"); + + $self->pidl("if (flags & NDR_SET_VALUES) {"); + $self->pidl("\t$ndr->flags |= LIBNDR_PRINT_SET_VALUES;"); + $self->pidl("}"); + + $self->pidl("if (flags & NDR_IN) {"); + $self->indent; + $self->pidl("ndr_print_struct($ndr, \"in\", \"$fn->{NAME}\");"); + $self->pidl("$ndr->depth++;"); + + my $env = GenerateFunctionInEnv($fn); + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/in/,@{$e->{DIRECTION}})) { + $self->ParseElementPrint($e, $ndr, $env->{$e->{NAME}}, $env); + } + } + $self->pidl("$ndr->depth--;"); + $self->deindent; + $self->pidl("}"); + + $self->pidl("if (flags & NDR_OUT) {"); + $self->indent; + $self->pidl("ndr_print_struct($ndr, \"out\", \"$fn->{NAME}\");"); + $self->pidl("$ndr->depth++;"); + + $env = GenerateFunctionOutEnv($fn); + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/out/,@{$e->{DIRECTION}})) { + $self->ParseElementPrint($e, $ndr, $env->{$e->{NAME}}, $env); + } + } + if ($fn->{RETURN_TYPE}) { + $self->pidl("ndr_print_$fn->{RETURN_TYPE}($ndr, \"result\", r->out.result);"); + } + $self->pidl("$ndr->depth--;"); + $self->deindent; + $self->pidl("}"); + + $self->pidl("$ndr->depth--;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +##################################################################### +# parse a function +sub ParseFunctionPush($$) +{ + my($self, $fn) = @_; + my $ndr = "ndr"; + + $self->fn_declare("push", $fn, "enum ndr_err_code ndr_push_$fn->{NAME}(struct ndr_push *$ndr, ndr_flags_type flags, const struct $fn->{NAME} *r)") or return; + + return if has_property($fn, "nopush"); + + $self->pidl("{"); + $self->indent; + + foreach my $e (@{$fn->{ELEMENTS}}) { + $self->DeclareArrayVariables($e); + } + + $self->pidl("NDR_PUSH_CHECK_FN_FLAGS(ndr, flags);"); + + $self->pidl("if (flags & NDR_IN) {"); + $self->indent; + + my $env = GenerateFunctionInEnv($fn); + + EnvSubstituteValue($env, $fn); + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/in/,@{$e->{DIRECTION}})) { + $self->CheckRefPtrs($e, $ndr, $env); + } + } + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/in/,@{$e->{DIRECTION}})) { + $self->ParseElementPush($e, $ndr, $env, 1, 1); + } + } + + $self->deindent; + $self->pidl("}"); + + $self->pidl("if (flags & NDR_OUT) {"); + $self->indent; + + $env = GenerateFunctionOutEnv($fn); + EnvSubstituteValue($env, $fn); + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/out/,@{$e->{DIRECTION}})) { + $self->CheckRefPtrs($e, $ndr, $env); + } + } + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/out/,@{$e->{DIRECTION}})) { + $self->ParseElementPush($e, $ndr, $env, 1, 1); + } + } + + if ($fn->{RETURN_TYPE}) { + $self->pidl("NDR_CHECK(ndr_push_$fn->{RETURN_TYPE}($ndr, NDR_SCALARS, r->out.result));"); + } + + $self->deindent; + $self->pidl("}"); + $self->pidl("return NDR_ERR_SUCCESS;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub AllocateArrayLevel($$$$$$) +{ + my ($self,$e,$l,$ndr,$var,$size) = @_; + + my $pl = GetPrevLevel($e, $l); + if (defined($pl) and + $pl->{TYPE} eq "POINTER" and + $pl->{POINTER_TYPE} eq "ref" + and not $l->{IS_ZERO_TERMINATED}) { + $self->pidl("if ($ndr->flags & LIBNDR_FLAG_REF_ALLOC) {"); + $self->pidl("\tNDR_PULL_ALLOC_N($ndr, $var, $size);"); + $self->pidl("}"); + if (grep(/in/,@{$e->{DIRECTION}}) and + grep(/out/,@{$e->{DIRECTION}})) { + $self->pidl("memcpy(r->out.$e->{NAME}, r->in.$e->{NAME}, ($size) * sizeof(*r->in.$e->{NAME}));"); + } + return; + } + + $self->pidl("NDR_PULL_ALLOC_N($ndr, $var, $size);"); +} + +##################################################################### +# parse a function +sub ParseFunctionPull($$) +{ + my($self,$fn) = @_; + my $ndr = "ndr"; + + # pull function args + $self->fn_declare("pull", $fn, "enum ndr_err_code ndr_pull_$fn->{NAME}(struct ndr_pull *$ndr, ndr_flags_type flags, struct $fn->{NAME} *r)") or return; + + $self->pidl("{"); + $self->indent; + + # declare any internal pointers we need + foreach my $e (@{$fn->{ELEMENTS}}) { + $self->DeclarePtrVariables($e); + $self->DeclareArrayVariables($e, "pull"); + } + + my %double_cases = (); + foreach my $e (@{$fn->{ELEMENTS}}) { + next if ($e->{TYPE} eq "EMPTY"); + next if ($double_cases{"$e->{NAME}"}); + $self->DeclareMemCtxVariables($e); + $double_cases{"$e->{NAME}"} = 1; + } + + $self->pidl("NDR_PULL_CHECK_FN_FLAGS(ndr, flags);"); + + $self->pidl("if (flags & NDR_IN) {"); + $self->indent; + + # auto-init the out section of a structure. I originally argued that + # this was a bad idea as it hides bugs, but coping correctly + # with initialisation and not wiping ref vars is turning + # out to be too tricky (tridge) + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless grep(/out/, @{$e->{DIRECTION}}); + $self->pidl("NDR_ZERO_STRUCT(r->out);"); + $self->pidl(""); + last; + } + + my $env = GenerateFunctionInEnv($fn); + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/, @{$e->{DIRECTION}})); + $self->ParseElementPull($e, $ndr, $env, 1, 1); + } + + # allocate the "simple" out ref variables. FIXME: Shouldn't this have it's + # own flag rather than be in NDR_IN ? + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/out/, @{$e->{DIRECTION}})); + next unless ($e->{LEVELS}[0]->{TYPE} eq "POINTER" and + $e->{LEVELS}[0]->{POINTER_TYPE} eq "ref"); + next if (($e->{LEVELS}[1]->{TYPE} eq "DATA") and + (Parse::Pidl::Typelist::is_string_type($e->{LEVELS}[1]->{DATA_TYPE}))); + next if ($e->{LEVELS}[1]->{TYPE} eq "PIPE"); + next if (($e->{LEVELS}[1]->{TYPE} eq "ARRAY") + and $e->{LEVELS}[1]->{IS_ZERO_TERMINATED}); + + if ($e->{LEVELS}[1]->{TYPE} eq "ARRAY") { + my $size = ParseExprExt($e->{LEVELS}[1]->{SIZE_IS}, $env, $e->{ORIGINAL}, + check_null_pointer($e, $env, sub { $self->pidl(shift); }, + "return ndr_pull_error($ndr, NDR_ERR_INVALID_POINTER, \"NULL Pointer for size_is()\");"), + check_fully_dereferenced($e, $env)); + $self->pidl("NDR_PULL_ALLOC_N($ndr, r->out.$e->{NAME}, $size);"); + + if (grep(/in/, @{$e->{DIRECTION}})) { + $self->pidl("memcpy(r->out.$e->{NAME}, r->in.$e->{NAME}, ($size) * sizeof(*r->in.$e->{NAME}));"); + } else { + $self->pidl("memset(r->out.$e->{NAME}, 0, ($size) * sizeof(*r->out.$e->{NAME}));"); + } + } elsif ($e->{LEVELS}[1]->{TYPE} eq "ARRAY") { + if (grep(/in/, @{$e->{DIRECTION}})) { + $self->pidl("r->out.$e->{NAME} = r->in.$e->{NAME};"); + } else { + $self->pidl("r->out.$e->{NAME} = NULL;"); + } + } else { + $self->pidl("NDR_PULL_ALLOC($ndr, r->out.$e->{NAME});"); + + if (grep(/in/, @{$e->{DIRECTION}})) { + $self->pidl("*r->out.$e->{NAME} = *r->in.$e->{NAME};"); + } else { + $self->pidl("NDR_ZERO_STRUCTP(r->out.$e->{NAME});"); + } + } + } + + $self->add_deferred(); + $self->deindent; + $self->pidl("}"); + + $self->pidl("if (flags & NDR_OUT) {"); + $self->indent; + + $self->pidl("#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"); + + # This is for fuzzers of ndr_pull where the out elements refer to + # in elements in size_is or length_is. + # + # Not actually very harmful but also not useful outside a fuzzer + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/, @{$e->{DIRECTION}})); + next unless ($e->{LEVELS}[0]->{TYPE} eq "POINTER" and + $e->{LEVELS}[0]->{POINTER_TYPE} eq "ref"); + next if (($e->{LEVELS}[1]->{TYPE} eq "DATA") and + (Parse::Pidl::Typelist::is_string_type($e->{LEVELS}[1]->{DATA_TYPE}))); + next if ($e->{LEVELS}[1]->{TYPE} eq "PIPE"); + next if ($e->{LEVELS}[1]->{TYPE} eq "ARRAY"); + + $self->pidl("if (r->in.$e->{NAME} == NULL) {"); + $self->indent; + $self->pidl("NDR_PULL_ALLOC($ndr, r->in.$e->{NAME});"); + $self->pidl("NDR_ZERO_STRUCTP(r->in.$e->{NAME});"); + $self->deindent; + $self->pidl("}"); + } + + $self->pidl("#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */"); + + $env = GenerateFunctionOutEnv($fn); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless grep(/out/, @{$e->{DIRECTION}}); + $self->ParseElementPull($e, $ndr, $env, 1, 1); + } + + if ($fn->{RETURN_TYPE}) { + $self->pidl("NDR_CHECK(ndr_pull_$fn->{RETURN_TYPE}($ndr, NDR_SCALARS, &r->out.result));"); + } + + $self->add_deferred(); + $self->deindent; + $self->pidl("}"); + + $self->pidl("return NDR_ERR_SUCCESS;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub AuthServiceStruct($$$) +{ + my ($self, $ifacename, $authservice) = @_; + my @a = split /,/, $authservice; + my $authservice_count = $#a + 1; + + $self->pidl("static const char * const $ifacename\_authservice_strings[] = {"); + foreach my $ap (@a) { + $self->pidl("\t$ap, "); + } + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static const struct ndr_interface_string_array $ifacename\_authservices = {"); + $self->pidl("\t.count\t= $authservice_count,"); + $self->pidl("\t.names\t= $ifacename\_authservice_strings"); + $self->pidl("};"); + $self->pidl(""); +} + +sub ParseGeneratePipeArray($$$) +{ + my ($self, $fn, $direction) = @_; + + $self->pidl("static const struct ndr_interface_call_pipe $fn->{NAME}\_$direction\_pipes[] = {"); + $self->indent; + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless ContainsPipe($e, $e->{LEVELS}[0]); + next unless (grep(/$direction/, @{$e->{DIRECTION}})); + + my $cname = "$e->{TYPE}_chunk"; + + $self->pidl("{"); + $self->indent; + $self->pidl("\"$direction.$e->{NAME}\","); + $self->pidl("\"$cname\","); + $self->pidl("sizeof(struct $cname),"); + $self->pidl("(ndr_push_flags_fn_t) ndr_push_$cname,"); + $self->pidl("(ndr_pull_flags_fn_t) ndr_pull_$cname,"); + $self->pidl("(ndr_print_fn_t) ndr_print_$cname,"); + $self->deindent; + $self->pidl("},"); + } + $self->pidl("{ .name = NULL }"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); +} + +sub FunctionCallPipes($$) +{ + my ($self, $d) = @_; + return if not defined($d->{OPNUM}); + + my $in_pipes = 0; + my $out_pipes = 0; + + foreach my $e (@{$d->{ELEMENTS}}) { + next unless ContainsPipe($e, $e->{LEVELS}[0]); + + if (grep(/in/, @{$e->{DIRECTION}})) { + $in_pipes++; + } + if (grep(/out/, @{$e->{DIRECTION}})) { + $out_pipes++; + } + } + + if ($in_pipes) { + $self->ParseGeneratePipeArray($d, "in"); + } + + if ($out_pipes) { + $self->ParseGeneratePipeArray($d, "out"); + } +} + +sub FunctionCallEntry($$) +{ + my ($self, $d) = @_; + return 0 if not defined($d->{OPNUM}); + + my $in_pipes = 0; + my $out_pipes = 0; + + foreach my $e (@{$d->{ELEMENTS}}) { + next unless ContainsPipe($e, $e->{LEVELS}[0]); + + if (grep(/in/, @{$e->{DIRECTION}})) { + $in_pipes++; + } + if (grep(/out/, @{$e->{DIRECTION}})) { + $out_pipes++; + } + } + + my $in_pipes_ptr = "NULL"; + my $out_pipes_ptr = "NULL"; + + if ($in_pipes) { + $in_pipes_ptr = "$d->{NAME}_in_pipes"; + } + + if ($out_pipes) { + $out_pipes_ptr = "$d->{NAME}_out_pipes"; + } + + $self->pidl("\t{"); + $self->pidl("\t\t\"$d->{NAME}\","); + $self->pidl("\t\tsizeof(struct $d->{NAME}),"); + $self->pidl("\t\t(ndr_push_flags_fn_t) ndr_push_$d->{NAME},"); + $self->pidl("\t\t(ndr_pull_flags_fn_t) ndr_pull_$d->{NAME},"); + $self->pidl("\t\t(ndr_print_function_t) ndr_print_$d->{NAME},"); + $self->pidl("\t\t{ $in_pipes, $in_pipes_ptr },"); + $self->pidl("\t\t{ $out_pipes, $out_pipes_ptr },"); + $self->pidl("\t},"); + return 1; +} + +sub StructEntry($$) +{ + my ($self, $d) = @_; + my $type_decl = $typefamily{$d->{TYPE}}->{DECL}->($d, "base", $d->{NAME}, ""); + + $self->pidl("\t{"); + $self->pidl("\t\t.name = \"$d->{NAME}\","); + $self->pidl("\t\t.struct_size = sizeof($type_decl),"); + $self->pidl("\t\t.ndr_push = (ndr_push_flags_fn_t) ndr_push_$d->{NAME},"); + $self->pidl("\t\t.ndr_pull = (ndr_pull_flags_fn_t) ndr_pull_$d->{NAME},"); + $self->pidl("\t\t.ndr_print = (ndr_print_function_t) ndr_print_flags_$d->{NAME},"); + $self->pidl("\t},"); + return 1; +} + +##################################################################### +# produce a function call table +sub FunctionTable($$) +{ + my($self,$interface) = @_; + my $count = 0; + my $count_public_structs = 0; + my $uname = uc $interface->{NAME}; + + foreach my $d (@{$interface->{TYPES}}) { + next unless (is_public_struct($d)); + $count_public_structs += 1; + } + return if ($#{$interface->{FUNCTIONS}}+1 == 0 and + $count_public_structs == 0); + + foreach my $d (@{$interface->{INHERITED_FUNCTIONS}},@{$interface->{FUNCTIONS}}) { + $self->FunctionCallPipes($d); + } + + $self->pidl("static const struct ndr_interface_public_struct $interface->{NAME}\_public_structs[] = {"); + + foreach my $d (@{$interface->{TYPES}}) { + next unless (is_public_struct($d)); + $self->StructEntry($d); + } + $self->pidl("\t{ .name = NULL }"); + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static const struct ndr_interface_call $interface->{NAME}\_calls[] = {"); + + foreach my $d (@{$interface->{INHERITED_FUNCTIONS}},@{$interface->{FUNCTIONS}}) { + $count += $self->FunctionCallEntry($d); + } + $self->pidl("\t{ .name = NULL }"); + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static const char * const $interface->{NAME}\_endpoint_strings[] = {"); + foreach my $ep (@{$interface->{ENDPOINTS}}) { + $self->pidl("\t$ep, "); + } + my $endpoint_count = $#{$interface->{ENDPOINTS}}+1; + + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static const struct ndr_interface_string_array $interface->{NAME}\_endpoints = {"); + $self->pidl("\t.count\t= $endpoint_count,"); + $self->pidl("\t.names\t= $interface->{NAME}\_endpoint_strings"); + $self->pidl("};"); + $self->pidl(""); + + if (! defined $interface->{PROPERTIES}->{authservice}) { + $interface->{PROPERTIES}->{authservice} = "\"host\""; + } + + $self->AuthServiceStruct($interface->{NAME}, + $interface->{PROPERTIES}->{authservice}); + + $self->pidl("\nconst struct ndr_interface_table ndr_table_$interface->{NAME} = {"); + $self->pidl("\t.name\t\t= \"$interface->{NAME}\","); + if (defined $interface->{PROPERTIES}->{uuid}) { + $self->pidl("\t.syntax_id\t= {"); + $self->pidl("\t\t" . print_uuid($interface->{UUID}) .","); + $self->pidl("\t\tNDR_$uname\_VERSION"); + $self->pidl("\t},"); + $self->pidl("\t.helpstring\t= NDR_$uname\_HELPSTRING,"); + } + $self->pidl("\t.num_calls\t= $count,"); + $self->pidl("\t.calls\t\t= $interface->{NAME}\_calls,"); + $self->pidl("\t.num_public_structs\t= $count_public_structs,"); + $self->pidl("\t.public_structs\t\t= $interface->{NAME}\_public_structs,"); + $self->pidl("\t.endpoints\t= &$interface->{NAME}\_endpoints,"); + $self->pidl("\t.authservices\t= &$interface->{NAME}\_authservices"); + $self->pidl("};"); + $self->pidl(""); + +} + +##################################################################### +# generate include statements for imported idl files +sub HeaderImport +{ + my $self = shift; + my @imports = @_; + foreach (@imports) { + $_ = unmake_str($_); + s/\.idl$//; + $self->pidl(choose_header("librpc/gen_ndr/ndr_$_\.h", "gen_ndr/ndr_$_.h")); + } +} + +##################################################################### +# generate include statements for included header files +sub HeaderInclude +{ + my $self = shift; + my @includes = @_; + foreach (@includes) { + $self->pidl_hdr("#include $_"); + } +} + +##################################################################### +# generate prototypes and defines for the interface definitions +# FIXME: these prototypes are for the DCE/RPC client functions, not the +# NDR parser and so do not belong here, technically speaking +sub HeaderInterface($$$) +{ + my($self,$interface,$needed) = @_; + + my $count = 0; + + if ($needed->{"compression"}) { + $self->pidl(choose_header("librpc/ndr/ndr_compression.h", "ndr/compression.h")); + } + + if (has_property($interface, "object")) { + $self->pidl(choose_header("librpc/gen_ndr/ndr_orpc.h", "ndr/orpc.h")); + } + + if (defined $interface->{PROPERTIES}->{helper}) { + $self->HeaderInclude(split /,/, $interface->{PROPERTIES}->{helper}); + } + + if (defined $interface->{PROPERTIES}->{uuid}) { + my $name = uc $interface->{NAME}; + $self->pidl_hdr("#define NDR_$name\_UUID " . + Parse::Pidl::Util::make_str(lc($interface->{UUID}))); + + $self->pidl_hdr("#define NDR_$name\_VERSION $interface->{VERSION}"); + + $self->pidl_hdr("#define NDR_$name\_NAME \"$interface->{NAME}\""); + + if(!defined $interface->{PROPERTIES}->{helpstring}) { $interface->{PROPERTIES}->{helpstring} = "NULL"; } + $self->pidl_hdr("#define NDR_$name\_HELPSTRING $interface->{PROPERTIES}->{helpstring}"); + } + + my $count_public_structs = 0; + foreach my $d (@{$interface->{TYPES}}) { + next unless (has_property($d, "public")); + $count_public_structs += 1; + } + if ($#{$interface->{FUNCTIONS}}+1 > 0 or + $count_public_structs > 0) { + $self->pidl_hdr("extern const struct ndr_interface_table ndr_table_$interface->{NAME};"); + } + + foreach (@{$interface->{FUNCTIONS}}) { + next if has_property($_, "noopnum"); + next if grep(/^$_->{NAME}$/,@{$interface->{INHERITED_FUNCTIONS}}); + my $u_name = uc $_->{NAME}; + + my $val = sprintf("0x%02x", $count); + if (defined($interface->{BASE})) { + $val .= " + NDR_" . uc $interface->{BASE} . "_CALL_COUNT"; + } + + $self->pidl_hdr("#define NDR_$u_name ($val)"); + + $self->pidl_hdr(""); + $count++; + } + + my $val = $count; + + if (defined($interface->{BASE})) { + $val .= " + NDR_" . uc $interface->{BASE} . "_CALL_COUNT"; + } + + $self->pidl_hdr("#define NDR_" . uc $interface->{NAME} . "_CALL_COUNT ($val)"); + +} + +sub ParseTypePush($$$$$$) +{ + my ($self,$e, $ndr, $varname, $primitives, $deferred) = @_; + + # save the old relative_base_offset + $self->pidl("uint32_t _save_relative_base_offset = ndr_push_get_relative_base_offset($ndr);") if defined(has_property($e, "relative_base")); + $typefamily{$e->{TYPE}}->{PUSH_FN_BODY}->($self, $e, $ndr, $varname); + # restore the old relative_base_offset + $self->pidl("ndr_push_restore_relative_base_offset($ndr, _save_relative_base_offset);") if defined(has_property($e, "relative_base")); +} + +sub ParseTypePushFunction($$$) +{ + my ($self, $e, $varname) = @_; + my $ndr = "ndr"; + + my $args = $typefamily{$e->{TYPE}}->{DECL}->($e, "push", $e->{NAME}, $varname); + + $self->fn_declare("push", $e, "enum ndr_err_code ".TypeFunctionName("ndr_push", $e)."(struct ndr_push *$ndr, ndr_flags_type ndr_flags, $args)") or return; + + $self->pidl("{"); + $self->indent; + $self->ParseTypePush($e, $ndr, $varname, 1, 1); + $self->pidl("return NDR_ERR_SUCCESS;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("");; +} + +sub ParseTypePull($$$$$$) +{ + my ($self, $e, $ndr, $varname, $primitives, $deferred) = @_; + + # save the old relative_base_offset + $self->pidl("uint32_t _save_relative_base_offset = ndr_pull_get_relative_base_offset($ndr);") if defined(has_property($e, "relative_base")); + $typefamily{$e->{TYPE}}->{PULL_FN_BODY}->($self, $e, $ndr, $varname); + # restore the old relative_base_offset + $self->pidl("ndr_pull_restore_relative_base_offset($ndr, _save_relative_base_offset);") if defined(has_property($e, "relative_base")); +} + +sub ParseTypePullFunction($$) +{ + my ($self, $e, $varname) = @_; + my $ndr = "ndr"; + + my $args = $typefamily{$e->{TYPE}}->{DECL}->($e, "pull", $e->{NAME}, $varname); + + $self->fn_declare("pull", $e, "enum ndr_err_code ".TypeFunctionName("ndr_pull", $e)."(struct ndr_pull *$ndr, ndr_flags_type ndr_flags, $args)") or return; + + $self->pidl("{"); + $self->indent; + $self->ParseTypePull($e, $ndr, $varname, 1, 1); + $self->pidl("return NDR_ERR_SUCCESS;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseTypePrint($$$$) +{ + my ($self, $e, $ndr, $varname) = @_; + + $typefamily{$e->{TYPE}}->{PRINT_FN_BODY}->($self, $e, $ndr, $e->{NAME}, $varname); +} + +sub ParseTypePrintFunction($$$) +{ + my ($self, $e, $varname) = @_; + my $ndr = "ndr"; + + my $args = $typefamily{$e->{TYPE}}->{DECL}->($e, "print", $e->{NAME}, $varname); + + $self->pidl_hdr("void ".TypeFunctionName("ndr_print", $e)."(struct ndr_print *ndr, const char *name, $args);"); + + if (is_public_struct($e)) { + $self->pidl("static void ".TypeFunctionName("ndr_print_flags", $e). + "(struct ndr_print *$ndr, const char *name, ndr_flags_type unused, $args)" + ); + $self->pidl("{"); + $self->indent; + $self->pidl(TypeFunctionName("ndr_print", $e)."($ndr, name, $varname);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + } + + return if (has_property($e, "noprint")); + + $self->pidl("_PUBLIC_ void ".TypeFunctionName("ndr_print", $e)."(struct ndr_print *$ndr, const char *name, $args)"); + $self->pidl("{"); + $self->indent; + $self->ParseTypePrint($e, $ndr, $varname); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub ParseTypeNdrSize($$) +{ + my ($self,$t) = @_; + + my $varname = "r"; + my $tf = $typefamily{$t->{TYPE}}; + my $args = $tf->{SIZE_FN_ARGS}->($t, $t->{NAME}, $varname); + + $self->fn_declare("size", $t, "size_t ndr_size_$t->{NAME}($args)") or return; + + $self->pidl("{"); + $self->indent; + $typefamily{$t->{TYPE}}->{SIZE_FN_BODY}->($self,$t, $t->{NAME}, $varname); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +##################################################################### +# parse the interface definitions +sub ParseInterface($$$) +{ + my($self,$interface,$needed) = @_; + + $self->pidl_hdr("#ifndef _HEADER_NDR_$interface->{NAME}"); + $self->pidl_hdr("#define _HEADER_NDR_$interface->{NAME}"); + + $self->pidl_hdr(""); + + $self->HeaderInterface($interface, $needed); + + # Typedefs + foreach my $d (@{$interface->{TYPES}}) { + if (Parse::Pidl::Typelist::typeIs($d, "PIPE")) { + ($needed->{TypeFunctionName("ndr_push", $d)}) && + $self->ParsePipePushChunk($d); + ($needed->{TypeFunctionName("ndr_pull", $d)}) && + $self->ParsePipePullChunk($d); + ($needed->{TypeFunctionName("ndr_print", $d)}) && + $self->ParsePipePrintChunk($d); + + $needed->{TypeFunctionName("ndr_pull", $d)} = 0; + $needed->{TypeFunctionName("ndr_push", $d)} = 0; + $needed->{TypeFunctionName("ndr_print", $d)} = 0; + next; + } + + next unless(typeHasBody($d)); + + ($needed->{TypeFunctionName("ndr_push", $d)}) && $self->ParseTypePushFunction($d, "r"); + ($needed->{TypeFunctionName("ndr_pull", $d)}) && $self->ParseTypePullFunction($d, "r"); + ($needed->{TypeFunctionName("ndr_print", $d)}) && $self->ParseTypePrintFunction($d, "r"); + + # Make sure we don't generate a function twice... + $needed->{TypeFunctionName("ndr_push", $d)} = + $needed->{TypeFunctionName("ndr_pull", $d)} = + $needed->{TypeFunctionName("ndr_print", $d)} = 0; + + ($needed->{"ndr_size_$d->{NAME}"}) && $self->ParseTypeNdrSize($d); + } + + # Functions + foreach my $d (@{$interface->{FUNCTIONS}}) { + ($needed->{"ndr_push_$d->{NAME}"}) && $self->ParseFunctionPush($d); + ($needed->{"ndr_pull_$d->{NAME}"}) && $self->ParseFunctionPull($d); + ($needed->{"ndr_print_$d->{NAME}"}) && $self->ParseFunctionPrint($d); + } + + # Allow compilation of generated files where replacement functions + # for structures declared nopull/nopush have not been provided. + # + # This makes sense when only the print functions are used + # + # Otherwise the ndr_table XXX will reference these + + $self->pidl("#ifndef SKIP_NDR_TABLE_$interface->{NAME}"); + $self->FunctionTable($interface); + $self->pidl("#endif /* SKIP_NDR_TABLE_$interface->{NAME} */"); + + $self->pidl_hdr("#endif /* _HEADER_NDR_$interface->{NAME} */"); +} + +sub GenerateIncludes($) +{ + my ($self) = @_; + if (is_intree()) { + $self->pidl("#include \"includes.h\""); + } else { + $self->pidl("#ifndef _GNU_SOURCE"); + $self->pidl("#define _GNU_SOURCE"); + $self->pidl("#endif"); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + } +} + +##################################################################### +# parse a parsed IDL structure back into an IDL file +sub Parse($$$$) +{ + my($self, $ndr,$gen_header,$ndr_header) = @_; + + $self->pidl_hdr("/* header auto-generated by pidl */"); + $self->pidl_hdr(""); + $self->pidl_hdr(choose_header("librpc/ndr/libndr.h", "ndr.h")); + $self->pidl_hdr("#include \"$gen_header\"") if ($gen_header); + $self->pidl_hdr(""); + + $self->pidl("/* parser auto-generated by pidl */"); + $self->pidl(""); + $self->GenerateIncludes(); + $self->pidl("#include \"$ndr_header\"") if ($ndr_header); + $self->pidl(""); + + my %needed = (); + + foreach (@{$ndr}) { + ($_->{TYPE} eq "INTERFACE") && NeededInterface($_, \%needed); + } + + foreach (@{$ndr}) { + ($_->{TYPE} eq "INTERFACE") && $self->ParseInterface($_, \%needed); + ($_->{TYPE} eq "IMPORT") && $self->HeaderImport(@{$_->{PATHS}}); + ($_->{TYPE} eq "INCLUDE") && $self->HeaderInclude(@{$_->{PATHS}}); + } + + return ($self->{res_hdr}, $self->{res}); +} + +sub NeededElement($$$) +{ + my ($e, $dir, $needed) = @_; + + return if ($e->{TYPE} eq "EMPTY"); + + return if (ref($e->{TYPE}) eq "HASH" and + not defined($e->{TYPE}->{NAME})); + + my ($t, $rt); + if (ref($e->{TYPE}) eq "HASH") { + $t = $e->{TYPE}->{TYPE}."_".$e->{TYPE}->{NAME}; + } else { + $t = $e->{TYPE}; + } + + if (ref($e->{REPRESENTATION_TYPE}) eq "HASH") { + $rt = $e->{REPRESENTATION_TYPE}->{TYPE}."_".$e->{REPRESENTATION_TYPE}->{NAME}; + } else { + $rt = $e->{REPRESENTATION_TYPE}; + } + + die ("$e->{NAME} $t, $rt FOO") unless ($rt ne ""); + + my @fn = (); + if ($dir eq "print") { + push(@fn, TypeFunctionName("ndr_print", $e->{REPRESENTATION_TYPE})); + } elsif ($dir eq "pull") { + push (@fn, TypeFunctionName("ndr_pull", $e->{TYPE})); + push (@fn, "ndr_$t\_to_$rt") + if ($rt ne $t); + } elsif ($dir eq "push") { + push (@fn, TypeFunctionName("ndr_push", $e->{TYPE})); + push (@fn, "ndr_$rt\_to_$t") + if ($rt ne $t); + } else { + die("invalid direction `$dir'"); + } + + foreach (@fn) { + unless (defined($needed->{$_})) { + $needed->{$_} = 1; + } + } +} + +sub NeededFunction($$) +{ + my ($fn,$needed) = @_; + $needed->{"ndr_pull_$fn->{NAME}"} = 1; + $needed->{"ndr_push_$fn->{NAME}"} = 1; + $needed->{"ndr_print_$fn->{NAME}"} = 1; + foreach my $e (@{$fn->{ELEMENTS}}) { + $e->{PARENT} = $fn; + NeededElement($e, $_, $needed) foreach ("pull", "push", "print"); + } +} + +sub NeededType($$$) +{ + sub NeededType($$$); + my ($t,$needed,$req) = @_; + + NeededType($t->{DATA}, $needed, $req) if ($t->{TYPE} eq "TYPEDEF"); + NeededType($t->{DATA}, $needed, $req) if ($t->{TYPE} eq "PIPE"); + + if ($t->{TYPE} eq "STRUCT" or $t->{TYPE} eq "UNION") { + return unless defined($t->{ELEMENTS}); + for my $e (@{$t->{ELEMENTS}}) { + $e->{PARENT} = $t; + if (has_property($e, "compression")) { + $needed->{"compression"} = 1; + } + NeededElement($e, $req, $needed); + NeededType($e->{TYPE}, $needed, $req) if (ref($e->{TYPE}) eq "HASH"); + } + } +} + +##################################################################### +# work out what parse functions are needed +sub NeededInterface($$) +{ + my ($interface,$needed) = @_; + NeededFunction($_, $needed) foreach (@{$interface->{FUNCTIONS}}); + foreach (reverse @{$interface->{TYPES}}) { + + if (has_property($_, "public")) { + $needed->{TypeFunctionName("ndr_pull", $_)} = $needed->{TypeFunctionName("ndr_push", $_)} = + $needed->{TypeFunctionName("ndr_print", $_)} = 1; + } + + NeededType($_, $needed, "pull") if ($needed->{TypeFunctionName("ndr_pull", $_)}); + NeededType($_, $needed, "push") if ($needed->{TypeFunctionName("ndr_push", $_)}); + NeededType($_, $needed, "print") if ($needed->{TypeFunctionName("ndr_print", $_)}); + if (has_property($_, "gensize")) { + $needed->{"ndr_size_$_->{NAME}"} = 1; + } + } +} + +sub TypeFunctionName($$) +{ + my ($prefix, $t) = @_; + + return "$prefix\_$t->{NAME}" if (ref($t) eq "HASH" and + $t->{TYPE} eq "TYPEDEF"); + return "$prefix\_$t->{TYPE}_$t->{NAME}" if (ref($t) eq "HASH"); + return "$prefix\_$t"; +} + +1; diff --git a/pidl/lib/Parse/Pidl/Samba4/NDR/Server.pm b/pidl/lib/Parse/Pidl/Samba4/NDR/Server.pm new file mode 100644 index 0000000..7335998 --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/NDR/Server.pm @@ -0,0 +1,358 @@ +################################################### +# server boilerplate generator +# Copyright tridge@samba.org 2003 +# Copyright metze@samba.org 2004 +# released under the GNU GPL + +package Parse::Pidl::Samba4::NDR::Server; + +use strict; +use warnings; +use Parse::Pidl::Util; + +use vars qw($VERSION); +$VERSION = '0.01'; + +my($res); + +sub pidl($) +{ + $res .= shift; +} + + +##################################################### +# generate the switch statement for function dispatch +sub gen_dispatch_switch($) +{ + my $interface = shift; + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + next if not defined($fn->{OPNUM}); + + pidl "\tcase $fn->{OPNUM}: {\n"; + pidl "\t\tstruct $fn->{NAME} *r2 = (struct $fn->{NAME} *)r;\n"; + pidl "\t\tif (DEBUGLEVEL >= 10) {\n"; + pidl "\t\t\tNDR_PRINT_FUNCTION_DEBUG($fn->{NAME}, NDR_IN, r2);\n"; + pidl "\t\t}\n"; + if ($fn->{RETURN_TYPE} && $fn->{RETURN_TYPE} ne "void") { + pidl "\t\tr2->out.result = dcesrv_$fn->{NAME}(dce_call, mem_ctx, r2);\n"; + } else { + pidl "\t\tdcesrv_$fn->{NAME}(dce_call, mem_ctx, r2);\n"; + } + pidl "\t\tif (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {\n"; + pidl "\t\t\tDEBUG(5,(\"function $fn->{NAME} will reply async\\n\"));\n"; + pidl "\t\t}\n"; + pidl "\t\tbreak;\n\t}\n"; + } +} + +##################################################### +# generate the switch statement for function reply +sub gen_reply_switch($) +{ + my $interface = shift; + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + next if not defined($fn->{OPNUM}); + + pidl "\tcase $fn->{OPNUM}: {\n"; + pidl "\t\tstruct $fn->{NAME} *r2 = (struct $fn->{NAME} *)r;\n"; + pidl "\t\tif (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {\n"; + pidl "\t\t\tDEBUG(5,(\"function $fn->{NAME} replied async\\n\"));\n"; + pidl "\t\t}\n"; + pidl "\t\tif (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {\n"; + pidl "\t\t\tNDR_PRINT_FUNCTION_DEBUG($fn->{NAME}, NDR_OUT | NDR_SET_VALUES, r2);\n"; + pidl "\t\t}\n"; + pidl "\t\tif (dce_call->fault_code != 0) {\n"; + pidl "\t\t\tDEBUG(2,(\"dcerpc_fault %s in $fn->{NAME}\\n\", dcerpc_errstr(mem_ctx, dce_call->fault_code)));\n"; + pidl "\t\t}\n"; + pidl "\t\tbreak;\n\t}\n"; + } +} + +##################################################################### +# produce boilerplate code for a interface +sub Boilerplate_Iface($) +{ + my($interface) = shift; + my $name = $interface->{NAME}; + my $uname = uc $name; + my $uuid = lc($interface->{UUID}); + my $if_version = $interface->{VERSION}; + + pidl " +static NTSTATUS $name\__op_bind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface) +{ +#ifdef DCESRV_INTERFACE_$uname\_BIND + return DCESRV_INTERFACE_$uname\_BIND(context,iface); +#else + return NT_STATUS_OK; +#endif +} + +static void $name\__op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface) +{ +#ifdef DCESRV_INTERFACE_$uname\_UNBIND + DCESRV_INTERFACE_$uname\_UNBIND(context, iface); +#else + return; +#endif +} + +static NTSTATUS $name\__op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_pull *pull, void **r) +{ + enum ndr_err_code ndr_err; + uint16_t opnum = dce_call->pkt.u.request.opnum; + + dce_call->fault_code = 0; + + if (opnum >= ndr_table_$name.num_calls) { + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + return NT_STATUS_NET_WRITE_FAULT; + } + + *r = talloc_named(mem_ctx, + ndr_table_$name.calls[opnum].struct_size, + \"struct %s\", + ndr_table_$name.calls[opnum].name); + NT_STATUS_HAVE_NO_MEMORY(*r); + + /* unravel the NDR for the packet */ + ndr_err = ndr_table_$name.calls[opnum].ndr_pull(pull, NDR_IN, *r); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + dce_call->fault_code = DCERPC_FAULT_NDR; + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static NTSTATUS $name\__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16_t opnum = dce_call->pkt.u.request.opnum; + + switch (opnum) { +"; + gen_dispatch_switch($interface); + +pidl " + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static NTSTATUS $name\__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16_t opnum = dce_call->pkt.u.request.opnum; + + switch (opnum) { +"; + gen_reply_switch($interface); + +pidl " + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static NTSTATUS $name\__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, const void *r) +{ + enum ndr_err_code ndr_err; + uint16_t opnum = dce_call->pkt.u.request.opnum; + + ndr_err = ndr_table_$name.calls[opnum].ndr_push(push, NDR_OUT, r); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + dce_call->fault_code = DCERPC_FAULT_NDR; + return NT_STATUS_NET_WRITE_FAULT; + } + + return NT_STATUS_OK; +} + +static const struct dcesrv_interface dcesrv\_$name\_interface = { + .name = \"$name\", + .syntax_id = {".print_uuid($uuid).",$if_version}, + .bind = $name\__op_bind, + .unbind = $name\__op_unbind, + .ndr_pull = $name\__op_ndr_pull, + .dispatch = $name\__op_dispatch, + .reply = $name\__op_reply, + .ndr_push = $name\__op_ndr_push, + .local = NULL, +#ifdef DCESRV_INTERFACE_$uname\_FLAGS + .flags = DCESRV_INTERFACE_$uname\_FLAGS +#else + .flags = 0 +#endif +}; + +"; +} + +##################################################################### +# produce boilerplate code for an endpoint server +sub Boilerplate_Ep_Server($) +{ + my($interface) = shift; + my $name = $interface->{NAME}; + my $uname = uc $name; + + pidl " +static NTSTATUS $name\__op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server) +{ + int i; +#ifdef DCESRV_INTERFACE_$uname\_NCACN_NP_SECONDARY_ENDPOINT + const char *ncacn_np_secondary_endpoint = + DCESRV_INTERFACE_$uname\_NCACN_NP_SECONDARY_ENDPOINT; +#else + const char *ncacn_np_secondary_endpoint = NULL; +#endif + + for (i=0;icount;i++) { + NTSTATUS ret; + const char *name = ndr_table_$name.endpoints->names[i]; + + ret = dcesrv_interface_register(dce_ctx, + name, + ncacn_np_secondary_endpoint, + &dcesrv_$name\_interface, + NULL); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1,(\"$name\_op_init_server: failed to register endpoint \'%s\'\\n\",name)); + return ret; + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS $name\__op_shutdown_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server) +{ + return NT_STATUS_OK; +} + +static bool $name\__op_interface_by_uuid(struct dcesrv_interface *iface, const struct GUID *uuid, uint32_t if_version) +{ + if (dcesrv_$name\_interface.syntax_id.if_version == if_version && + GUID_equal(\&dcesrv\_$name\_interface.syntax_id.uuid, uuid)) { + memcpy(iface,&dcesrv\_$name\_interface, sizeof(*iface)); + return true; + } + + return false; +} + +static bool $name\__op_interface_by_name(struct dcesrv_interface *iface, const char *name) +{ + if (strcmp(dcesrv_$name\_interface.name, name)==0) { + memcpy(iface, &dcesrv_$name\_interface, sizeof(*iface)); + return true; + } + + return false; +} + +NTSTATUS dcerpc_server_$name\_init(TALLOC_CTX *ctx) +{ + NTSTATUS ret; + static const struct dcesrv_endpoint_server ep_server = { + /* fill in our name */ + .name = \"$name\", + + /* Initialization flag */ + .initialized = false, + + /* fill in all the operations */ +#ifdef DCESRV_INTERFACE_$uname\_INIT_SERVER + .init_server = DCESRV_INTERFACE_$uname\_INIT_SERVER, +#else + .init_server = $name\__op_init_server, +#endif +#ifdef DCESRV_INTERFACE_$uname\_SHUTDOWN_SERVER + .shutdown_server = DCESRV_INTERFACE_$uname\_SHUTDOWN_SERVER, +#else + .shutdown_server = $name\__op_shutdown_server, +#endif + .interface_by_uuid = $name\__op_interface_by_uuid, + .interface_by_name = $name\__op_interface_by_name + }; + /* register ourselves with the DCERPC subsystem. */ + ret = dcerpc_register_ep_server(&ep_server); + + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,(\"Failed to register \'$name\' endpoint server!\\n\")); + return ret; + } + + return ret; +} + +"; +} + +##################################################################### +# dcerpc server boilerplate from a parsed IDL structure +sub ParseInterface($) +{ + my($interface) = shift; + my $count = 0; + + $res .= "NTSTATUS dcerpc_server_$interface->{NAME}\_init(TALLOC_CTX *);\n"; + $res .= "\n"; + + if (!defined $interface->{PROPERTIES}->{uuid}) { + return $res; + } + + if (!defined $interface->{PROPERTIES}->{version}) { + $interface->{PROPERTIES}->{version} = "0.0"; + } + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + if (defined($fn->{OPNUM})) { $count++; } + } + + if ($count == 0) { + return $res; + } + + $res .= "/* $interface->{NAME} - dcerpc server boilerplate generated by pidl */\n\n"; + Boilerplate_Iface($interface); + Boilerplate_Ep_Server($interface); + + return $res; +} + +sub Parse($$) +{ + my($ndr,$header) = @_; + + $res = ""; + $res .= "/* server functions auto-generated by pidl */\n"; + $res .= "#include \"$header\"\n"; + $res .= "#include \n"; + $res .= "\n"; + + foreach my $x (@{$ndr}) { + ParseInterface($x) if ($x->{TYPE} eq "INTERFACE" and not defined($x->{PROPERTIES}{object})); + } + + return $res; +} + +1; diff --git a/pidl/lib/Parse/Pidl/Samba4/NDR/ServerCompat.pm b/pidl/lib/Parse/Pidl/Samba4/NDR/ServerCompat.pm new file mode 100644 index 0000000..aaa10ff --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/NDR/ServerCompat.pm @@ -0,0 +1,624 @@ +################################################### +# server boilerplate generator +# Copyright tridge@samba.org 2003 +# Copyright metze@samba.org 2004 +# Copyright scabrero@samba.org 2019 +# released under the GNU GPL + +package Parse::Pidl::Samba4::NDR::ServerCompat; + +use Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(Parse); + +use Parse::Pidl::Util qw(print_uuid has_property ParseExpr); +use Parse::Pidl::Typelist qw(mapTypeName); +use Parse::Pidl qw(error fatal); +use Parse::Pidl::NDR qw(ContainsPipe GetNextLevel); +use Parse::Pidl::Samba4 qw(ElementStars); +use Parse::Pidl::Samba4::Header qw(GenerateFunctionOutEnv); + +use vars qw($VERSION); +$VERSION = '1.0'; + +use strict; + +sub indent($) { my ($self) = @_; $self->{tabs}.="\t"; } +sub deindent($) { my ($self) = @_; $self->{tabs} = substr($self->{tabs}, 1); } +sub pidl($$) { my ($self,$txt) = @_; $self->{res} .= $txt ? "$self->{tabs}$txt\n" : "\n"; } +sub pidlnoindent($$) { my ($self,$txt) = @_; $self->{res} .= $txt ? "$txt\n" : "\n"; } +sub pidl_hdr($$) { my ($self, $txt) = @_; $self->{res_hdr} .= "$txt\n"; } +sub pidl_both($$) { my ($self, $txt) = @_; $self->{hdr} .= "$txt\n"; $self->{res_hdr} .= "$txt\n"; } + +sub new($) +{ + my ($class) = shift; + my $self = { res => "", res_hdr => "", tabs => "" }; + bless($self, $class); +} + +sub decl_level($$) +{ + my ($self, $e, $l) = @_; + my $res = ""; + + if (has_property($e, "charset")) { + $res .= "const char"; + } else { + $res .= mapTypeName($e->{TYPE}); + } + + my $stars = ElementStars($e, $l); + + $res .= " ".$stars unless ($stars eq ""); + + return $res; +} + +sub alloc_out_var($$$$$) +{ + my ($self, $e, $mem_ctx, $name, $env, $alloc_error_block) = @_; + + my $l = $e->{LEVELS}[0]; + + # we skip pointer to arrays + if ($l->{TYPE} eq "POINTER") { + my $nl = GetNextLevel($e, $l); + $l = $nl if ($nl->{TYPE} eq "ARRAY"); + } elsif + + # we don't support multi-dimensional arrays yet + ($l->{TYPE} eq "ARRAY") { + my $nl = GetNextLevel($e, $l); + if ($nl->{TYPE} eq "ARRAY") { + fatal($e->{ORIGINAL},"multi-dimensional [out] arrays are not supported!"); + } + } else { + # neither pointer nor array, no need to alloc something. + return; + } + + if ($l->{TYPE} eq "ARRAY") { + unless(defined($l->{SIZE_IS})) { + error($e->{ORIGINAL}, "No size known for array `$e->{NAME}'"); + $self->pidl("#error No size known for array `$e->{NAME}'"); + } else { + my $size = ParseExpr($l->{SIZE_IS}, $env, $e); + $self->pidl("$name = talloc_zero_array($mem_ctx, " . $self->decl_level($e, 1) . ", $size);"); + } + } else { + $self->pidl("$name = talloc_zero($mem_ctx, " . $self->decl_level($e, 1) . ");"); + } + + $self->pidl("if ($name == NULL) {"); + $self->indent(); + foreach (@{$alloc_error_block}) { + $self->pidl($_); + } + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); +} + +sub gen_fn_out($$) +{ + my ($self, $fn, $alloc_error_block) = @_; + + my $hasout = 0; + foreach (@{$fn->{ELEMENTS}}) { + if (grep(/out/, @{$_->{DIRECTION}})) { + $hasout = 1; + } + } + + if ($hasout) { + $self->pidl("NDR_ZERO_STRUCT(r2->out);"); + } + + foreach (@{$fn->{ELEMENTS}}) { + my @dir = @{$_->{DIRECTION}}; + if (grep(/in/, @dir) and grep(/out/, @dir)) { + $self->pidl("r2->out.$_->{NAME} = r2->in.$_->{NAME};"); + } + } + + foreach (@{$fn->{ELEMENTS}}) { + next if ContainsPipe($_, $_->{LEVELS}[0]); + + my @dir = @{$_->{DIRECTION}}; + + if (grep(/in/, @dir) and grep(/out/, @dir)) { + # noop + } elsif (grep(/out/, @dir) and not has_property($_, "represent_as")) { + my $env = GenerateFunctionOutEnv($fn, "r2->"); + $self->alloc_out_var($_, "r2", "r2->out.$_->{NAME}", $env, $alloc_error_block); + } + + } +} + +##################################################### +# generate the switch statement for function dispatch +sub gen_dispatch_switch($) +{ + my ($self, $interface) = @_; + + my @alloc_error_block = ("status = NT_STATUS_NO_MEMORY;", + "p->fault_state = DCERPC_FAULT_CANT_PERFORM;", + "goto fail;"); + foreach my $fn (@{$interface->{FUNCTIONS}}) { + next if not defined($fn->{OPNUM}); + + my $fname = $fn->{NAME}; + my $ufname = uc($fname); + + $self->pidl("case $fn->{OPNUM}: { /* $fn->{NAME} */"); + $self->indent(); + $self->pidl("struct $fname *r2 = (struct $fname *)r;"); + $self->pidl("if (DEBUGLEVEL >= 10) {"); + $self->indent(); + $self->pidl("NDR_PRINT_FUNCTION_DEBUG($fname, NDR_IN, r2);"); + $self->deindent(); + $self->pidl("}"); + + $self->gen_fn_out($fn, \@alloc_error_block); + + $self->pidl_hdr("struct $fname;"); + + if ($fn->{RETURN_TYPE} && $fn->{RETURN_TYPE} ne "void") { + $self->pidl_hdr(mapTypeName($fn->{RETURN_TYPE}) . " _$fname(struct pipes_struct *p, struct $fname *r);"); + $self->pidl("r2->out.result = _$fname(p, r2);"); + } else { + $self->pidl_hdr("void _$fname(struct pipes_struct *p, struct $fname *r);"); + $self->pidl("_$fname(p, r2);"); + } + + $self->pidl("break;"); + $self->deindent(); + $self->pidl("}"); + } +} + +##################################################### +# generate the switch statement for function reply +sub gen_reply_switch($) +{ + my ($self, $interface) = @_; + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + next if not defined($fn->{OPNUM}); + + $self->pidl("case $fn->{OPNUM}: { /* $fn->{NAME} */"); + $self->indent(); + $self->pidl("struct $fn->{NAME} *r2 = (struct $fn->{NAME} *)r;"); + $self->pidl("if (dce_call->state_flags & DCESRV_CALL_STATE_FLAG_ASYNC) {"); + $self->indent(); + $self->pidl("DEBUG(5,(\"function $fn->{NAME} replied async\\n\"));"); + $self->deindent(); + $self->pidl("}"); + $self->pidl("if (DEBUGLEVEL >= 10 && dce_call->fault_code == 0) {"); + $self->indent(); + $self->pidl("NDR_PRINT_FUNCTION_DEBUG($fn->{NAME}, NDR_OUT | NDR_SET_VALUES, r2);"); + $self->deindent(); + $self->pidl("}"); + $self->pidl("if (dce_call->fault_code != 0) {"); + $self->indent(); + $self->pidl("DBG_WARNING(\"dcerpc_fault %s in $fn->{NAME}\\n\", dcerpc_errstr(mem_ctx, dce_call->fault_code));"); + $self->deindent(); + $self->pidl("}"); + $self->pidl("break;"); + $self->deindent(); + $self->pidl("}"); + } +} + +##################################################################### +# produce boilerplate code for a interface +sub boilerplate_iface($) +{ + my ($self, $interface) = @_; + + my $name = $interface->{NAME}; + my $uname = uc $name; + my $uuid = lc($interface->{UUID}); + my $if_version = $interface->{VERSION}; + + $self->pidl("static NTSTATUS $name\__op_bind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface)"); + $self->pidl("{"); + $self->indent(); + $self->pidlnoindent("#ifdef DCESRV_INTERFACE_$uname\_BIND"); + $self->pidl("return DCESRV_INTERFACE_$uname\_BIND(context,iface);"); + $self->pidlnoindent("#else"); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent(); + $self->pidl("#endif"); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static void $name\__op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface)"); + $self->pidl("{"); + $self->pidlnoindent("#ifdef DCESRV_INTERFACE_$uname\_UNBIND"); + $self->indent(); + $self->pidl("DCESRV_INTERFACE_$uname\_UNBIND(context, iface);"); + $self->pidlnoindent("#else"); + $self->pidl("return;"); + $self->pidlnoindent("#endif"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl_hdr("NTSTATUS $name\__op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_pull *pull, void **r);"); + $self->pidl("NTSTATUS $name\__op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_pull *pull, void **r)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("enum ndr_err_code ndr_err;"); + $self->pidl("uint16_t opnum = dce_call->pkt.u.request.opnum;"); + $self->pidl(""); + $self->pidl("dce_call->fault_code = 0;"); + $self->pidl(""); + $self->pidl("if (opnum >= ndr_table_$name.num_calls) {"); + $self->indent(); + $self->pidl("dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;"); + $self->pidl("return NT_STATUS_NET_WRITE_FAULT;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + $self->pidl("*r = talloc_named(mem_ctx, ndr_table_$name.calls[opnum].struct_size, \"struct %s\", ndr_table_$name.calls[opnum].name);"); + $self->pidl("NT_STATUS_HAVE_NO_MEMORY(*r);"); + $self->pidl(""); + $self->pidl("/* unravel the NDR for the packet */"); + $self->pidl("ndr_err = ndr_table_$name.calls[opnum].ndr_pull(pull, NDR_IN, *r);"); + $self->pidl("if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {"); + $self->indent(); + $self->pidl("dce_call->fault_code = DCERPC_FAULT_NDR;"); + $self->pidl("return NT_STATUS_NET_WRITE_FAULT;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static NTSTATUS $name\__op_dispatch_internal(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r, enum s3compat_rpc_dispatch dispatch)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("uint16_t opnum = dce_call->pkt.u.request.opnum;"); + $self->pidl("struct pipes_struct *p = NULL;"); + $self->pidl("NTSTATUS status = NT_STATUS_OK;"); + $self->pidl("bool impersonated = false;"); + $self->pidl(""); + $self->pidl("/* Retrieve pipes struct */"); + $self->pidl("p = dcesrv_get_pipes_struct(dce_call->conn);"); + $self->pidl("p->dce_call = dce_call;"); + $self->pidl("p->mem_ctx = mem_ctx;"); + $self->pidl("/* Reset pipes struct fault state */"); + $self->pidl("p->fault_state = 0;"); + $self->pidl(""); + + $self->pidl("/* Impersonate */"); + $self->pidl("if (dispatch == S3COMPAT_RPC_DISPATCH_EXTERNAL) {"); + $self->indent(); + $self->pidl("impersonated = become_authenticated_pipe_user(dce_call->auth_state->session_info);"); + $self->pidl("if (!impersonated) {"); + $self->indent(); + $self->pidl("dce_call->fault_code = DCERPC_FAULT_ACCESS_DENIED;"); + $self->pidl("status = NT_STATUS_NET_WRITE_FAULT;"); + $self->pidl("goto fail;"); + $self->deindent(); + $self->pidl("}"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("switch (opnum) {"); + $self->gen_dispatch_switch($interface); + $self->pidl("default:"); + $self->indent(); + $self->pidl("dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;"); + $self->pidl("break;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidlnoindent("fail:"); + $self->pidl("/* Unimpersonate */"); + $self->pidl("if (impersonated) {"); + $self->indent(); + $self->pidl("unbecome_authenticated_pipe_user();"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("p->dce_call = NULL;"); + $self->pidl("p->mem_ctx = NULL;"); + $self->pidl("/* Check pipes struct fault state */"); + $self->pidl("if (p->fault_state != 0) {"); + $self->indent(); + $self->pidl("dce_call->fault_code = p->fault_state;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl("if (dce_call->fault_code != 0) {"); + $self->indent(); + $self->pidl("status = NT_STATUS_NET_WRITE_FAULT;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("return status;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl_hdr("NTSTATUS $name\__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r);"); + $self->pidl("NTSTATUS $name\__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("return $name\__op_dispatch_internal(dce_call, mem_ctx, r, S3COMPAT_RPC_DISPATCH_EXTERNAL);"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl_hdr("NTSTATUS $name\__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r);"); + $self->pidl("NTSTATUS $name\__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("uint16_t opnum = dce_call->pkt.u.request.opnum;"); + $self->pidl(""); + $self->pidl("switch (opnum) {"); + $self->gen_reply_switch($interface); + $self->pidl("default:"); + $self->indent(); + $self->pidl("dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;"); + $self->pidl("break;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("if (dce_call->fault_code != 0) {"); + $self->indent(); + $self->pidl("return NT_STATUS_NET_WRITE_FAULT;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl_hdr("NTSTATUS $name\__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, const void *r);"); + $self->pidl("NTSTATUS $name\__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, const void *r)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("enum ndr_err_code ndr_err;"); + $self->pidl("uint16_t opnum = dce_call->pkt.u.request.opnum;"); + $self->pidl(""); + $self->pidl("ndr_err = ndr_table_$name.calls[opnum].ndr_push(push, NDR_OUT, r);"); + $self->pidl("if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {"); + $self->indent(); + $self->pidl("dce_call->fault_code = DCERPC_FAULT_NDR;"); + $self->pidl("return NT_STATUS_NET_WRITE_FAULT;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + ############################## + #### LOCAL DISPATCH #### + ############################## + $self->pidl_hdr("NTSTATUS $name\__op_local(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r);"); + $self->pidl("NTSTATUS $name\__op_local(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("return $name\__op_dispatch_internal(dce_call, mem_ctx, r, S3COMPAT_RPC_DISPATCH_INTERNAL);"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static const struct dcesrv_interface dcesrv\_$name\_interface = {"); + $self->indent(); + $self->pidl(".name = \"$name\","); + $self->pidl(".syntax_id = {".print_uuid($uuid).",$if_version},"); + $self->pidl(".bind = $name\__op_bind,"); + $self->pidl(".unbind = $name\__op_unbind,"); + $self->pidl(".ndr_pull = $name\__op_ndr_pull,"); + $self->pidl(".dispatch = $name\__op_dispatch,"); + $self->pidl(".reply = $name\__op_reply,"); + $self->pidl(".ndr_push = $name\__op_ndr_push,"); + $self->pidl(".local = $name\__op_local,"); + $self->pidlnoindent("#ifdef DCESRV_INTERFACE_$uname\_FLAGS"); + $self->pidl(".flags = DCESRV_INTERFACE_$uname\_FLAGS"); + $self->pidlnoindent("#else"); + $self->pidl(".flags = 0"); + $self->pidlnoindent("#endif"); + $self->deindent(); + $self->pidl("};"); + $self->pidl(""); +} + +##################################################################### +# produce boilerplate code for an endpoint server +sub boilerplate_ep_server($) +{ + my ($self, $interface) = @_; + my $name = $interface->{NAME}; + my $uname = uc $name; + + $self->pidl("static NTSTATUS $name\__op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("uint32_t i;"); + $self->pidl("NTSTATUS ret;"); + $self->pidl(""); + $self->pidlnoindent("#ifdef DCESRV_INTERFACE_$uname\_NCACN_NP_SECONDARY_ENDPOINT"); + $self->pidl("const char *ncacn_np_secondary_endpoint = DCESRV_INTERFACE_$uname\_NCACN_NP_SECONDARY_ENDPOINT;"); + $self->pidlnoindent("#else"); + $self->pidl("const char *ncacn_np_secondary_endpoint = NULL;"); + $self->pidlnoindent("#endif"); + $self->pidl(""); + $self->pidl("for (i=0;icount;i++) {"); + $self->indent(); + $self->pidl("const char *name = ndr_table_$name.endpoints->names[i];"); + $self->pidl(""); + $self->pidl("ret = dcesrv_interface_register(dce_ctx, name, ncacn_np_secondary_endpoint, &dcesrv_$name\_interface, NULL);"); + $self->pidl("if (!NT_STATUS_IS_OK(ret)) {"); + $self->indent(); + $self->pidl("DBG_ERR(\"Failed to register endpoint \'%s\'\\n\",name);"); + $self->pidl("return ret;"); + $self->deindent(); + $self->pidl("}"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static NTSTATUS $name\__op_shutdown_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("return NT_STATUS_OK;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static bool $name\__op_interface_by_uuid(struct dcesrv_interface *iface, const struct GUID *uuid, uint32_t if_version)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("if (dcesrv_$name\_interface.syntax_id.if_version == if_version && GUID_equal(\&dcesrv\_$name\_interface.syntax_id.uuid, uuid)) {"); + $self->indent(); + $self->pidl("memcpy(iface,&dcesrv\_$name\_interface, sizeof(*iface));"); + $self->pidl("return true;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return false;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static bool $name\__op_interface_by_name(struct dcesrv_interface *iface, const char *name)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("if (strcmp(dcesrv_$name\_interface.name, name)==0) {"); + $self->indent(); + $self->pidl("memcpy(iface, &dcesrv_$name\_interface, sizeof(*iface));"); + $self->pidl("return true;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return false;"); + $self->deindent(); + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static const struct dcesrv_endpoint_server $name\_ep_server = {"); + $self->indent(); + $self->pidl("/* fill in our name */"); + $self->pidl(".name = \"$name\","); + $self->pidl(""); + $self->pidl("/* Initialization flag */"); + $self->pidl(".initialized = false,"); + $self->pidl(""); + $self->pidl("/* fill in all the operations */"); + $self->pidlnoindent("#ifdef DCESRV_INTERFACE_$uname\_INIT_SERVER"); + $self->pidl(".init_server = DCESRV_INTERFACE_$uname\_INIT_SERVER,"); + $self->pidlnoindent("#else"); + $self->pidl(".init_server = $name\__op_init_server,"); + $self->pidlnoindent("#endif"); + $self->pidlnoindent("#ifdef DCESRV_INTERFACE_$uname\_SHUTDOWN_SERVER"); + $self->pidl(".shutdown_server = DCESRV_INTERFACE_$uname\_SHUTDOWN_SERVER,"); + $self->pidlnoindent("#else"); + $self->pidl(".shutdown_server = $name\__op_shutdown_server,"); + $self->pidlnoindent("#endif"); + $self->pidl(".interface_by_uuid = $name\__op_interface_by_uuid,"); + $self->pidl(".interface_by_name = $name\__op_interface_by_name"); + $self->deindent(); + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("const struct dcesrv_endpoint_server *$name\_get_ep_server(void)"); + $self->pidl("{"); + $self->indent(); + $self->pidl("return &$name\_ep_server;"); + $self->deindent(); + $self->pidl("}"); +} + +##################################################################### +# dcerpc server boilerplate from a parsed IDL structure +sub parse_interface($) +{ + my ($self, $interface) = @_; + my $count = 0; + my $uif = uc($interface->{NAME}); + + + $self->pidl_hdr("#ifndef __NDR_${uif}_SCOMPAT_H__"); + $self->pidl_hdr("#define __NDR_${uif}_SCOMPAT_H__"); + $self->pidl_hdr(""); + $self->pidl_hdr("struct pipes_struct;"); + $self->pidl_hdr("struct dcesrv_endpoint_server;"); + $self->pidl_hdr("struct dcesrv_call_state;"); + $self->pidl_hdr(""); + $self->pidl_hdr("const struct dcesrv_endpoint_server *$interface->{NAME}\_get_ep_server(void);"); + $self->pidl_hdr(""); + + if (!defined $interface->{PROPERTIES}->{uuid}) { + $self->pidl_hdr("#endif /* __NDR_${uif}_SCOMPAT_H__ */"); + return; + } + + if (!defined $interface->{PROPERTIES}->{version}) { + $interface->{PROPERTIES}->{version} = "0.0"; + } + + foreach my $fn (@{$interface->{FUNCTIONS}}) { + if (defined($fn->{OPNUM})) { $count++; } + } + + if ($count == 0) { + $self->pidl_hdr("#endif /* __NDR_${uif}_SCOMPAT_H__ */"); + return; + } + + $self->pidl("/* $interface->{NAME} - dcerpc server boilerplate generated by pidl */"); + $self->boilerplate_iface($interface); + $self->boilerplate_ep_server($interface); + + $self->pidl_hdr("#endif /* __NDR_${uif}_SCOMPAT_H__ */"); +} + +sub Parse($$) +{ + my ($self, $ndr, $h_scompat, $header) = @_; + + $self->pidl("/* s3 compat server functions auto-generated by pidl */"); + $self->pidl("#include \"$header\""); + $self->pidl("#include \"$h_scompat\""); + + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl(""); + $self->pidl("enum s3compat_rpc_dispatch {"); + $self->indent(); + $self->pidl("S3COMPAT_RPC_DISPATCH_EXTERNAL = 0x00000001,"); + $self->pidl("S3COMPAT_RPC_DISPATCH_INTERNAL = 0x00000002,"); + $self->deindent(); + $self->pidl("};"); + $self->pidl(""); + + foreach my $x (@{$ndr}) { + $self->parse_interface($x) if ($x->{TYPE} eq "INTERFACE" and not defined($x->{PROPERTIES}{object})); + } + + return ($self->{res}, $self->{res_hdr}); +} + +1; diff --git a/pidl/lib/Parse/Pidl/Samba4/Python.pm b/pidl/lib/Parse/Pidl/Samba4/Python.pm new file mode 100644 index 0000000..c09ee4b --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/Python.pm @@ -0,0 +1,2504 @@ +################################################### +# Python function wrapper generator +# Copyright jelmer@samba.org 2007-2008 +# released under the GNU GPL + +package Parse::Pidl::Samba4::Python; +use parent Parse::Pidl::Base; + +use strict; +use warnings; +use Parse::Pidl qw(warning fatal error); +use Parse::Pidl::Typelist qw(hasType resolveType getType mapTypeName expandAlias bitmap_type_fn enum_type_fn); +use Parse::Pidl::Util qw(has_property ParseExpr unmake_str); +use Parse::Pidl::NDR qw(ReturnTypeElement GetPrevLevel GetNextLevel ContainsDeferred ContainsPipe is_charset_array); +use Parse::Pidl::CUtil qw(get_value_of get_pointer_to); +use Parse::Pidl::Samba4 qw(ArrayDynamicallyAllocated); +use Parse::Pidl::Samba4::Header qw(GenerateFunctionInEnv GenerateFunctionOutEnv EnvSubstituteValue GenerateStructEnv); + + +use vars qw($VERSION); +$VERSION = '0.01'; + +sub new($) { + my ($class) = @_; + my $self = { res => "", res_hdr => "", tabs => "", + constants => [], constants_uniq => {}, + module_methods => [], + module_objects => [], module_objects_uniq => {}, + ready_types => [], + module_imports => [], module_imports_uniq => {}, + type_imports => [], type_imports_uniq => {}, + patch_type_calls => [], prereadycode => [], + postreadycode => []}; + bless($self, $class); +} + +sub PrettifyTypeName($$) +{ + my ($name, $basename) = @_; + + $basename =~ s/^.*\.([^.]+)$/$1/; + + $name =~ s/^$basename\_//; + + + return $name; +} + +sub Import +{ + my $self = shift; + my @imports = @_; + foreach (@imports) { + $_ = unmake_str($_); + s/\.idl$//; + $self->pidl_hdr("#include \"librpc/gen_ndr/$_\.h\""); + $self->register_module_import("samba.dcerpc.$_"); + } +} + +sub Const($$) +{ + my ($self, $const) = @_; + $self->register_constant($const->{NAME}, $const->{DTYPE}, $const->{VALUE}); +} + +sub register_constant($$$$) +{ + my ($self, $name, $type, $value) = @_; + + unless (defined $self->{constants_uniq}->{$name}) { + my $h = {"key" => $name, "val" => [$type, $value]}; + push @{$self->{constants}}, $h; + $self->{constants_uniq}->{$name} = $h; + } +} + +sub EnumAndBitmapConsts($$$) +{ + my ($self, $name, $d) = @_; + + return unless (defined($d->{ELEMENTS})); + + foreach my $e (@{$d->{ELEMENTS}}) { + $e =~ /^([A-Za-z0-9_]+)/; + my $cname = $1; + + $self->register_constant($cname, $d, $cname); + } +} + +sub FromUnionToPythonFunction($$$$) +{ + my ($self, $mem_ctx, $type, $switch, $name) = @_; + + $self->pidl("PyObject *ret;"); + $self->pidl(""); + + $self->pidl("switch ($switch) {"); + $self->indent; + + foreach my $e (@{$type->{ELEMENTS}}) { + $self->pidl("$e->{CASE}:"); + + $self->indent; + + if ($e->{NAME}) { + $self->ConvertObjectToPython($mem_ctx, {}, $e, "$name->$e->{NAME}", "ret", "return NULL;"); + } else { + $self->pidl("ret = Py_None;"); + $self->pidl("Py_INCREF(ret);"); + } + + $self->pidl("return ret;"); + $self->pidl(""); + + $self->deindent; + } + + $self->deindent; + $self->pidl("}"); + + $self->pidl("PyErr_SetString(PyExc_TypeError, \"unknown union level\");"); + $self->pidl("return NULL;"); +} + +sub FromPythonToUnionFunction($$$$$) +{ + my ($self, $type, $typename, $switch, $mem_ctx, $name) = @_; + + my $has_default = 0; + + $self->pidl("$typename *ret = talloc_zero($mem_ctx, $typename);"); + + $self->pidl("switch ($switch) {"); + $self->indent; + + foreach my $e (@{$type->{ELEMENTS}}) { + $self->pidl("$e->{CASE}:"); + if ($e->{CASE} eq "default") { $has_default = 1; } + $self->indent; + if ($e->{NAME}) { + $self->ConvertObjectFromPython({}, $mem_ctx, $e, $name, "ret->$e->{NAME}", "talloc_free(ret); return NULL;"); + } + $self->pidl("break;"); + $self->deindent; + $self->pidl(""); + } + + if (!$has_default) { + $self->pidl("default:"); + $self->indent; + $self->pidl("PyErr_SetString(PyExc_TypeError, \"invalid union level value\");"); + $self->pidl("talloc_free(ret);"); + $self->pidl("ret = NULL;"); + $self->deindent; + } + + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return ret;"); +} + +sub PythonElementGetSet($$$$$$) { + my ($self, $name, $cname, $ename, $e, $env) = @_; + + my $varname = "object->$ename"; + $self->pidl("static PyObject *py_$name\_get_$e->{NAME}(PyObject *obj, void *closure)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$cname *object = pytalloc_get_ptr(obj);"); + $self->pidl("PyObject *py_$e->{NAME};"); + my $l = $e->{LEVELS}[0]; + if ($l->{TYPE} eq "POINTER") { + $self->pidl("if ($varname == NULL) {"); + $self->indent; + $self->pidl("Py_RETURN_NONE;"); + $self->deindent; + $self->pidl("}"); + } + $self->ConvertObjectToPython("pytalloc_get_mem_ctx(obj)", $env, $e, $varname, "py_$e->{NAME}", "return NULL;"); + $self->pidl("return py_$e->{NAME};"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static int py_$name\_set_$e->{NAME}(PyObject *py_obj, PyObject *value, void *closure)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$cname *object = pytalloc_get_ptr(py_obj);"); + my $mem_ctx = "pytalloc_get_mem_ctx(py_obj)"; + my $nl = GetNextLevel($e, $l); + if ($l->{TYPE} eq "POINTER" and + not ($nl->{TYPE} eq "ARRAY" and ($nl->{IS_FIXED} or is_charset_array($e, $nl))) and + not ($nl->{TYPE} eq "DATA" and Parse::Pidl::Typelist::scalar_is_reference($nl->{DATA_TYPE}))) { + $self->pidl("talloc_unlink($mem_ctx, discard_const($varname));"); + } + $self->ConvertObjectFromPython($env, $mem_ctx, $e, "value", $varname, "return -1;"); + $self->pidl("return 0;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub PythonStruct($$$$$$) +{ + my ($self, $modulename, $prettyname, $name, $cname, $d) = @_; + + my $env = GenerateStructEnv($d, "object"); + + $self->pidl(""); + + my $getsetters = "NULL"; + + if ($#{$d->{ELEMENTS}} > -1) { + foreach my $e (@{$d->{ELEMENTS}}) { + $self->PythonElementGetSet($name, $cname, $e->{NAME}, $e, $env); + } + + $getsetters = "py_$name\_getsetters"; + $self->pidl("static PyGetSetDef ".$getsetters."[] = {"); + $self->indent; + foreach my $e (@{$d->{ELEMENTS}}) { + my $etype = ""; + if (ref($e->{TYPE}) eq "HASH") { + $etype = $e->{TYPE}->{NAME}; + } else { + $etype = $e->{TYPE}; + } + $self->pidl("{"); + $self->indent; + $self->pidl(".name = discard_const_p(char, \"$e->{NAME}\"),"); + $self->pidl(".get = py_$name\_get_$e->{NAME},"); + $self->pidl(".set = py_$name\_set_$e->{NAME},"); + $self->pidl(".doc = discard_const_p(char, \"PIDL-generated element of base type $etype\")"); + $self->deindent; + $self->pidl("},"); + } + $self->pidl("{ .name = NULL }"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + } + + $self->pidl("static PyObject *py_$name\_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("return pytalloc_new($cname, type);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + my $py_methods = "NULL"; + + # If the struct is not public there ndr_pull/ndr_push functions will + # be static so not callable from here + if (has_property($d, "public")) { + $self->pidl("static PyObject *py_$name\_ndr_pack(PyObject *py_obj, PyObject *Py_UNUSED(ignored))"); + $self->pidl("{"); + $self->indent; + $self->pidl("$cname *object = pytalloc_get_ptr(py_obj);"); + $self->pidl("PyObject *ret = NULL;"); + $self->pidl("DATA_BLOB blob;"); + $self->pidl("enum ndr_err_code err;"); + $self->pidl("TALLOC_CTX *tmp_ctx = talloc_new(pytalloc_get_mem_ctx(py_obj));"); + $self->pidl("if (tmp_ctx == NULL) {"); + $self->indent; + $self->pidl("PyErr_SetNdrError(NDR_ERR_ALLOC);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("err = ndr_push_struct_blob(&blob, tmp_ctx, object, (ndr_push_flags_fn_t)ndr_push_$name);"); + $self->pidl("if (!NDR_ERR_CODE_IS_SUCCESS(err)) {"); + $self->indent; + $self->pidl("TALLOC_FREE(tmp_ctx);"); + $self->pidl("PyErr_SetNdrError(err);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("ret = PyBytes_FromStringAndSize((char *)blob.data, blob.length);"); + $self->pidl("TALLOC_FREE(tmp_ctx);"); + $self->pidl("return ret;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_unpack(PyObject *py_obj, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$cname *object = pytalloc_get_ptr(py_obj);"); + $self->pidl("DATA_BLOB blob = {.data = NULL, .length = 0};"); + $self->pidl("Py_ssize_t blob_length = 0;"); + $self->pidl("enum ndr_err_code err;"); + $self->pidl("const char * const kwnames[] = { \"data_blob\", \"allow_remaining\", NULL };"); + $self->pidl("PyObject *allow_remaining_obj = NULL;"); + $self->pidl("bool allow_remaining = false;"); + $self->pidl(""); + $self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, PYARG_BYTES_LEN \"|O:__ndr_unpack__\","); + $self->indent; + $self->pidl("discard_const_p(char *, kwnames),"); + $self->pidl("&blob.data, &blob_length,"); + $self->pidl("&allow_remaining_obj)) {"); + $self->deindent; + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("blob.length = blob_length;"); + $self->pidl(""); + $self->pidl("if (allow_remaining_obj && PyObject_IsTrue(allow_remaining_obj)) {"); + $self->indent; + $self->pidl("allow_remaining = true;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("if (allow_remaining) {"); + $self->indent; + $self->pidl("err = ndr_pull_struct_blob(&blob, pytalloc_get_mem_ctx(py_obj), object, (ndr_pull_flags_fn_t)ndr_pull_$name);"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $self->pidl("err = ndr_pull_struct_blob_all(&blob, pytalloc_get_mem_ctx(py_obj), object, (ndr_pull_flags_fn_t)ndr_pull_$name);"); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (!NDR_ERR_CODE_IS_SUCCESS(err)) {"); + $self->indent; + $self->pidl("PyErr_SetNdrError(err);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("Py_RETURN_NONE;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_print(PyObject *py_obj, PyObject *Py_UNUSED(ignored))"); + $self->pidl("{"); + $self->indent; + $self->pidl("$cname *object = pytalloc_get_ptr(py_obj);"); + $self->pidl("PyObject *ret;"); + $self->pidl("char *retstr;"); + $self->pidl(""); + $self->pidl("retstr = ndr_print_struct_string(pytalloc_get_mem_ctx(py_obj), (ndr_print_fn_t)ndr_print_$name, \"$name\", object);"); + $self->pidl("ret = PyUnicode_FromString(retstr);"); + $self->pidl("talloc_free(retstr);"); + $self->pidl(""); + $self->pidl("return ret;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $py_methods = "py_$name\_methods"; + $self->pidl("static PyMethodDef $py_methods\[] = {"); + $self->indent; + $self->pidl("{ \"__ndr_pack__\", (PyCFunction)py_$name\_ndr_pack, METH_NOARGS, \"S.ndr_pack(object) -> blob\\nNDR pack\" },"); + $self->pidl("{ \"__ndr_unpack__\", PY_DISCARD_FUNC_SIG(PyCFunction,py_$name\_ndr_unpack), METH_VARARGS|METH_KEYWORDS, \"S.ndr_unpack(class, blob, allow_remaining=False) -> None\\nNDR unpack\" },"); + $self->pidl("{ \"__ndr_print__\", (PyCFunction)py_$name\_ndr_print, METH_NOARGS, \"S.ndr_print(object) -> None\\nNDR print\" },"); + $self->pidl("{ NULL, NULL, 0, NULL }"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + } + + $self->pidl_hdr("static PyTypeObject $name\_Type;"); + $self->pidl(""); + my $docstring = $self->DocString($d, $name); + my $typeobject = "$name\_Type"; + $self->pidl("static PyTypeObject $typeobject = {"); + $self->indent; + $self->pidl("PyVarObject_HEAD_INIT(NULL, 0)"); + $self->pidl(".tp_name = \"$modulename.$prettyname\","); + $self->pidl(".tp_getset = $getsetters,"); + if ($docstring) { + $self->pidl(".tp_doc = $docstring,"); + } + $self->pidl(".tp_methods = $py_methods,"); + $self->pidl(".tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,"); + $self->pidl(".tp_new = py_$name\_new,"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + + my $talloc_typename = $self->import_type_variable("talloc", "BaseObject"); + $self->register_module_prereadycode(["$name\_Type.tp_base = $talloc_typename;", + "$name\_Type.tp_basicsize = pytalloc_BaseObject_size();", + ""]); + + return "&$typeobject"; +} + +sub PythonFunctionStruct($$$$) +{ + my ($self, $modulename, $fn, $iface, $prettyname) = @_; + + my $inenv = GenerateFunctionInEnv($fn, "object->"); + my $outenv = GenerateFunctionOutEnv($fn, "object->"); + + my $name = "$fn->{NAME}"; + my $cname = "struct $name"; + + $self->pidl(""); + + my $getsetters = "NULL"; + + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/in/,@{$e->{DIRECTION}})) { + my $inname = "$name\_in"; + my $ename = "in.$e->{NAME}"; + $self->PythonElementGetSet($inname, $cname, $ename, $e, $inenv); + } + if (grep(/out/,@{$e->{DIRECTION}})) { + my $outname = "$name\_out"; + my $ename = "out.$e->{NAME}"; + $self->PythonElementGetSet($outname, $cname, $ename, $e, $outenv); + } + } + + if (defined($fn->{RETURN_TYPE})) { + my $e = ReturnTypeElement($fn); + my $ename = "out.result"; + $self->PythonElementGetSet($name, $cname, $ename, $e, $outenv); + } + + $getsetters = "py_$name\_getsetters"; + $self->pidl("static PyGetSetDef ".$getsetters."[] = {"); + $self->indent; + foreach my $e (@{$fn->{ELEMENTS}}) { + if (grep(/in/,@{$e->{DIRECTION}})) { + $self->pidl("{"); + $self->indent; + $self->pidl(".name = discard_const_p(char, \"in_$e->{NAME}\"),"); + $self->pidl(".get = py_$name\_in_get_$e->{NAME},"); + $self->pidl(".set = py_$name\_in_set_$e->{NAME},"); + $self->pidl(".doc = discard_const_p(char, \"PIDL-generated element of base type $e->{TYPE}\")"); + $self->deindent; + $self->pidl("},"); + } + if (grep(/out/,@{$e->{DIRECTION}})) { + $self->pidl("{"); + $self->indent; + $self->pidl(".name = discard_const_p(char, \"out_$e->{NAME}\"),"); + $self->pidl(".get = py_$name\_out_get_$e->{NAME},"); + $self->pidl(".set = py_$name\_out_set_$e->{NAME},"); + $self->pidl(".doc = discard_const_p(char, \"PIDL-generated element of base type $e->{TYPE}\")"); + $self->deindent; + $self->pidl("},"); + } + } + if (defined($fn->{RETURN_TYPE})) { + $self->pidl("{"); + $self->indent; + $self->pidl(".name = discard_const_p(char, \"result\"),"); + $self->pidl(".get = py_$name\_get_result,"); + $self->pidl(".set = py_$name\_set_result,"); + $self->pidl(".doc = discard_const_p(char, \"PIDL-generated element of type $fn->{RETURN_TYPE}\")"); + $self->deindent; + $self->pidl("},"); + } + $self->pidl("{ .name = NULL }"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + + # This creates a new, zeroed C structure and python object. + # These may not be valid or sensible values, but this is as + # well as we can do. + + $self->pidl("PyObject *self = pytalloc_new($cname, type);"); + + # If there are any children that are ref pointers, we need to + # allocate something for them to point to just as the pull + # routine will when parsing the structure from NDR. + # + # We then make those pointers point to zeroed memory + # + # A ref pointer is a pointer in the C structure but a scalar + # on the wire. It is for a remote function like: + # + # int foo(int *i) + # + # This may be called with the pointer by reference eg foo(&i) + # + # That is why this only goes as far as the next level; deeply + # nested pointer chains will end in a NULL. + + my @ref_elements; + foreach my $e (@{$fn->{ELEMENTS}}) { + if (has_property($e, "ref") && ! has_property($e, "charset")) { + if (!has_property($e, 'in') && !has_property($e, 'out')) { + die "ref pointer that is not in or out"; + } + push @ref_elements, $e; + } + } + if (@ref_elements) { + $self->pidl("$cname *_self = ($cname *)pytalloc_get_ptr(self);"); + $self->pidl("TALLOC_CTX *mem_ctx = pytalloc_get_mem_ctx(self);"); + foreach my $e (@ref_elements) { + my $ename = $e->{NAME}; + my $t = mapTypeName($e->{TYPE}); + my $p = $e->{ORIGINAL}->{POINTERS} // 1; + if ($p > 1) { + $self->pidl("/* a pointer to a NULL pointer */"); + $t .= ' ' . '*' x ($p - 1); + } + + # We checked in the loop above that each ref + # pointer is in or out (or both) + if (has_property($e, 'in')) { + $self->pidl("_self->in.$ename = talloc_zero(mem_ctx, $t);"); + } + + if (has_property($e, 'out')) { + $self->pidl("_self->out.$ename = talloc_zero(mem_ctx, $t);"); + } + } + } + $self->pidl("return self;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + my $py_methods = "NULL"; + + my $ndr_call = "const struct ndr_interface_call *call = NULL;"; + my $object_ptr = "$cname *object = pytalloc_get_ptr(py_obj);"; + + $self->pidl("static PyObject *py_$name\_ndr_opnum(PyTypeObject *type, PyObject *Py_UNUSED(ignored))"); + $self->pidl("{"); + $self->indent; + $self->pidl(""); + $self->pidl(""); + $self->pidl("return PyLong_FromLong($fn->{OPNUM});"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_pack(PyObject *py_obj, ndr_flags_type ndr_inout_flags, libndr_flags ndr_push_flags)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$ndr_call"); + $self->pidl("$object_ptr"); + $self->pidl("PyObject *ret = NULL;"); + $self->pidl("struct ndr_push *push = NULL;"); + $self->pidl("DATA_BLOB blob;"); + $self->pidl("enum ndr_err_code err;"); + $self->pidl(""); + $self->pidl("if (ndr_table_$iface\.num_calls < " . ($fn->{OPNUM}+1) . + ") {"); + $self->indent; + $self->pidl("PyErr_SetString(PyExc_TypeError, \"Internal Error, ndr_interface_call missing for py_$name\_ndr_pack\");"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("call = &ndr_table_$iface\.calls[$fn->{OPNUM}];"); + $self->pidl(""); + $self->pidl("push = ndr_push_init_ctx(pytalloc_get_mem_ctx(py_obj));"); + $self->pidl("if (push == NULL) {"); + $self->indent; + $self->pidl("PyErr_SetNdrError(NDR_ERR_ALLOC);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("push->flags |= ndr_push_flags;"); + $self->pidl(""); + $self->pidl("err = call->ndr_push(push, ndr_inout_flags, object);"); + $self->pidl("if (!NDR_ERR_CODE_IS_SUCCESS(err)) {"); + $self->indent; + $self->pidl("TALLOC_FREE(push);"); + $self->pidl("PyErr_SetNdrError(err);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("blob = ndr_push_blob(push);"); + $self->pidl("ret = PyBytes_FromStringAndSize((char *)blob.data, blob.length);"); + $self->pidl("TALLOC_FREE(push);"); + $self->pidl("return ret;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_pack_in(PyObject *py_obj, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("const char * const kwnames[] = { \"bigendian\", \"ndr64\", NULL };"); + $self->pidl("PyObject *bigendian_obj = NULL;"); + $self->pidl("PyObject *ndr64_obj = NULL;"); + $self->pidl("libndr_flags ndr_push_flags = 0;"); + $self->pidl(""); + $self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"|OO:__ndr_pack_in__\","); + $self->indent; + $self->pidl("discard_const_p(char *, kwnames),"); + $self->pidl("&bigendian_obj,"); + $self->pidl("&ndr64_obj)) {"); + $self->deindent; + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("if (bigendian_obj && PyObject_IsTrue(bigendian_obj)) {"); + $self->indent; + $self->pidl("ndr_push_flags |= LIBNDR_FLAG_BIGENDIAN;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (ndr64_obj && PyObject_IsTrue(ndr64_obj)) {"); + $self->indent; + $self->pidl("ndr_push_flags |= LIBNDR_FLAG_NDR64;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return py_$name\_ndr_pack(py_obj, NDR_IN, ndr_push_flags);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_pack_out(PyObject *py_obj, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("const char * const kwnames[] = { \"bigendian\", \"ndr64\", NULL };"); + $self->pidl("PyObject *bigendian_obj = NULL;"); + $self->pidl("PyObject *ndr64_obj = NULL;"); + $self->pidl("libndr_flags ndr_push_flags = 0;"); + $self->pidl(""); + $self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"|OO:__ndr_pack_out__\","); + $self->indent; + $self->pidl("discard_const_p(char *, kwnames),"); + $self->pidl("&bigendian_obj,"); + $self->pidl("&ndr64_obj)) {"); + $self->deindent; + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("if (bigendian_obj && PyObject_IsTrue(bigendian_obj)) {"); + $self->indent; + $self->pidl("ndr_push_flags |= LIBNDR_FLAG_BIGENDIAN;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (ndr64_obj && PyObject_IsTrue(ndr64_obj)) {"); + $self->indent; + $self->pidl("ndr_push_flags |= LIBNDR_FLAG_NDR64;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return py_$name\_ndr_pack(py_obj, NDR_OUT, ndr_push_flags);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_unpack(PyObject *py_obj, const DATA_BLOB *blob, ndr_flags_type ndr_inout_flags, libndr_flags ndr_pull_flags, bool allow_remaining)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$ndr_call"); + $self->pidl("$object_ptr"); + $self->pidl("struct ndr_pull *pull = NULL;"); + $self->pidl("enum ndr_err_code err;"); + $self->pidl(""); + $self->pidl("if (ndr_table_$iface\.num_calls < " . ($fn->{OPNUM}+1) . + ") {"); + $self->indent; + $self->pidl("PyErr_SetString(PyExc_TypeError, \"Internal Error, ndr_interface_call missing for py_$name\_ndr_unpack\");"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("call = &ndr_table_$iface\.calls[$fn->{OPNUM}];"); + $self->pidl(""); + $self->pidl("pull = ndr_pull_init_blob(blob, object);"); + $self->pidl("if (pull == NULL) {"); + $self->indent; + $self->pidl("PyErr_SetNdrError(NDR_ERR_ALLOC);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("pull->flags |= ndr_pull_flags;"); + $self->pidl(""); + $self->pidl("err = call->ndr_pull(pull, ndr_inout_flags, object);"); + $self->pidl("if (!NDR_ERR_CODE_IS_SUCCESS(err)) {"); + $self->indent; + $self->pidl("TALLOC_FREE(pull);"); + $self->pidl("PyErr_SetNdrError(err);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (!allow_remaining) {"); + $self->indent; + $self->pidl("uint32_t highest_ofs;"); + $self->pidl(""); + $self->pidl("if (pull->offset > pull->relative_highest_offset) {"); + $self->indent; + $self->pidl("highest_ofs = pull->offset;"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $self->pidl("highest_ofs = pull->relative_highest_offset;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (highest_ofs < pull->data_size) {"); + $self->indent; + $self->pidl("err = ndr_pull_error(pull, NDR_ERR_UNREAD_BYTES,"); + $self->indent; + $self->pidl("\"not all bytes consumed ofs[%u] size[%u]\","); + $self->pidl("highest_ofs, pull->data_size);"); + $self->deindent; + $self->pidl("TALLOC_FREE(pull);"); + $self->pidl("PyErr_SetNdrError(err);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("TALLOC_FREE(pull);"); + $self->pidl("Py_RETURN_NONE;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_unpack_in(PyObject *py_obj, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("DATA_BLOB blob;"); + $self->pidl("Py_ssize_t blob_length = 0;"); + $self->pidl("const char * const kwnames[] = { \"data_blob\", \"bigendian\", \"ndr64\", \"allow_remaining\", NULL };"); + $self->pidl("PyObject *bigendian_obj = NULL;"); + $self->pidl("PyObject *ndr64_obj = NULL;"); + $self->pidl("libndr_flags ndr_pull_flags = LIBNDR_FLAG_REF_ALLOC;"); + $self->pidl("PyObject *allow_remaining_obj = NULL;"); + $self->pidl("bool allow_remaining = false;"); + $self->pidl(""); + $self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, PYARG_BYTES_LEN \"|OOO:__ndr_unpack_in__\","); + $self->indent; + $self->pidl("discard_const_p(char *, kwnames),"); + $self->pidl("&blob.data, &blob_length,"); + $self->pidl("&bigendian_obj,"); + $self->pidl("&ndr64_obj,"); + $self->pidl("&allow_remaining_obj)) {"); + $self->deindent; + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("blob.length = blob_length;"); + $self->pidl(""); + $self->pidl("if (bigendian_obj && PyObject_IsTrue(bigendian_obj)) {"); + $self->indent; + $self->pidl("ndr_pull_flags |= LIBNDR_FLAG_BIGENDIAN;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (ndr64_obj && PyObject_IsTrue(ndr64_obj)) {"); + $self->indent; + $self->pidl("ndr_pull_flags |= LIBNDR_FLAG_NDR64;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("if (allow_remaining_obj && PyObject_IsTrue(allow_remaining_obj)) {"); + $self->indent; + $self->pidl("allow_remaining = true;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return py_$name\_ndr_unpack(py_obj, &blob, NDR_IN, ndr_pull_flags, allow_remaining);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_unpack_out(PyObject *py_obj, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("DATA_BLOB blob;"); + $self->pidl("Py_ssize_t blob_length = 0;"); + $self->pidl("const char * const kwnames[] = { \"data_blob\", \"bigendian\", \"ndr64\", \"allow_remaining\", NULL };"); + $self->pidl("PyObject *bigendian_obj = NULL;"); + $self->pidl("PyObject *ndr64_obj = NULL;"); + $self->pidl("libndr_flags ndr_pull_flags = LIBNDR_FLAG_REF_ALLOC;"); + $self->pidl("PyObject *allow_remaining_obj = NULL;"); + $self->pidl("bool allow_remaining = false;"); + $self->pidl(""); + $self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, PYARG_BYTES_LEN \"|OOO:__ndr_unpack_out__\","); + $self->indent; + $self->pidl("discard_const_p(char *, kwnames),"); + $self->pidl("&blob.data, &blob_length,"); + $self->pidl("&bigendian_obj,"); + $self->pidl("&ndr64_obj,"); + $self->pidl("&allow_remaining_obj)) {"); + $self->deindent; + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("blob.length = blob_length;"); + $self->pidl(""); + $self->pidl("if (bigendian_obj && PyObject_IsTrue(bigendian_obj)) {"); + $self->indent; + $self->pidl("ndr_pull_flags |= LIBNDR_FLAG_BIGENDIAN;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (ndr64_obj && PyObject_IsTrue(ndr64_obj)) {"); + $self->indent; + $self->pidl("ndr_pull_flags |= LIBNDR_FLAG_NDR64;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("if (allow_remaining_obj && PyObject_IsTrue(allow_remaining_obj)) {"); + $self->indent; + $self->pidl("allow_remaining = true;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return py_$name\_ndr_unpack(py_obj, &blob, NDR_OUT, ndr_pull_flags, allow_remaining);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_print(PyObject *py_obj, const char *name, ndr_flags_type ndr_inout_flags)"); + $self->pidl("{"); + $self->indent; + $self->pidl("$ndr_call"); + $self->pidl("$object_ptr"); + $self->pidl("PyObject *ret;"); + $self->pidl("char *retstr;"); + $self->pidl(""); + $self->pidl("if (ndr_table_$iface\.num_calls < " . ($fn->{OPNUM}+1) . + ") {"); + $self->indent; + $self->pidl("PyErr_SetString(PyExc_TypeError, \"Internal Error, ndr_interface_call missing for py_$name\_ndr_print\");"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("call = &ndr_table_$iface\.calls[$fn->{OPNUM}];"); + $self->pidl(""); + $self->pidl("retstr = ndr_print_function_string(pytalloc_get_mem_ctx(py_obj), call->ndr_print, name, ndr_inout_flags, object);"); + $self->pidl("ret = PyUnicode_FromString(retstr);"); + $self->pidl("TALLOC_FREE(retstr);"); + $self->pidl(""); + $self->pidl("return ret;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_print_in(PyObject *py_obj, PyObject *Py_UNUSED(ignored))"); + $self->pidl("{"); + $self->indent; + $self->pidl("return py_$name\_ndr_print(py_obj, \"$name\_in\", NDR_IN);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$name\_ndr_print_out(PyObject *py_obj, PyObject *Py_UNUSED(ignored))"); + $self->pidl("{"); + $self->indent; + $self->pidl("return py_$name\_ndr_print(py_obj, \"$name\_out\", NDR_OUT);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $py_methods = "py_$name\_methods"; + $self->pidl("static PyMethodDef $py_methods\[] = {"); + $self->indent; + $self->pidl("{ \"opnum\", (PyCFunction)py_$name\_ndr_opnum, METH_NOARGS|METH_CLASS,"); + $self->indent; + $self->pidl("\"$modulename.$prettyname.opnum() -> ".sprintf("%d (0x%02x)", $fn->{OPNUM}, $fn->{OPNUM})." \" },"); + $self->deindent; + $self->pidl("{ \"__ndr_pack_in__\", PY_DISCARD_FUNC_SIG(PyCFunction,py_$name\_ndr_pack_in), METH_VARARGS|METH_KEYWORDS,"); + $self->indent; + $self->pidl("\"S.ndr_pack_in(object, bigendian=False, ndr64=False) -> blob\\nNDR pack input\" },"); + $self->deindent; + $self->pidl("{ \"__ndr_pack_out__\", PY_DISCARD_FUNC_SIG(PyCFunction,py_$name\_ndr_pack_out), METH_VARARGS|METH_KEYWORDS,"); + $self->indent; + $self->pidl("\"S.ndr_pack_out(object, bigendian=False, ndr64=False) -> blob\\nNDR pack output\" },"); + $self->deindent; + $self->pidl("{ \"__ndr_unpack_in__\", PY_DISCARD_FUNC_SIG(PyCFunction,py_$name\_ndr_unpack_in), METH_VARARGS|METH_KEYWORDS,"); + $self->indent; + $self->pidl("\"S.ndr_unpack_in(class, blob, bigendian=False, ndr64=False, allow_remaining=False) -> None\\nNDR unpack input\" },"); + $self->deindent; + $self->pidl("{ \"__ndr_unpack_out__\", PY_DISCARD_FUNC_SIG(PyCFunction,py_$name\_ndr_unpack_out), METH_VARARGS|METH_KEYWORDS,"); + $self->indent; + $self->pidl("\"S.ndr_unpack_out(class, blob, bigendian=False, ndr64=False, allow_remaining=False) -> None\\nNDR unpack output\" },"); + $self->deindent; + $self->pidl("{ \"__ndr_print_in__\", (PyCFunction)py_$name\_ndr_print_in, METH_NOARGS, \"S.ndr_print_in(object) -> None\\nNDR print input\" },"); + $self->pidl("{ \"__ndr_print_out__\", (PyCFunction)py_$name\_ndr_print_out, METH_NOARGS, \"S.ndr_print_out(object) -> None\\nNDR print output\" },"); + $self->pidl("{ NULL, NULL, 0, NULL }"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + + $self->pidl_hdr("static PyTypeObject $name\_Type;"); + $self->pidl(""); + my $docstring = $self->DocString($fn, $name); + my $typeobject = "$name\_Type"; + $self->pidl("static PyTypeObject $typeobject = {"); + $self->indent; + $self->pidl("PyVarObject_HEAD_INIT(NULL, 0)"); + $self->pidl(".tp_name = \"$modulename.$prettyname\","); + $self->pidl(".tp_getset = $getsetters,"); + if ($docstring) { + $self->pidl(".tp_doc = $docstring,"); + } + $self->pidl(".tp_methods = $py_methods,"); + $self->pidl(".tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,"); + $self->pidl(".tp_new = py_$name\_new,"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + + my $talloc_typename = $self->import_type_variable("talloc", "BaseObject"); + $self->register_module_prereadycode(["$name\_Type.tp_base = $talloc_typename;", + "$name\_Type.tp_basicsize = pytalloc_BaseObject_size();", + ""]); + + return "&$typeobject"; +} + +sub get_metadata_var($) +{ + my ($e) = @_; + sub get_var($) { my $x = shift; $x =~ s/\*//g; return $x; } + + if (has_property($e, "length_is")) { + return get_var($e->{PROPERTIES}->{length_is}); + } elsif (has_property($e, "size_is")) { + return get_var($e->{PROPERTIES}->{size_is}); + } + + return undef; +} + +sub find_metadata_args($) +{ + my ($fn) = @_; + my $metadata_args = { in => {}, out => {} }; + + # Determine arguments that are metadata for other arguments (size_is/length_is) + foreach my $e (@{$fn->{ELEMENTS}}) { + foreach my $dir (@{$e->{DIRECTION}}) { + my $main = get_metadata_var($e); + if ($main) { + $metadata_args->{$dir}->{$main} = $e->{NAME}; + } + } + } + + return $metadata_args; +} + +sub PythonFunctionUnpackOut($$$) +{ + my ($self, $fn, $fnname) = @_; + + my $outfnname = "unpack_$fnname\_args_out"; + my $signature = ""; + + my $metadata_args = find_metadata_args($fn); + + my $env = GenerateFunctionOutEnv($fn, "r->"); + my $result_size = 0; + + $self->pidl("static PyObject *$outfnname(struct $fn->{NAME} *r)"); + $self->pidl("{"); + $self->indent; + $self->pidl("PyObject *result;"); + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/out/,@{$e->{DIRECTION}})); + next if (($metadata_args->{in}->{$e->{NAME}} and grep(/in/, @{$e->{DIRECTION}})) or + ($metadata_args->{out}->{$e->{NAME}}) and grep(/out/, @{$e->{DIRECTION}})); + $self->pidl("PyObject *py_$e->{NAME};"); + $result_size++; + } + + if ($fn->{RETURN_TYPE}) { + $result_size++ unless ($fn->{RETURN_TYPE} eq "WERROR" or $fn->{RETURN_TYPE} eq "NTSTATUS"); + } + + my $i = 0; + + if ($result_size > 1) { + $self->pidl("result = PyTuple_New($result_size);"); + $signature .= "("; + } elsif ($result_size == 0) { + $self->pidl("result = Py_None;"); + $self->pidl("Py_INCREF(result);"); + $signature .= "None"; + } + + foreach my $e (@{$fn->{ELEMENTS}}) { + next if ($metadata_args->{out}->{$e->{NAME}}); + my $py_name = "py_$e->{NAME}"; + if (grep(/out/,@{$e->{DIRECTION}})) { + $self->ConvertObjectToPython("r", $env, $e, "r->out.$e->{NAME}", $py_name, "return NULL;"); + if ($result_size > 1) { + $self->pidl("PyTuple_SetItem(result, $i, $py_name);"); + $i++; + $signature .= "$e->{NAME}, "; + } else { + $self->pidl("result = $py_name;"); + $signature .= $e->{NAME}; + } + } + } + + if (defined($fn->{RETURN_TYPE}) and $fn->{RETURN_TYPE} eq "NTSTATUS") { + $self->handle_ntstatus("r->out.result", "NULL", undef); + } elsif (defined($fn->{RETURN_TYPE}) and $fn->{RETURN_TYPE} eq "WERROR") { + $self->handle_werror("r->out.result", "NULL", undef); + } elsif (defined($fn->{RETURN_TYPE})) { + my $conv = $self->ConvertObjectToPythonData("r", $fn->{RETURN_TYPE}, "r->out.result", $fn); + if ($result_size > 1) { + $self->pidl("PyTuple_SetItem(result, $i, $conv);"); + } else { + $self->pidl("result = $conv;"); + } + $signature .= "result"; + } + + if (substr($signature, -2) eq ", ") { + $signature = substr($signature, 0, -2); + } + if ($result_size > 1) { + $signature .= ")"; + } + + $self->pidl("return result;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + return ($outfnname, $signature); +} + +sub PythonFunctionPackIn($$$) +{ + my ($self, $fn, $fnname) = @_; + my $metadata_args = find_metadata_args($fn); + + my $infnname = "pack_$fnname\_args_in"; + + $self->pidl("static bool $infnname(PyObject *args, PyObject *kwargs, struct $fn->{NAME} *r)"); + $self->pidl("{"); + $self->indent; + my $args_format = ""; + my $args_string = ""; + my $args_names = ""; + my $signature = ""; + + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/,@{$e->{DIRECTION}})); + next if (($metadata_args->{in}->{$e->{NAME}} and grep(/in/, @{$e->{DIRECTION}})) or + ($metadata_args->{out}->{$e->{NAME}}) and grep(/out/, @{$e->{DIRECTION}})); + $self->pidl("PyObject *py_$e->{NAME};"); + $args_format .= "O"; + $args_string .= ", &py_$e->{NAME}"; + $args_names .= "\"$e->{NAME}\", "; + $signature .= "$e->{NAME}, "; + } + if (substr($signature, -2) eq ", ") { + $signature = substr($signature, 0, -2); + } + $self->pidl("const char *kwnames[] = {"); + $self->indent; + $self->pidl($args_names . "NULL"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + $self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"$args_format:$fn->{NAME}\", discard_const_p(char *, kwnames)$args_string)) {"); + $self->indent; + $self->pidl("return false;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + my $env = GenerateFunctionInEnv($fn, "r->"); + + my $fail = "return false;"; + foreach my $e (@{$fn->{ELEMENTS}}) { + next unless (grep(/in/,@{$e->{DIRECTION}})); + if ($metadata_args->{in}->{$e->{NAME}}) { + my $py_var = "py_".$metadata_args->{in}->{$e->{NAME}}; + $self->pidl("PY_CHECK_TYPE(&PyList_Type, $py_var, $fail);"); + my $val = "PyList_GET_SIZE($py_var)"; + if ($e->{LEVELS}[0]->{TYPE} eq "POINTER") { + $self->pidl("r->in.$e->{NAME} = talloc_ptrtype(r, r->in.$e->{NAME});"); + $self->pidl("if (r->in.$e->{NAME} == NULL) {"); + $self->indent; + $self->pidl("PyErr_NoMemory();"); + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + $self->pidl("*r->in.$e->{NAME} = $val;"); + } else { + $self->pidl("r->in.$e->{NAME} = $val;"); + } + } else { + $self->ConvertObjectFromPython($env, "r", $e, "py_$e->{NAME}", "r->in.$e->{NAME}", $fail); + } + } + $self->pidl("return true;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + return ($infnname, $signature); +} + +sub PythonFunction($$$) +{ + my ($self, $fn, $iface, $prettyname) = @_; + + my $fnname = "py_$fn->{NAME}"; + my $docstring = $self->DocString($fn, $fn->{NAME}); + + my ($infn, $insignature) = $self->PythonFunctionPackIn($fn, $fnname); + my ($outfn, $outsignature) = $self->PythonFunctionUnpackOut($fn, $fnname); + my $signature = "S.$prettyname($insignature) -> $outsignature"; + if ($docstring) { + $docstring = "\"$signature\\n\\n\"$docstring"; + } else { + $docstring = "\"$signature\""; + } + + return ($infn, $outfn, $docstring); +} + +sub handle_werror($$$$) +{ + my ($self, $var, $retval, $mem_ctx) = @_; + + $self->pidl("if (!W_ERROR_IS_OK($var)) {"); + $self->indent; + $self->pidl("PyErr_SetWERROR($var);"); + $self->pidl("talloc_free($mem_ctx);") if ($mem_ctx); + $self->pidl("return $retval;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub handle_ntstatus($$$$) +{ + my ($self, $var, $retval, $mem_ctx) = @_; + + $self->pidl("if (NT_STATUS_IS_ERR($var)) {"); + $self->indent; + $self->pidl("PyErr_SetNTSTATUS($var);"); + $self->pidl("talloc_free($mem_ctx);") if ($mem_ctx); + $self->pidl("return $retval;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); +} + +sub PythonType($$$$) +{ + my ($self, $modulename, $d, $interface, $basename) = @_; + + my $actual_ctype = $d; + if ($actual_ctype->{TYPE} eq "TYPEDEF") { + $actual_ctype = $actual_ctype->{DATA}; + } + + if ($actual_ctype->{TYPE} eq "STRUCT") { + my $typeobject; + my $fn_name = PrettifyTypeName($d->{NAME}, $basename); + + if ($d->{TYPE} eq "STRUCT") { + $typeobject = $self->PythonStruct($modulename, $fn_name, $d->{NAME}, mapTypeName($d), $d); + } else { + $typeobject = $self->PythonStruct($modulename, $fn_name, $d->{NAME}, mapTypeName($d), $d->{DATA}); + } + + $self->register_module_typeobject($fn_name, $typeobject, $d->{ORIGINAL}); + } + + if ($d->{TYPE} eq "ENUM" or $d->{TYPE} eq "BITMAP") { + $self->EnumAndBitmapConsts($d->{NAME}, $d); + } + + if ($d->{TYPE} eq "TYPEDEF" and ($d->{DATA}->{TYPE} eq "ENUM" or $d->{DATA}->{TYPE} eq "BITMAP")) { + $self->EnumAndBitmapConsts($d->{NAME}, $d->{DATA}); + } + + if ($actual_ctype->{TYPE} eq "UNION" and defined($actual_ctype->{ELEMENTS})) { + my $prettyname = PrettifyTypeName($d->{NAME}, $basename); + my $typeobject = "$d->{NAME}\_Type"; + my $docstring = $self->DocString($d, $d->{NAME}); + my $cname = "union $d->{NAME}"; + + $self->pidl("static PyObject *py_import_$d->{NAME}(TALLOC_CTX *mem_ctx, int level, " .mapTypeName($d) . " *in)"); + $self->pidl("{"); + $self->indent; + $self->FromUnionToPythonFunction("mem_ctx", $actual_ctype, "level", "in") if ($actual_ctype->{TYPE} eq "UNION"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static ".mapTypeName($d) . " *py_export_$d->{NAME}(TALLOC_CTX *mem_ctx, int level, PyObject *in)"); + $self->pidl("{"); + $self->indent; + $self->FromPythonToUnionFunction($actual_ctype, mapTypeName($d), "level", "mem_ctx", "in") if ($actual_ctype->{TYPE} eq "UNION"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + my $getsetters = "NULL"; + my $py_methods = "NULL"; + my $typename = mapTypeName($d); + + $self->pidl("static PyObject *py_$d->{NAME}\_import(PyTypeObject *type, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("const char * const kwnames[] = { \"mem_ctx\", \"level\", \"in\", NULL };"); + $self->pidl("PyObject *mem_ctx_obj = NULL;"); + $self->pidl("TALLOC_CTX *mem_ctx = NULL;"); + $self->pidl("int level = 0;"); + $self->pidl("PyObject *in_obj = NULL;"); + $self->pidl("$typename *in = NULL;"); + $self->pidl(""); + $self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"OiO:import\","); + $self->indent; + $self->pidl("discard_const_p(char *, kwnames),"); + $self->pidl("&mem_ctx_obj,"); + $self->pidl("&level,"); + $self->pidl("&in_obj)) {"); + $self->deindent; + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("mem_ctx = pytalloc_get_ptr(mem_ctx_obj);"); + $self->pidl("if (mem_ctx == NULL) {"); + $self->indent; + $self->pidl("PyErr_SetString(PyExc_TypeError, \"mem_ctx is NULL)!\");"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("in = ($typename *)pytalloc_get_ptr(in_obj);"); + $self->pidl("if (in == NULL) {"); + $self->indent; + $self->pidl("PyErr_Format(PyExc_TypeError, \"in needs to be a pointer to $typename!\");"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return py_import_$d->{NAME}(mem_ctx, level, in);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$d->{NAME}\_export(PyTypeObject *type, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("const char * const kwnames[] = { \"mem_ctx\", \"level\", \"in\", NULL };"); + $self->pidl("PyObject *mem_ctx_obj = NULL;"); + $self->pidl("TALLOC_CTX *mem_ctx = NULL;"); + $self->pidl("int level = 0;"); + $self->pidl("PyObject *in = NULL;"); + $self->pidl("$typename *out = NULL;"); + $self->pidl(""); + $self->pidl("if (!PyArg_ParseTupleAndKeywords(args, kwargs, \"OiO:export\","); + $self->indent; + $self->pidl("discard_const_p(char *, kwnames),"); + $self->pidl("&mem_ctx_obj,"); + $self->pidl("&level,"); + $self->pidl("&in)) {"); + $self->deindent; + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl("mem_ctx = pytalloc_get_ptr(mem_ctx_obj);"); + $self->pidl("if (mem_ctx == NULL) {"); + $self->indent; + $self->pidl("PyErr_SetString(PyExc_TypeError, \"mem_ctx is NULL)!\");"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("out = py_export_$d->{NAME}(mem_ctx, level, in);"); + $self->pidl("if (out == NULL) {"); + $self->indent; + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("return pytalloc_GenericObject_reference(out);"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $py_methods = "py_$d->{NAME}_methods"; + $self->pidl("static PyMethodDef $py_methods\[] = {"); + $self->indent; + $self->pidl("{ \"__import__\", PY_DISCARD_FUNC_SIG(PyCFunction,py_$d->{NAME}\_import),"); + $self->indent; + $self->pidl("METH_VARARGS|METH_KEYWORDS|METH_CLASS,"); + $self->pidl("\"T.__import__(mem_ctx, level, in) => ret.\" },"); + $self->deindent; + $self->pidl("{ \"__export__\", PY_DISCARD_FUNC_SIG(PyCFunction,py_$d->{NAME}\_export),"); + $self->indent; + $self->pidl("METH_VARARGS|METH_KEYWORDS|METH_CLASS,"); + $self->pidl("\"T.__export__(mem_ctx, level, in) => ret.\" },"); + $self->deindent; + $self->pidl("{ NULL, NULL, 0, NULL }"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static PyObject *py_$d->{NAME}\_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("PyErr_Format(PyExc_TypeError, \"New %s Objects are not supported\", type->tp_name);"); + $self->pidl("return NULL;"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + + $self->pidl(""); + $self->pidl_hdr("static PyTypeObject $typeobject;"); + $self->pidl("static PyTypeObject $typeobject = {"); + $self->indent; + $self->pidl("PyVarObject_HEAD_INIT(NULL, 0)"); + $self->pidl(".tp_name = \"$modulename.$prettyname\","); + $self->pidl(".tp_getset = $getsetters,"); + if ($docstring) { + $self->pidl(".tp_doc = $docstring,"); + } + $self->pidl(".tp_methods = $py_methods,"); + $self->pidl(".tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,"); + $self->pidl(".tp_new = py_$d->{NAME}\_new,"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + + my $talloc_typename = $self->import_type_variable("talloc", "BaseObject"); + $self->register_module_prereadycode(["$typeobject.tp_base = $talloc_typename;", + "$typeobject.tp_basicsize = pytalloc_BaseObject_size();", + ""]); + + $self->register_module_typeobject($prettyname, "&$typeobject", $d->{ORIGINAL}); + } +} + +sub DocString($$$) +{ + my ($self, $d, $name) = @_; + if (has_property($d, "helpstring")) { + my $docstring = uc("py_doc_$name"); + $self->pidl("#define $docstring ".has_property($d, "helpstring")); + return $docstring; + } + + return undef; +} + +sub Interface($$$) +{ + my($self,$interface,$basename) = @_; + + if (has_property($interface, "pyhelper")) { + $self->pidl("#include \"".unmake_str($interface->{PROPERTIES}->{pyhelper})."\"\n"); + } + + $self->Const($_) foreach (@{$interface->{CONSTS}}); + + foreach my $d (@{$interface->{TYPES}}) { + next if has_property($d, "nopython"); + + $self->PythonType($basename, $d, $interface, $basename); + } + + if (defined $interface->{PROPERTIES}->{uuid}) { + $self->pidl_hdr("static PyTypeObject $interface->{NAME}_InterfaceType;"); + $self->pidl(""); + + my @fns = (); + + foreach my $d (@{$interface->{FUNCTIONS}}) { + next if has_property($d, "noopnum"); + next if has_property($d, "nopython"); + next if has_property($d, "todo"); + + my $skip = 0; + foreach my $e (@{$d->{ELEMENTS}}) { + if (ContainsPipe($e, $e->{LEVELS}[0])) { + $skip = 1; + last; + } + } + next if $skip; + + my $prettyname = $d->{NAME}; + + $prettyname =~ s/^$interface->{NAME}_//; + $prettyname =~ s/^$basename\_//; + + my $typeobject = $self->PythonFunctionStruct($basename, $d, $interface->{NAME}, $prettyname); + $self->register_module_typeobject($prettyname, $typeobject, $d->{ORIGINAL}); + + my ($infn, $outfn, $fndocstring) = $self->PythonFunction($d, $interface->{NAME}, $prettyname); + + push (@fns, [$infn, $outfn, "dcerpc_$d->{NAME}_r", $prettyname, $fndocstring, $d->{OPNUM}]); + } + + $self->pidl("const struct PyNdrRpcMethodDef py_ndr_$interface->{NAME}\_methods[] = {"); + $self->indent; + foreach my $d (@fns) { + my ($infn, $outfn, $callfn, $prettyname, $docstring, $opnum) = @$d; + $self->pidl("{ \"$prettyname\", $docstring, (py_dcerpc_call_fn)$callfn, (py_data_pack_fn)$infn, (py_data_unpack_fn)$outfn, $opnum, &ndr_table_$interface->{NAME} },"); + } + $self->pidl("{0}"); + $self->deindent; + $self->pidl("};"); + $self->pidl(""); + + $self->pidl("static PyObject *interface_$interface->{NAME}_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("return py_dcerpc_interface_init_helper(type, args, kwargs, &ndr_table_$interface->{NAME});"); + $self->deindent; + $self->pidl("}"); + + $self->pidl(""); + + my $signature = +"\"$interface->{NAME}(binding, lp_ctx=None, credentials=None) -> connection\\n\" +\"\\n\" +\"binding should be a DCE/RPC binding string (for example: ncacn_ip_tcp:127.0.0.1)\\n\" +\"lp_ctx should be a path to a smb.conf file or a param.LoadParm object\\n\" +\"credentials should be a credentials.Credentials object.\\n\\n\""; + + my $docstring = $self->DocString($interface, $interface->{NAME}); + + if ($docstring) { + $docstring = "$signature$docstring"; + } else { + $docstring = $signature; + } + + my $if_typename = "$interface->{NAME}_InterfaceType"; + + $self->pidl("static PyTypeObject $if_typename = {"); + $self->indent; + $self->pidl("PyVarObject_HEAD_INIT(NULL, 0)"); + $self->pidl(".tp_name = \"$basename.$interface->{NAME}\","); + $self->pidl(".tp_basicsize = sizeof(dcerpc_InterfaceObject),"); + $self->pidl(".tp_doc = $docstring,"); + $self->pidl(".tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,"); + $self->pidl(".tp_new = interface_$interface->{NAME}_new,"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + + $self->register_module_typeobject($interface->{NAME}, "&$if_typename", $interface->{ORIGINAL}); + my $dcerpc_typename = $self->import_type_variable("samba.dcerpc.base", "ClientConnection"); + $self->register_module_prereadycode(["$if_typename.tp_base = $dcerpc_typename;", ""]); + $self->register_module_postreadycode(["if (!PyInterface_AddNdrRpcMethods(&$if_typename, py_ndr_$interface->{NAME}\_methods))", "\treturn NULL;", ""]); + + + $self->pidl("static PyObject *syntax_$interface->{NAME}_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)"); + $self->pidl("{"); + $self->indent; + $self->pidl("return py_dcerpc_syntax_init_helper(type, args, kwargs, &ndr_table_$interface->{NAME}.syntax_id);"); + $self->deindent; + $self->pidl("}"); + + $self->pidl(""); + + $signature = "\"$interface->{NAME}_abstract_syntax()\\n\""; + + $docstring = $self->DocString($interface, $interface->{NAME}."_syntax"); + + if ($docstring) { + $docstring = "$signature$docstring"; + } else { + $docstring = $signature; + } + + my $syntax_typename = "$interface->{NAME}_SyntaxType"; + + $self->pidl("static PyTypeObject $syntax_typename = {"); + $self->indent; + $self->pidl("PyVarObject_HEAD_INIT(NULL, 0)"); + $self->pidl(".tp_name = \"$basename.$interface->{NAME}_abstract_syntax\","); + $self->pidl(".tp_doc = $docstring,"); + $self->pidl(".tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,"); + $self->pidl(".tp_new = syntax_$interface->{NAME}_new,"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + + $self->register_module_typeobject("$interface->{NAME}_abstract_syntax", "&$syntax_typename", $interface->{ORIGINAL}); + if (not defined($self->existing_module_object("abstract_syntax"))) { + # Only the first syntax gets registered with the legacy + # "abstract_syntax" name + $self->register_module_typeobject("abstract_syntax", "&$syntax_typename", $interface->{ORIGINAL}); + } + my $ndr_typename = $self->import_type_variable("samba.dcerpc.misc", "ndr_syntax_id"); + $self->register_module_prereadycode(["$syntax_typename.tp_base = $ndr_typename;", + "$syntax_typename.tp_basicsize = pytalloc_BaseObject_size();", + ""]); + } + + $self->pidl_hdr(""); +} + +sub register_module_method($$$$$) +{ + my ($self, $fn_name, $pyfn_name, $flags, $doc) = @_; + + push (@{$self->{module_methods}}, [$fn_name, $pyfn_name, $flags, $doc]) +} + +sub register_module_typeobject($$$$) +{ + my ($self, $name, $py_name, $location) = @_; + + $self->register_module_object($name, "(PyObject *)(void *)$py_name", $location); + + $self->check_ready_type($py_name); + + $self->register_patch_type_call($name, $py_name); +} + +sub check_ready_type($$) +{ + my ($self, $py_name) = @_; + push (@{$self->{ready_types}}, $py_name) unless (grep(/^$py_name$/,@{$self->{ready_types}})); +} + +sub register_module_import($$) +{ + my ($self, $module_path) = @_; + + my $var_name = $module_path; + $var_name =~ s/\./_/g; + $var_name = "dep_$var_name"; + + unless (defined $self->{module_imports_uniq}->{$var_name}) { + my $h = { "key" => $var_name, "val" => $module_path}; + push @{$self->{module_imports}}, $h; + $self->{module_imports_uniq}->{$var_name} = $h; + } + return $var_name; +} + +sub import_type_variable($$$) +{ + my ($self, $module, $name) = @_; + + $self->register_module_import($module); + unless (defined $self->{type_imports_uniq}->{$name}) { + my $h = { "key" => $name, "val" => $module}; + push @{$self->{type_imports}}, $h; + $self->{type_imports_uniq}->{$name} = $h; + } + return "$name\_Type"; +} + +sub use_type_variable($$) +{ + my ($self, $orig_ctype) = @_; + # FIXME: Have a global lookup table for types that look different on the + # wire than they are named in C? + if ($orig_ctype->{NAME} eq "dom_sid2" or + $orig_ctype->{NAME} eq "dom_sid28" or + $orig_ctype->{NAME} eq "dom_sid0") { + $orig_ctype->{NAME} = "dom_sid"; + } + if ($orig_ctype->{NAME} eq "spoolss_security_descriptor") { + $orig_ctype->{NAME} = "security_descriptor"; + } + + my $ctype = resolveType($orig_ctype); + unless (defined($ctype->{BASEFILE})) { + return undef; + } + # If this is an external type, make sure we do the right imports. + if (($ctype->{BASEFILE} ne $self->{BASENAME})) { + return $self->import_type_variable("samba.dcerpc.$ctype->{BASEFILE}", $ctype->{NAME}); + } + return "&$ctype->{NAME}_Type"; +} + +sub register_patch_type_call($$$) +{ + my ($self, $typename, $cvar) = @_; + + push(@{$self->{patch_type_calls}}, [$typename, $cvar]); + +} + +sub register_module_prereadycode($$) +{ + my ($self, $code) = @_; + + push (@{$self->{prereadycode}}, @$code); +} + +sub register_module_postreadycode($$) +{ + my ($self, $code) = @_; + + push (@{$self->{postreadycode}}, @$code); +} + +sub existing_module_object($$) +{ + my ($self, $name) = @_; + + if (defined($self->{module_object_uniq}->{$name})) { + return $self->{module_object_uniq}->{$name}; + } + + return undef; +} + +sub register_module_object($$$$) +{ + my ($self, $name, $py_name, $location) = @_; + + my $existing = $self->existing_module_object($name); + fatal($location, "module_object($name, $py_name) registered twice! $existing.") if defined($existing); + + push (@{$self->{module_objects}}, [$name, $py_name]); + $self->{module_object_uniq}->{$name} = $py_name; +} + +sub assign($$$) +{ + my ($self, $dest, $src) = @_; + if ($dest =~ /^\&/ and $src eq "NULL") { + $self->pidl("memset($dest, 0, sizeof(" . get_value_of($dest) . "));"); + } elsif ($dest =~ /^\&/) { + my $destvar = get_value_of($dest); + $self->pidl("$destvar = *$src;"); + } else { + $self->pidl("$dest = $src;"); + } +} + +sub ConvertStringFromPythonData($$$$$) +{ + my ($self, $mem_ctx, $py_var, $target, $fail) = @_; + + $self->pidl("{"); + $self->indent; + $self->pidl("const char *test_str;"); + $self->pidl("const char *talloc_str;"); + $self->pidl("PyObject *unicode = NULL;"); + $self->pidl("if (PyUnicode_Check($py_var)) {"); + $self->indent; + # FIXME: Use Unix charset setting rather than utf-8 + $self->pidl("unicode = PyUnicode_AsEncodedString($py_var, \"utf-8\", \"ignore\");"); + $self->pidl("if (unicode == NULL) {"); + $self->indent; + $self->pidl("$fail"); + $self->deindent; + $self->pidl("}"); + + $self->pidl("test_str = PyBytes_AS_STRING(unicode);"); + $self->deindent; + $self->pidl("} else if (PyBytes_Check($py_var)) {"); + $self->indent; + $self->pidl("test_str = PyBytes_AS_STRING($py_var);"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $self->pidl("PyErr_Format(PyExc_TypeError, \"Expected string or unicode object, got %s\", Py_TYPE($py_var)->tp_name);"); + $self->pidl("$fail"); + $self->deindent; + $self->pidl("}"); + $self->pidl("talloc_str = talloc_strdup($mem_ctx, test_str);"); + $self->pidl("if (unicode != NULL) {"); + $self->indent; + $self->pidl("Py_DECREF(unicode);"); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (talloc_str == NULL) {"); + $self->indent; + $self->pidl("PyErr_NoMemory();"); + $self->pidl("$fail"); + $self->deindent; + $self->pidl("}"); + $self->pidl("$target = talloc_str;"); + $self->deindent; + $self->pidl("}"); +} + +sub ConvertU16StringFromPythonData($$$$$) +{ + my ($self, $mem_ctx, $py_var, $target, $fail) = @_; + + $self->pidl("{"); + $self->indent; + $self->pidl("unsigned char *str = NULL;"); + $self->pidl(""); + $self->pidl("str = PyUtf16String_FromBytes("); + $self->pidl(" $mem_ctx, $py_var);"); + $self->pidl("if (str == NULL) {"); + $self->indent; + $self->pidl("$fail"); + $self->deindent; + $self->pidl("}"); + $self->pidl(""); + $self->pidl("$target = str;"); + $self->deindent; + $self->pidl("}"); +} + +sub ConvertObjectFromPythonData($$$$$$;$$) +{ + my ($self, $mem_ctx, $cvar, $ctype, $target, $fail, $location, $switch) = @_; + + fatal($location, "undef type for $cvar") unless(defined($ctype)); + + $ctype = resolveType($ctype); + + my $actual_ctype = $ctype; + if ($actual_ctype->{TYPE} eq "TYPEDEF") { + $actual_ctype = $actual_ctype->{DATA}; + } + + # We need to cover ENUMs, BITMAPS and SCALAR values here, as + # all could otherwise be assigned invalid integer values + my $ctype_alias = ""; + my $uint_max = ""; + if ($actual_ctype->{TYPE} eq "ENUM") { + # Importantly, ENUM values are unsigned in pidl, and + # typically map to uint32 + $ctype_alias = enum_type_fn($actual_ctype); + } elsif ($actual_ctype->{TYPE} eq "BITMAP") { + $ctype_alias = bitmap_type_fn($actual_ctype); + } elsif ($actual_ctype->{TYPE} eq "SCALAR") { + $ctype_alias = expandAlias($actual_ctype->{NAME}); + } + + # This is the unsigned Python Integer -> C integer validation + # case. The signed case is below. + if ($ctype_alias =~ /^(uint[0-9]*|hyper|udlong|udlongr + |NTTIME_hyper|NTTIME|NTTIME_1sec + |uid_t|gid_t)$/x) { + $self->pidl("{"); + $self->indent; + $self->pidl("const unsigned long long uint_max = ndr_sizeof2uintmax(sizeof($target));"); + $self->pidl("if (PyLong_Check($cvar)) {"); + $self->indent; + $self->pidl("unsigned long long test_var;"); + $self->pidl("test_var = PyLong_AsUnsignedLongLong($cvar);"); + $self->pidl("if (PyErr_Occurred() != NULL) {"); + $self->indent; + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (test_var > uint_max) {"); + $self->indent; + $self->pidl("PyErr_Format(PyExc_OverflowError, \"Expected type %s within range 0 - %llu, got %llu\","); + $self->pidl(" PyLong_Type.tp_name, uint_max, test_var);"); + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + $self->pidl("$target = test_var;"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $self->pidl("PyErr_Format(PyExc_TypeError, \"Expected type %s\","); + $self->pidl(" PyLong_Type.tp_name);"); + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + $self->deindent; + $self->pidl("}"); + return; + } + + # Confirm the signed python integer fits in the C type + # correctly. It is subtly different from the unsigned case + # above, so while it looks like a duplicate, it is not + # actually a duplicate. + if ($ctype_alias =~ /^(dlong|char|int[0-9]*|time_t)$/x) { + $self->pidl("{"); + $self->indent; + $self->pidl("const long long int_max = ndr_sizeof2intmax(sizeof($target));"); + $self->pidl("const long long int_min = -int_max - 1;"); + $self->pidl("if (PyLong_Check($cvar)) {"); + $self->indent; + $self->pidl("long long test_var;"); + $self->pidl("test_var = PyLong_AsLongLong($cvar);"); + $self->pidl("if (PyErr_Occurred() != NULL) {"); + $self->indent; + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + $self->pidl("if (test_var < int_min || test_var > int_max) {"); + $self->indent; + $self->pidl("PyErr_Format(PyExc_OverflowError, \"Expected type %s within range %lld - %lld, got %lld\","); + $self->pidl(" PyLong_Type.tp_name, int_min, int_max, test_var);"); + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + $self->pidl("$target = test_var;"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $self->pidl("PyErr_Format(PyExc_TypeError, \"Expected type %s\","); + $self->pidl(" PyLong_Type.tp_name);"); + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + $self->deindent; + $self->pidl("}"); + return; + } + + if ($actual_ctype->{TYPE} eq "STRUCT" or $actual_ctype->{TYPE} eq "INTERFACE") { + my $ctype_name = $self->use_type_variable($ctype); + unless (defined ($ctype_name)) { + error($location, "Unable to determine origin of type `" . mapTypeName($ctype) . "'"); + $self->pidl("PyErr_SetString(PyExc_TypeError, \"Can not convert C Type " . mapTypeName($ctype) . " from Python\");"); + return; + } + $self->pidl("PY_CHECK_TYPE($ctype_name, $cvar, $fail);"); + $self->pidl("if (talloc_reference($mem_ctx, pytalloc_get_mem_ctx($cvar)) == NULL) {"); + $self->indent; + $self->pidl("PyErr_NoMemory();"); + $self->pidl("$fail"); + $self->deindent; + $self->pidl("}"); + $self->assign($target, "(".mapTypeName($ctype)." *)pytalloc_get_ptr($cvar)"); + return; + } + + if ($actual_ctype->{TYPE} eq "UNION") { + my $ctype_name = $self->use_type_variable($ctype); + unless (defined ($ctype_name)) { + error($location, "Unable to determine origin of type `" . mapTypeName($ctype) . "'"); + $self->pidl("PyErr_SetString(PyExc_TypeError, \"Can not convert C Type " . mapTypeName($ctype) . " from Python\");"); + return; + } + my $export = "pyrpc_export_union($ctype_name, $mem_ctx, $switch, $cvar, \"".mapTypeName($ctype)."\")"; + $self->assign($target, "(".mapTypeName($ctype)." *)$export"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "DATA_BLOB") { + $self->pidl("$target = data_blob_talloc($mem_ctx, PyBytes_AS_STRING($cvar), PyBytes_GET_SIZE($cvar));"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and + ($actual_ctype->{NAME} eq "string" + or $actual_ctype->{NAME} eq "nbt_string" + or $actual_ctype->{NAME} eq "nbt_name" + or $actual_ctype->{NAME} eq "wrepl_nbt_name" + or $actual_ctype->{NAME} eq "dns_string" + or $actual_ctype->{NAME} eq "dnsp_string" + or $actual_ctype->{NAME} eq "dns_name" + or $actual_ctype->{NAME} eq "ipv4address" + or $actual_ctype->{NAME} eq "ipv6address" + or $actual_ctype->{NAME} eq "dnsp_name")) { + $self->ConvertStringFromPythonData($mem_ctx, $cvar, $target, $fail); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and + $actual_ctype->{NAME} eq "u16string") { + $self->ConvertU16StringFromPythonData($mem_ctx, $cvar, $target, $fail); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "NTSTATUS") { + $self->pidl("$target = NT_STATUS(PyLong_AsLong($cvar));"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "WERROR") { + $self->pidl("$target = W_ERROR(PyLong_AsLong($cvar));"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "HRESULT") { + $self->pidl("$target = HRES_ERROR(PyLong_AsLong($cvar));"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "string_array") { + $self->pidl("$target = pytalloc_get_ptr($cvar);"); + return; + } + + if ($actual_ctype->{TYPE} eq "SCALAR" and $actual_ctype->{NAME} eq "pointer") { + $self->assign($target, "pytalloc_get_ptr($cvar)"); + return; + } + + fatal($location, "unknown type `$actual_ctype->{TYPE}' for ".mapTypeName($ctype) . ": $cvar"); + +} + +sub ConvertObjectFromPythonLevel($$$$$$$$$) +{ + my ($self, $env, $mem_ctx, $py_var, $e, $l, $var_name, $fail, $recurse) = @_; + my $nl = GetNextLevel($e, $l); + if ($nl and $nl->{TYPE} eq "SUBCONTEXT") { + $nl = GetNextLevel($e, $nl); + } + my $pl = GetPrevLevel($e, $l); + if ($pl and $pl->{TYPE} eq "SUBCONTEXT") { + $pl = GetPrevLevel($e, $pl); + } + + if ($recurse == 0) { + $self->pidl("if ($py_var == NULL) {"); + $self->indent; + $self->pidl("PyErr_Format(PyExc_AttributeError, \"Cannot delete NDR object: " . + mapTypeName($var_name) . "\");"); + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + } + $recurse = $recurse + 1; + + if ($l->{TYPE} eq "POINTER") { + my $need_deindent = 0; + my $need_deref = 0; + + if ($l->{POINTER_TYPE} ne "ref") { + $self->pidl("if ($py_var == Py_None) {"); + $self->indent; + $self->pidl("$var_name = NULL;"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $need_deindent = 1; + if ($nl->{TYPE} eq "POINTER") { + $need_deref = 1; + } + } + + if ($l->{POINTER_TYPE} eq "ref" or $need_deref == 1) { + $self->pidl("$var_name = talloc_ptrtype($mem_ctx, $var_name);"); + $self->pidl("if ($var_name == NULL) {"); + $self->indent; + $self->pidl("PyErr_NoMemory();"); + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + } elsif ($nl->{TYPE} eq "DATA" and Parse::Pidl::Typelist::is_scalar($nl->{DATA_TYPE}) + and not Parse::Pidl::Typelist::scalar_is_reference($nl->{DATA_TYPE})) { + $self->pidl("$var_name = talloc_ptrtype($mem_ctx, $var_name);"); + $self->pidl("if ($var_name == NULL) {"); + $self->indent; + $self->pidl("PyErr_NoMemory();"); + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + } else { + $self->pidl("$var_name = NULL;"); + } + if ($need_deref == 1) { + my $ndr_pointer_typename = $self->import_type_variable("samba.dcerpc.base", "ndr_pointer"); + $self->pidl("$py_var = py_dcerpc_ndr_pointer_deref($ndr_pointer_typename, $py_var);"); + $self->pidl("if ($py_var == NULL) {"); + $self->indent; + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); + } + unless ($nl->{TYPE} eq "DATA" and Parse::Pidl::Typelist::scalar_is_reference($nl->{DATA_TYPE})) { + $var_name = get_value_of($var_name); + } + $self->ConvertObjectFromPythonLevel($env, $mem_ctx, $py_var, $e, $nl, $var_name, $fail, $recurse); + if ($need_deindent == 1) { + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "ARRAY") { + if ($pl && $pl->{TYPE} eq "POINTER") { + $var_name = get_pointer_to($var_name); + } + + if (is_charset_array($e, $l)) { + $self->ConvertStringFromPythonData($mem_ctx, $py_var, $var_name, $fail); + } else { + my $counter = "$e->{NAME}_cntr_$l->{LEVEL_INDEX}"; + $self->pidl("PY_CHECK_TYPE(&PyList_Type, $py_var, $fail);"); + $self->pidl("{"); + $self->indent; + $self->pidl("int $counter;"); + if (ArrayDynamicallyAllocated($e, $l)) { + $self->pidl("$var_name = talloc_array_ptrtype($mem_ctx, $var_name, PyList_GET_SIZE($py_var));"); + $self->pidl("if (!$var_name) { $fail }"); + $self->pidl("talloc_set_name_const($var_name, \"ARRAY: $var_name\");"); + } else { + $self->pidl("if (ARRAY_SIZE($var_name) != PyList_GET_SIZE($py_var)) {"); + $self->indent; + $self->pidl("PyErr_Format(PyExc_TypeError, \"Expected list of type %s, length %zu, got %zd\", Py_TYPE($py_var)->tp_name, ARRAY_SIZE($var_name), PyList_GET_SIZE($py_var));"); + $self->pidl("$fail"); + $self->deindent; + $self->pidl("}"); + } + $self->pidl("for ($counter = 0; $counter < PyList_GET_SIZE($py_var); $counter++) {"); + $self->indent; + if (ArrayDynamicallyAllocated($e, $l)) { + $self->ConvertObjectFromPythonLevel($env, $var_name, "PyList_GET_ITEM($py_var, $counter)", $e, $nl, "($var_name)"."[$counter]", $fail, 0); + } else { + $self->ConvertObjectFromPythonLevel($env, $mem_ctx, "PyList_GET_ITEM($py_var, $counter)", $e, $nl, "($var_name)"."[$counter]", $fail, 0); + } + $self->deindent; + $self->pidl("}"); + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "DATA") { + if (not Parse::Pidl::Typelist::is_scalar($l->{DATA_TYPE})) { + $var_name = get_pointer_to($var_name); + } + $self->ConvertObjectFromPythonData($mem_ctx, $py_var, $l->{DATA_TYPE}, $var_name, $fail, $e->{ORIGINAL}); + } elsif ($l->{TYPE} eq "SWITCH") { + $var_name = get_pointer_to($var_name); + my $switch = ParseExpr($l->{SWITCH_IS}, $env, $e); + my $switch_ptr = "$e->{NAME}_switch_$l->{LEVEL_INDEX}"; + $self->pidl("{"); + $self->indent; + my $union_type = mapTypeName($nl->{DATA_TYPE}); + $self->pidl("$union_type *$switch_ptr;"); + $self->ConvertObjectFromPythonData($mem_ctx, $py_var, $nl->{DATA_TYPE}, $switch_ptr, $fail, $e->{ORIGINAL}, $switch); + $self->fail_on_null($switch_ptr, $fail); + $self->assign($var_name, "$switch_ptr"); + $self->deindent; + $self->pidl("}"); + } elsif ($l->{TYPE} eq "SUBCONTEXT") { + $self->ConvertObjectFromPythonLevel($env, $mem_ctx, $py_var, $e, $nl, $var_name, $fail, $recurse); + } else { + fatal($e->{ORIGINAL}, "unknown level type $l->{TYPE}"); + } +} + +sub ConvertObjectFromPython($$$$$$$) +{ + my ($self, $env, $mem_ctx, $ctype, $cvar, $target, $fail) = @_; + my $recurse = 0; + + $self->ConvertObjectFromPythonLevel($env, $mem_ctx, $cvar, $ctype, $ctype->{LEVELS}[0], $target, $fail, $recurse); +} + +sub ConvertScalarToPython($$$$) +{ + my ($self, $ctypename, $cvar, $mem_ctx) = @_; + + die("expected string for $cvar, not $ctypename") if (ref($ctypename) eq "HASH"); + + $ctypename = expandAlias($ctypename); + + if ($ctypename =~ /^(int64|dlong)$/) { + return "PyLong_FromLongLong($cvar)"; + } + + if ($ctypename =~ /^(uint64|hyper|NTTIME_hyper|NTTIME|NTTIME_1sec|udlong|udlongr|uid_t|gid_t)$/) { + return "PyLong_FromUnsignedLongLong($cvar)"; + } + + if ($ctypename =~ /^(char|int|int8|int16|int32|time_t)$/) { + return "PyLong_FromLong($cvar)"; + } + + # Needed to ensure unsigned values in a 32 or 16 bit enum is + # cast correctly to a uint32_t, not sign extended to a a + # possibly 64 bit unsigned long. (enums are signed in C, + # unsigned in NDR) + if ($ctypename =~ /^(uint32|uint3264)$/) { + return "PyLong_FromUnsignedLongLong((uint32_t)($cvar))"; + } + + if ($ctypename =~ /^(uint|uint8|uint16|uint1632)$/) { + return "PyLong_FromLong((uint16_t)($cvar))"; + } + + if ($ctypename eq "DATA_BLOB") { + return "PyBytes_FromStringAndSize((char *)($cvar).data, ($cvar).length)"; + } + + if ($ctypename eq "NTSTATUS") { + return "PyErr_FromNTSTATUS($cvar)"; + } + + if ($ctypename eq "WERROR") { + return "PyErr_FromWERROR($cvar)"; + } + + if ($ctypename eq "HRESULT") { + return "PyErr_FromHRESULT($cvar)"; + } + + if (($ctypename eq "string" or $ctypename eq "nbt_string" or $ctypename eq "nbt_name" or $ctypename eq "wrepl_nbt_name")) { + return "PyString_FromStringOrNULL($cvar)"; + } + + if (($ctypename eq "dns_string" or $ctypename eq "dns_name")) { + return "PyString_FromStringOrNULL($cvar)"; + } + + if ($ctypename eq "u16string") { + return "PyBytes_FromUtf16StringOrNULL($cvar)"; + } + + # Not yet supported + if ($ctypename eq "string_array") { + return "pytalloc_GenericObject_reference_ex($mem_ctx, $cvar)"; + } + if ($ctypename eq "ipv4address") { return "PyString_FromStringOrNULL($cvar)"; } + if ($ctypename eq "ipv6address") { return "PyString_FromStringOrNULL($cvar)"; } + if ($ctypename eq "dnsp_name") { return "PyString_FromStringOrNULL($cvar)"; } + if ($ctypename eq "dnsp_string") { return "PyString_FromStringOrNULL($cvar)"; } + if ($ctypename eq "pointer") { + return "pytalloc_GenericObject_reference_ex($mem_ctx, $cvar)"; + } + + die("Unknown scalar type $ctypename"); +} + +sub ConvertObjectToPythonData($$$$$;$$) +{ + my ($self, $mem_ctx, $ctype, $cvar, $location, $switch) = @_; + + die("undef type for $cvar") unless(defined($ctype)); + + $ctype = resolveType($ctype); + + my $actual_ctype = $ctype; + if ($actual_ctype->{TYPE} eq "TYPEDEF") { + $actual_ctype = $actual_ctype->{DATA}; + } + + if ($actual_ctype->{TYPE} eq "ENUM") { + return $self->ConvertScalarToPython(Parse::Pidl::Typelist::enum_type_fn($actual_ctype), $cvar, $mem_ctx); + } elsif ($actual_ctype->{TYPE} eq "BITMAP") { + return $self->ConvertScalarToPython(Parse::Pidl::Typelist::bitmap_type_fn($actual_ctype), $cvar, $mem_ctx); + } elsif ($actual_ctype->{TYPE} eq "SCALAR") { + return $self->ConvertScalarToPython($actual_ctype->{NAME}, $cvar, $mem_ctx); + } elsif ($actual_ctype->{TYPE} eq "UNION") { + my $ctype_name = $self->use_type_variable($ctype); + unless (defined($ctype_name)) { + error($location, "Unable to determine origin of type `" . mapTypeName($ctype) . "'"); + return "NULL"; # FIXME! + } + return "pyrpc_import_union($ctype_name, $mem_ctx, $switch, $cvar, \"".mapTypeName($ctype)."\")"; + } elsif ($actual_ctype->{TYPE} eq "STRUCT" or $actual_ctype->{TYPE} eq "INTERFACE") { + my $ctype_name = $self->use_type_variable($ctype); + unless (defined($ctype_name)) { + error($location, "Unable to determine origin of type `" . mapTypeName($ctype) . "'"); + return "NULL"; # FIXME! + } + return "pytalloc_reference_ex($ctype_name, $mem_ctx, $cvar)"; + } + + fatal($location, "unknown type $actual_ctype->{TYPE} for ".mapTypeName($ctype) . ": $cvar"); +} + +sub fail_on_null($$$) +{ + my ($self, $var, $fail) = @_; + $self->pidl("if ($var == NULL) {"); + $self->indent; + $self->pidl($fail); + $self->deindent; + $self->pidl("}"); +} + +sub ConvertObjectToPythonLevel($$$$$$$) +{ + my ($self, $mem_ctx, $env, $e, $l, $var_name, $py_var, $fail, $recurse) = @_; + my $nl = GetNextLevel($e, $l); + if ($nl and $nl->{TYPE} eq "SUBCONTEXT") { + $nl = GetNextLevel($e, $nl); + } + my $pl = GetPrevLevel($e, $l); + if ($pl and $pl->{TYPE} eq "SUBCONTEXT") { + $pl = GetPrevLevel($e, $pl); + } + + if ($l->{TYPE} eq "POINTER") { + my $need_wrap = 0; + if ($l->{POINTER_TYPE} ne "ref" and $nl->{TYPE} eq "POINTER") { + $need_wrap = 1; + } + if ($l->{POINTER_TYPE} ne "ref") { + if ($recurse == 0) { + $self->pidl("if ($var_name == NULL) {"); + $self->indent; + $self->pidl("$py_var = Py_None;"); + $self->pidl("Py_INCREF($py_var);"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + } else { + $self->pidl("{"); + $self->indent; + } + $recurse = $recurse + 1; + } + my $var_name2 = $var_name; + my $recurse2 = $recurse; + unless ($nl->{TYPE} eq "DATA" and Parse::Pidl::Typelist::scalar_is_reference($nl->{DATA_TYPE})) { + $var_name2 = get_value_of($var_name); + $recurse2 = 0; + } + $self->ConvertObjectToPythonLevel($var_name, $env, $e, $nl, $var_name2, $py_var, $fail, $recurse2); + if ($l->{POINTER_TYPE} ne "ref") { + $self->deindent; + $self->pidl("}"); + } + if ($need_wrap) { + my $py_var_wrap = undef; + $need_wrap = 1; + $self->pidl("{"); + $self->indent; + $py_var_wrap = "py_$e->{NAME}_level_$l->{LEVEL_INDEX}"; + $self->pidl("PyObject *$py_var_wrap = $py_var;"); + my $ndr_pointer_typename = $self->import_type_variable("samba.dcerpc.base", "ndr_pointer"); + $self->pidl("$py_var = py_dcerpc_ndr_pointer_wrap($ndr_pointer_typename, $py_var_wrap);"); + $self->pidl("Py_XDECREF($py_var_wrap);"); + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "ARRAY") { + if ($pl && $pl->{TYPE} eq "POINTER") { + $var_name = get_pointer_to($var_name); + } + + if (is_charset_array($e, $l)) { + # FIXME: Use Unix charset setting rather than utf-8 + $self->pidl("if ($var_name == NULL) {"); + $self->indent; + $self->pidl("$py_var = Py_None;"); + $self->pidl("Py_INCREF($py_var);"); + $self->deindent; + $self->pidl("} else {"); + $self->indent; + $self->pidl("$py_var = PyUnicode_Decode($var_name, strlen($var_name), \"utf-8\", \"ignore\");"); + $self->deindent; + $self->pidl("}"); + } else { + die("No SIZE_IS for array $var_name") unless (defined($l->{SIZE_IS})); + my $length = $l->{SIZE_IS}; + if (defined($l->{LENGTH_IS})) { + $length = $l->{LENGTH_IS}; + } + + $length = ParseExpr($length, $env, $e); + $self->pidl("$py_var = PyList_New($length);"); + $self->fail_on_null($py_var, $fail); + $self->pidl("{"); + $self->indent; + my $counter = "$e->{NAME}_cntr_$l->{LEVEL_INDEX}"; + $self->pidl("int $counter;"); + $self->pidl("for ($counter = 0; $counter < ($length); $counter++) {"); + $self->indent; + my $member_var = "py_$e->{NAME}_$l->{LEVEL_INDEX}"; + $self->pidl("PyObject *$member_var;"); + if (ArrayDynamicallyAllocated($e, $l)) { + $self->ConvertObjectToPythonLevel($var_name, $env, $e, $nl, "($var_name)"."[$counter]", $member_var, $fail, $recurse); + } else { + $self->ConvertObjectToPythonLevel($mem_ctx, $env, $e, $nl, "($var_name)"."[$counter]", $member_var, $fail, $recurse); + } + $self->pidl("PyList_SetItem($py_var, $counter, $member_var);"); + $self->deindent; + $self->pidl("}"); + $self->deindent; + $self->pidl("}"); + } + } elsif ($l->{TYPE} eq "SWITCH") { + $var_name = get_pointer_to($var_name); + my $switch = ParseExpr($l->{SWITCH_IS}, $env, $e); + my $conv = $self->ConvertObjectToPythonData($mem_ctx, $nl->{DATA_TYPE}, $var_name, $e->{ORIGINAL}, $switch); + $self->pidl("$py_var = $conv;"); + $self->fail_on_null($py_var, $fail); + + } elsif ($l->{TYPE} eq "DATA") { + if (not Parse::Pidl::Typelist::is_scalar($l->{DATA_TYPE})) { + $var_name = get_pointer_to($var_name); + } + my $conv = $self->ConvertObjectToPythonData($mem_ctx, $l->{DATA_TYPE}, $var_name, $e->{ORIGINAL}); + $self->pidl("$py_var = $conv;"); + } elsif ($l->{TYPE} eq "SUBCONTEXT") { + $self->ConvertObjectToPythonLevel($mem_ctx, $env, $e, $nl, $var_name, $py_var, $fail, $recurse); + } else { + fatal($e->{ORIGINAL}, "Unknown level type $l->{TYPE} $var_name"); + } +} + +sub ConvertObjectToPython($$$$$$) +{ + my ($self, $mem_ctx, $env, $ctype, $cvar, $py_var, $fail) = @_; + my $recurse = 0; + + $self->ConvertObjectToPythonLevel($mem_ctx, $env, $ctype, $ctype->{LEVELS}[0], $cvar, $py_var, $fail, $recurse); +} + +sub Parse($$$$$) +{ + my($self,$basename,$ndr,$ndr_hdr,$hdr) = @_; + + $self->{BASENAME} = $basename; + + my $ndr_hdr_include = ""; + if (defined($ndr_hdr)) { + $ndr_hdr_include = "#include \"$ndr_hdr\""; + } + $self->pidl_hdr(" +/* Python wrapper functions auto-generated by pidl */ +#define PY_SSIZE_T_CLEAN 1 /* We use Py_ssize_t for PyArg_ParseTupleAndKeywords */ +#include \"lib/replace/system/python.h\" +#include \"python/py3compat.h\" +#include \"includes.h\" +#include \"python/modules.h\" +#include +#include \"librpc/rpc/pyrpc.h\" +#include \"librpc/rpc/pyrpc_util.h\" +#include \"$hdr\" +$ndr_hdr_include + +/* + * Suppress compiler warnings if the generated code does not call these + * functions + */ +#ifndef _MAYBE_UNUSED_ +#ifdef __has_attribute +#if __has_attribute(unused) +#define _MAYBE_UNUSED_ __attribute__ ((unused)) +#else +#define _MAYBE_UNUSED_ +#endif +#endif +#endif +/* + * These functions are here to ensure they can be optimized out by + * the compiler based on the constant input values + */ + +static inline unsigned long long ndr_sizeof2uintmax(size_t var_size) +{ + switch (var_size) { + case 8: + return UINT64_MAX; + case 4: + return UINT32_MAX; + case 2: + return UINT16_MAX; + case 1: + return UINT8_MAX; + } + + return 0; +} + +static inline _MAYBE_UNUSED_ long long ndr_sizeof2intmax(size_t var_size) +{ + switch (var_size) { + case 8: + return INT64_MAX; + case 4: + return INT32_MAX; + case 2: + return INT16_MAX; + case 1: + return INT8_MAX; + } + + return 0; +} +"); + + foreach my $x (@$ndr) { + ($x->{TYPE} eq "IMPORT") && $self->Import(@{$x->{PATHS}}); + ($x->{TYPE} eq "INTERFACE") && $self->Interface($x, $basename); + } + + $self->pidl("static PyMethodDef $basename\_methods[] = {"); + $self->indent; + foreach (@{$self->{module_methods}}) { + my ($fn_name, $pyfn_name, $flags, $doc) = @$_; + $self->pidl("{ \"$fn_name\", (PyCFunction)$pyfn_name, $flags, $doc },"); + } + + $self->pidl("{ NULL, NULL, 0, NULL }"); + $self->deindent; + $self->pidl("};"); + + $self->pidl(""); + + $self->pidl("static struct PyModuleDef moduledef = {"); + $self->indent; + $self->pidl("PyModuleDef_HEAD_INIT,"); + $self->pidl(".m_name = \"$basename\","); + $self->pidl(".m_doc = \"$basename DCE/RPC\","); + $self->pidl(".m_size = -1,"); + $self->pidl(".m_methods = $basename\_methods,"); + $self->deindent; + $self->pidl("};"); + + $self->pidl("MODULE_INIT_FUNC($basename)"); + $self->pidl("{"); + $self->indent; + $self->pidl("PyObject *m = NULL;"); + foreach my $h (@{$self->{module_imports}}) { + $self->pidl("PyObject *$h->{'key'} = NULL;"); + } + $self->pidl(""); + + foreach my $h (@{$self->{module_imports}}) { + my $var_name = $h->{'key'}; + my $module_path = $h->{'val'}; + $self->pidl("$var_name = PyImport_ImportModule(\"$module_path\");"); + $self->pidl("if ($var_name == NULL)"); + $self->pidl("\tgoto out;"); + $self->pidl(""); + } + + foreach my $h (@{$self->{type_imports}}) { + my $type_var = "$h->{'key'}\_Type"; + my $module_path = $h->{'val'}; + $self->pidl_hdr("static PyTypeObject *$type_var;"); + my $pretty_name = PrettifyTypeName($h->{'key'}, $module_path); + my $module_var = "dep_$module_path"; + $module_var =~ s/\./_/g; + $self->pidl("$type_var = (PyTypeObject *)PyObject_GetAttrString($module_var, \"$pretty_name\");"); + $self->pidl("if ($type_var == NULL)"); + $self->pidl("\tgoto out;"); + $self->pidl(""); + } + + $self->pidl($_) foreach (@{$self->{prereadycode}}); + + foreach (@{$self->{ready_types}}) { + $self->pidl("if (PyType_Ready($_) < 0)"); + $self->pidl("\tgoto out;"); + } + + $self->pidl($_) foreach (@{$self->{postreadycode}}); + + foreach (@{$self->{patch_type_calls}}) { + my ($typename, $cvar) = @$_; + $self->pidl("#ifdef PY_".uc($typename)."_PATCH"); + $self->pidl("PY_".uc($typename)."_PATCH($cvar);"); + $self->pidl("#endif"); + } + + $self->pidl(""); + + $self->pidl("m = PyModule_Create(&moduledef);"); + $self->pidl("if (m == NULL)"); + $self->pidl("\tgoto out;"); + $self->pidl(""); + foreach my $h (@{$self->{constants}}) { + my $pretty_name = PrettifyTypeName($h->{'key'}, $basename); + my $py_obj; + my ($ctype, $cvar) = @{$h->{'val'}}; + if ($cvar =~ /^[0-9]+$/ or $cvar =~ /^0x[0-9a-fA-F]+$/) { + $py_obj = "PyLong_FromUnsignedLongLong($cvar)"; + } elsif ($cvar =~ /^".*"$/) { + $py_obj = "PyUnicode_FromString($cvar)"; + } else { + $py_obj = $self->ConvertObjectToPythonData("NULL", expandAlias($ctype), $cvar, undef); + } + + $self->pidl("PyModule_AddObject(m, \"$pretty_name\", $py_obj);"); + } + + foreach (@{$self->{module_objects}}) { + my ($object_name, $c_name) = @$_; + $self->pidl("Py_INCREF($c_name);"); + $self->pidl("PyModule_AddObject(m, \"$object_name\", $c_name);"); + } + + $self->pidl("#ifdef PY_MOD_".uc($basename)."_PATCH"); + $self->pidl("PY_MOD_".uc($basename)."_PATCH(m);"); + $self->pidl("#endif"); + $self->pidl("out:"); + foreach my $h (@{$self->{module_imports}}) { + my $mod_var = $h->{'key'}; + $self->pidl("Py_XDECREF($mod_var);"); + } + $self->pidl("return m;"); + $self->pidl(""); + $self->deindent; + $self->pidl("}"); + return ($self->{res_hdr} . $self->{res}); +} + +1; diff --git a/pidl/lib/Parse/Pidl/Samba4/TDR.pm b/pidl/lib/Parse/Pidl/Samba4/TDR.pm new file mode 100644 index 0000000..c328287 --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/TDR.pm @@ -0,0 +1,278 @@ +################################################### +# Trivial Parser Generator +# Copyright jelmer@samba.org 2005-2007 +# released under the GNU GPL + +package Parse::Pidl::Samba4::TDR; +use Parse::Pidl qw(fatal); +use Parse::Pidl::Util qw(has_property ParseExpr is_constant); +use Parse::Pidl::Samba4 qw(is_intree choose_header); +use Parse::Pidl::Typelist qw(mapTypeName); + +use base Parse::Pidl::Base; + +use vars qw($VERSION); +$VERSION = '0.01'; + +use strict; +use warnings; + +sub new($) { + my ($class) = shift; + my $self = { res => "", res_hdr => "", tabs => "" }; + bless($self, $class); +} + +sub typearg($) { + my $t = shift; + return(", const char *name") if ($t eq "print"); + return(", TALLOC_CTX *mem_ctx") if ($t eq "pull"); + return(""); +} + +sub fn_declare($$$) +{ + my ($self, $p, $d) = @_; + if ($p) { + $self->pidl($d); $self->pidl_hdr("$d;"); + } else { + $self->pidl("static $d"); + } +} + +sub ContainsArray($) +{ + my $e = shift; + foreach (@{$e->{ELEMENTS}}) { + next if (has_property($_, "charset") and + scalar(@{$_->{ARRAY_LEN}}) == 1); + return 1 if (defined($_->{ARRAY_LEN}) and + scalar(@{$_->{ARRAY_LEN}}) > 0); + } + return 0; +} + +sub ParserElement($$$$) +{ + my ($self, $e,$t,$env) = @_; + my $switch = ""; + my $array = ""; + my $name = ""; + my $mem_ctx = "mem_ctx"; + + fatal($e,"Pointers not supported in TDR") if ($e->{POINTERS} > 0); + fatal($e,"size_is() not supported in TDR") if (has_property($e, "size_is")); + fatal($e,"length_is() not supported in TDR") if (has_property($e, "length_is")); + + if ($t eq "print") { + $name = ", \"$e->{NAME}\"$array"; + } + + if (has_property($e, "flag")) { + $self->pidl("{"); + $self->indent; + $self->pidl("uint32_t saved_flags = tdr->flags;"); + $self->pidl("tdr->flags |= $e->{PROPERTIES}->{flag};"); + } + + if (has_property($e, "charset")) { + fatal($e,"charset() on non-array element") unless (defined($e->{ARRAY_LEN}) and scalar(@{$e->{ARRAY_LEN}}) > 0); + + my $len = ParseExpr(@{$e->{ARRAY_LEN}}[0], $env, $e); + if ($len eq "*") { $len = "-1"; } + $name = ", mem_ctx" if ($t eq "pull"); + $self->pidl("TDR_CHECK(tdr_$t\_charset(tdr$name, &v->$e->{NAME}, $len, sizeof($e->{TYPE}_t), CH_$e->{PROPERTIES}->{charset}));"); + return; + } + + if (has_property($e, "switch_is")) { + $switch = ", " . ParseExpr($e->{PROPERTIES}->{switch_is}, $env, $e); + } + + if (defined($e->{ARRAY_LEN}) and scalar(@{$e->{ARRAY_LEN}}) > 0) { + my $len = ParseExpr($e->{ARRAY_LEN}[0], $env, $e); + + if ($t eq "pull" and not is_constant($len)) { + $self->pidl("TDR_ALLOC(mem_ctx, v->$e->{NAME}, $len);"); + $mem_ctx = "v->$e->{NAME}"; + } + + $self->pidl("for (i = 0; i < $len; i++) {"); + $self->indent; + $array = "[i]"; + } + + if ($t eq "pull") { + $name = ", $mem_ctx"; + } + + if (has_property($e, "value") && $t eq "push") { + $self->pidl("v->$e->{NAME} = ".ParseExpr($e->{PROPERTIES}->{value}, $env, $e).";"); + } + + $self->pidl("TDR_CHECK(tdr_$t\_$e->{TYPE}(tdr$name$switch, &v->$e->{NAME}$array));"); + + if ($array) { $self->deindent; $self->pidl("}"); } + + if (has_property($e, "flag")) { + $self->pidl("tdr->flags = saved_flags;"); + $self->deindent; + $self->pidl("}"); + } +} + +sub ParserStruct($$$$$) +{ + my ($self, $e,$t,$p) = @_; + + $self->fn_declare($p,"NTSTATUS tdr_$t\_$e->{NAME} (struct tdr_$t *tdr".typearg($t).", struct $e->{NAME} *v)"); + $self->pidl("{"); $self->indent; + $self->pidl("int i;") if (ContainsArray($e)); + + if ($t eq "print") { + $self->pidl("tdr->print(tdr, \"\%-25s: struct $e->{NAME}\", name);"); + $self->pidl("tdr->level++;"); + } + + my %env = map { $_->{NAME} => "v->$_->{NAME}" } @{$e->{ELEMENTS}}; + $env{"this"} = "v"; + $self->ParserElement($_, $t, \%env) foreach (@{$e->{ELEMENTS}}); + + if ($t eq "print") { + $self->pidl("tdr->level--;"); + } + + $self->pidl("return NT_STATUS_OK;"); + + $self->deindent; $self->pidl("}"); +} + +sub ParserUnion($$$$) +{ + my ($self, $e,$t,$p) = @_; + + $self->fn_declare($p,"NTSTATUS tdr_$t\_$e->{NAME}(struct tdr_$t *tdr".typearg($t).", int level, union $e->{NAME} *v)"); + $self->pidl("{"); $self->indent; + $self->pidl("int i;") if (ContainsArray($e)); + + if ($t eq "print") { + $self->pidl("tdr->print(tdr, \"\%-25s: union $e->{NAME}\", name);"); + $self->pidl("tdr->level++;"); + } + + $self->pidl("switch (level) {"); $self->indent; + foreach (@{$e->{ELEMENTS}}) { + if (has_property($_, "case")) { + $self->pidl("case " . $_->{PROPERTIES}->{case} . ":"); + } elsif (has_property($_, "default")) { + $self->pidl("default:"); + } + $self->indent; $self->ParserElement($_, $t, {}); $self->deindent; + $self->pidl("break;"); + } + $self->deindent; $self->pidl("}"); + + if ($t eq "print") { + $self->pidl("tdr->level--;"); + } + + $self->pidl("return NT_STATUS_OK;\n"); + $self->deindent; $self->pidl("}"); +} + +sub ParserBitmap($$$$) +{ + my ($self,$e,$t,$p) = @_; + return if ($p); + $self->pidl("#define tdr_$t\_$e->{NAME} tdr_$t\_" . Parse::Pidl::Typelist::bitmap_type_fn($e)); +} + +sub ParserEnum($$$$) +{ + my ($self,$e,$t,$p) = @_; + my $bt = Parse::Pidl::Typelist::enum_type_fn($e); + my $mt = mapTypeName($bt); + + $self->fn_declare($p, "NTSTATUS tdr_$t\_$e->{NAME} (struct tdr_$t *tdr".typearg($t).", enum $e->{NAME} *v)"); + $self->pidl("{"); + if ($t eq "pull") { + $self->pidl("\t$mt r;"); + $self->pidl("\tTDR_CHECK(tdr_$t\_$bt(tdr, mem_ctx, \&r));"); + $self->pidl("\t*v = r;"); + } elsif ($t eq "push") { + $self->pidl("\tTDR_CHECK(tdr_$t\_$bt(tdr, ($mt *)v));"); + } elsif ($t eq "print") { + $self->pidl("\t/* FIXME */"); + } + $self->pidl("\treturn NT_STATUS_OK;"); + $self->pidl("}"); +} + +sub ParserTypedef($$$$) +{ + my ($self, $e,$t,$p) = @_; + + $self->ParserType($e->{DATA},$t); +} + +sub ParserType($$$) +{ + my ($self, $e,$t) = @_; + + return if (has_property($e, "no$t")); + + my $handlers = { + STRUCT => \&ParserStruct, UNION => \&ParserUnion, + ENUM => \&ParserEnum, BITMAP => \&ParserBitmap, + TYPEDEF => \&ParserTypedef + }; + + $handlers->{$e->{TYPE}}->($self, $e, $t, has_property($e, "public")) + if (defined($handlers->{$e->{TYPE}})); + + $self->pidl(""); +} + +sub ParserInterface($$) +{ + my ($self,$x) = @_; + + $self->pidl_hdr("#ifndef __TDR_$x->{NAME}_HEADER__"); + $self->pidl_hdr("#define __TDR_$x->{NAME}_HEADER__"); + + foreach (@{$x->{DATA}}) { + $self->ParserType($_, "pull"); + $self->ParserType($_, "push"); + $self->ParserType($_, "print"); + } + + $self->pidl_hdr("#endif /* __TDR_$x->{NAME}_HEADER__ */"); +} + +sub Parser($$$$) +{ + my ($self,$idl,$hdrname,$baseheader) = @_; + $self->pidl("/* autogenerated by pidl */"); + if (is_intree()) { + $self->pidl("#include \"includes.h\""); + } else { + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + $self->pidl("#include "); + } + $self->pidl("#include \"$hdrname\""); + $self->pidl(""); + $self->pidl_hdr("/* autogenerated by pidl */"); + $self->pidl_hdr("#include \"$baseheader\""); + $self->pidl_hdr(choose_header("lib/tdr/tdr.h", "tdr.h")); + $self->pidl_hdr(""); + + foreach (@$idl) { $self->ParserInterface($_) if ($_->{TYPE} eq "INTERFACE"); } + return ($self->{res_hdr}, $self->{res}); +} + +1; diff --git a/pidl/lib/Parse/Pidl/Samba4/Template.pm b/pidl/lib/Parse/Pidl/Samba4/Template.pm new file mode 100644 index 0000000..6a6755b --- /dev/null +++ b/pidl/lib/Parse/Pidl/Samba4/Template.pm @@ -0,0 +1,104 @@ +################################################### +# server template function generator +# Copyright tridge@samba.org 2003 +# released under the GNU GPL + +package Parse::Pidl::Samba4::Template; + +use vars qw($VERSION); +$VERSION = '0.01'; + +use Parse::Pidl::Util qw(genpad); + +use strict; +use warnings; + +my($res); + +##################################################################### +# produce boilerplate code for a interface +sub Template($) +{ + my($interface) = shift; + my($data) = $interface->{DATA}; + my $name = $interface->{NAME}; + + $res .= +"/* + Unix SMB/CIFS implementation. + + endpoint server for the $name pipe + + Copyright (C) YOUR NAME HERE YEAR + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include \"includes.h\" +#include \"rpc_server/dcerpc_server.h\" +#include \"librpc/gen_ndr/ndr_$name.h\" +#include \"rpc_server/common/common.h\" + +"; + + foreach my $d (@{$data}) { + if ($d->{TYPE} eq "FUNCTION") { + my $fname = $d->{NAME}; + my $pad = genpad("static $d->{RETURN_TYPE} dcesrv_$fname"); + $res .= +" +/* + $fname +*/ + +static $d->{RETURN_TYPE} dcesrv_$fname(struct dcesrv_call_state *dce_call, +$pad"."TALLOC_CTX *mem_ctx, +$pad"."struct $fname *r) +{ +"; + + if ($d->{RETURN_TYPE} eq "void") { + $res .= "\tDCESRV_FAULT_VOID(DCERPC_FAULT_OP_RNG_ERROR);\n"; + } else { + $res .= "\tDCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);\n"; + } + + $res .= "} + +"; + } + } + + $res .= +" +/* include the generated boilerplate */ +#include \"librpc/gen_ndr/ndr_$name\_s.c\" +" +} + + +##################################################################### +# parse a parsed IDL structure back into an IDL file +sub Parse($) +{ + my($idl) = shift; + $res = ""; + foreach my $x (@{$idl}) { + ($x->{TYPE} eq "INTERFACE") && + Template($x); + } + return $res; +} + +1; -- cgit v1.2.3