| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # Copyrights 2011-2017 by [Mark Overmeer]. | 
| 2 |  |  |  |  |  |  | #  For other contributors see ChangeLog. | 
| 3 |  |  |  |  |  |  | # See the manual pages for details on the licensing terms. | 
| 4 |  |  |  |  |  |  | # Pod stripped from pm file by OODoc 2.02. | 
| 5 | 1 |  |  | 1 |  | 1130 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 30 |  | 
| 6 | 1 |  |  | 1 |  | 4 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 24 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | package XML::Compile::WSS; | 
| 9 | 1 |  |  | 1 |  | 5 | use vars '$VERSION'; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 41 |  | 
| 10 |  |  |  |  |  |  | $VERSION = '1.14'; | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 1 |  |  | 1 |  | 4 | use Log::Report 'xml-compile-wss'; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 | 1 |  |  | 1 |  | 205 | use XML::Compile::WSS::Util qw/:wss11/; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 157 |  | 
| 16 | 1 |  |  | 1 |  | 377 | use XML::Compile::Util      qw/SCHEMA2001/; | 
|  | 1 |  |  |  |  | 1501 |  | 
|  | 1 |  |  |  |  | 51 |  | 
| 17 | 1 |  |  | 1 |  | 420 | use XML::Compile::Schema::BuiltInTypes qw/builtin_type_info/; | 
|  | 1 |  |  |  |  | 58657 |  | 
|  | 1 |  |  |  |  | 93 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 1 |  |  | 1 |  | 13 | use File::Basename          qw/dirname/; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 84 |  | 
| 20 | 1 |  |  | 1 |  | 7 | use POSIX                   qw/strftime/; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 21 | 1 |  |  | 1 |  | 57 | use Scalar::Util            qw/weaken/; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 1117 |  | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | my %prefixes10 = | 
| 24 |  |  |  |  |  |  | ( ds  => DSIG_NS, wsse => WSSE_10, wsu => WSU_10 | 
| 25 |  |  |  |  |  |  | ); | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | my %prefixes11 = | 
| 28 |  |  |  |  |  |  | ( ds  => DSIG_NS, wsse => WSSE_10, wsu => WSU_10 | 
| 29 |  |  |  |  |  |  | , wss => WSS_11,  xenc => XENC_NS | 
| 30 |  |  |  |  |  |  | ); | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | my %versions = | 
| 33 |  |  |  |  |  |  | ( '1.0' => { xsddir => 'wss10', prefixes => \%prefixes10 } | 
| 34 |  |  |  |  |  |  | , '1.1' => { xsddir => 'wss11', prefixes => \%prefixes11 } | 
| 35 |  |  |  |  |  |  | ); | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | sub new(@) | 
| 39 | 0 |  |  | 0 | 1 |  | {   my $class = shift; | 
| 40 | 0 | 0 |  |  |  |  | my $args  = @_==1 ? shift : {@_}; | 
| 41 | 0 |  |  |  |  |  | my $self  = (bless {}, $class)->init($args); | 
| 42 | 0 |  | 0 |  |  |  | $self->prepare($args->{prepare} || 'ALL'); | 
| 43 | 0 |  |  |  |  |  | $self; | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | sub init($) | 
| 47 | 0 |  |  | 0 | 0 |  | {   my ($self, $args) = @_; | 
| 48 |  |  |  |  |  |  | my $version = $args->{wss_version} || $args->{version} | 
| 49 | 0 | 0 | 0 |  |  |  | or error __x"explicit wss_version required"; | 
| 50 | 0 |  |  |  |  |  | trace "initializing wss $version"; | 
| 51 |  |  |  |  |  |  |  | 
| 52 | 0 | 0 |  |  |  |  | $version = '1.1' | 
| 53 |  |  |  |  |  |  | if $version eq WSS11MODULE; | 
| 54 |  |  |  |  |  |  |  | 
| 55 | 0 | 0 |  |  |  |  | $versions{$version} | 
| 56 |  |  |  |  |  |  | or error __x"unknown wss version {v}, pick from {vs}" | 
| 57 |  |  |  |  |  |  | , v => $version, vs => [keys %versions]; | 
| 58 | 0 |  |  |  |  |  | $self->{XCW_version} = $version; | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 0 | 0 |  |  |  |  | if(my $schema = $self->{XCW_schema} = $args->{schema}) | 
| 61 | 0 |  |  |  |  |  | {   weaken $self->{XCW_schema}; | 
| 62 | 0 |  |  |  |  |  | $self->loadSchemas($schema, $version); | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  |  | 
| 65 | 0 |  |  |  |  |  | $self; | 
| 66 |  |  |  |  |  |  | } | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | sub prepare($) | 
| 69 | 0 |  |  | 0 | 1 |  | {   my ($self, $how) = @_; | 
| 70 | 0 |  |  |  |  |  | my $schema = $self->schema; | 
| 71 |  |  |  |  |  |  |  | 
| 72 | 0 | 0 |  |  |  |  | my ($r, $w) | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | = $how eq 'ALL'    ? (1, 1) | 
| 74 |  |  |  |  |  |  | : $how eq 'READER' ? (1, 0) | 
| 75 |  |  |  |  |  |  | : $how eq 'WRITER' ? (0, 1) | 
| 76 |  |  |  |  |  |  | : $how eq 'NONE'   ? (0, 0) | 
| 77 |  |  |  |  |  |  | :                    panic $how; | 
| 78 |  |  |  |  |  |  |  | 
| 79 | 0 | 0 |  |  |  |  | $self->prepareWriting($schema) if $w; | 
| 80 | 0 | 0 |  |  |  |  | $self->prepareReading($schema) if $r; | 
| 81 | 0 |  |  |  |  |  | $self; | 
| 82 |  |  |  |  |  |  | } | 
| 83 | 0 |  |  | 0 | 0 |  | sub prepareWriting($) { $_[0]->{XCW_prepare_w}++; $_[0] } | 
|  | 0 |  |  |  |  |  |  | 
| 84 | 0 |  |  | 0 | 0 |  | sub prepareReading($) { $_[0]->{XCW_prepare_r}++; $_[0] } | 
|  | 0 |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | #----------- | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 0 |  |  | 0 | 1 |  | sub version()    {shift->{XCW_version}}  # deprecated | 
| 89 | 0 |  |  | 0 | 1 |  | sub wssVersion() {shift->{XCW_version}} | 
| 90 | 0 |  |  | 0 | 1 |  | sub schema()     {shift->{XCW_schema}} | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | #----------- | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | sub create($$) | 
| 95 | 0 |  |  | 0 | 1 |  | {   my $self = shift; | 
| 96 |  |  |  |  |  |  | panic __x"WSS plugin {name} is not prepared for writing", name => ref $self | 
| 97 | 0 | 0 |  |  |  |  | unless $self->{XCW_prepare_w}; | 
| 98 | 0 |  |  |  |  |  | $self; | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | sub check($) | 
| 103 | 0 |  |  | 0 | 1 |  | {   my $self = shift; | 
| 104 |  |  |  |  |  |  | panic __x"WSS plugin {name} is not prepared for reading", name => ref $self | 
| 105 | 0 | 0 |  |  |  |  | unless $self->{XCW_prepare_r}; | 
| 106 | 0 |  |  |  |  |  | $self; | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | #----------- | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | # wsu had "allow anything" date fields, not type dateTime | 
| 112 |  |  |  |  |  |  | sub dateTime($) | 
| 113 | 0 |  |  | 0 | 1 |  | {   my ($self, $time) = @_; | 
| 114 | 0 | 0 | 0 |  |  |  | return $time if !defined $time || ref $time; | 
| 115 |  |  |  |  |  |  |  | 
| 116 | 0 |  |  |  |  |  | my $dateTime = builtin_type_info 'dateTime'; | 
| 117 | 0 | 0 |  |  |  |  | if($time !~ m/[^0-9.]/) { $time = $dateTime->{format}->($time) } | 
|  | 0 | 0 |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | elsif($dateTime->{check}->($time)) {} | 
| 119 | 0 |  |  |  |  |  | else {return $time} | 
| 120 |  |  |  |  |  |  |  | 
| 121 | 0 |  |  |  |  |  | +{ _ => $time | 
| 122 |  |  |  |  |  |  | , ValueType => SCHEMA2001.'/dateTime' | 
| 123 |  |  |  |  |  |  | }; | 
| 124 |  |  |  |  |  |  | } | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | #----------- | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | sub loadSchemas($$) | 
| 129 | 0 |  |  | 0 | 1 |  | {   my ($thing, $schema, $version) = @_; | 
| 130 | 0 | 0 |  |  |  |  | return if $schema->{XCW_wss_loaded}++; | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 0 | 0 |  |  |  |  | $schema->isa('XML::Compile::Cache') | 
| 133 |  |  |  |  |  |  | or error __x"loadSchemas() requires a XML::Compile::Cache object"; | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 0 |  |  |  |  |  | my $def      = $versions{$version}; | 
| 136 | 0 |  |  |  |  |  | my $prefixes = $def->{prefixes}; | 
| 137 | 0 |  |  |  |  |  | $schema->addPrefixes($prefixes); | 
| 138 |  |  |  |  |  |  |  | 
| 139 | 0 |  |  |  |  |  | my $rewrite = join ',', sort keys %$prefixes; | 
| 140 | 0 |  |  |  |  |  | $schema->addKeyRewrite("PREFIXED($rewrite)"); | 
| 141 |  |  |  |  |  |  |  | 
| 142 | 0 |  |  |  |  |  | (my $xsddir = __FILE__) =~ s! \.pm$ !/$def->{xsddir}!x; | 
| 143 | 0 |  |  |  |  |  | my @xsd = glob "$xsddir/*.xsd"; | 
| 144 |  |  |  |  |  |  |  | 
| 145 | 0 |  |  |  |  |  | trace "loading wss schemas $version"; | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 0 |  |  |  |  |  | $schema->importDefinitions | 
| 148 |  |  |  |  |  |  | ( \@xsd | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | # Missing from wss-secext-1.1.xsd (schema BUG)  Gladly, all | 
| 151 |  |  |  |  |  |  | # provided schemas have element_form qualified. | 
| 152 |  |  |  |  |  |  | , element_form_default => 'qualified' | 
| 153 |  |  |  |  |  |  | ); | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | # Another schema bug; attribute wsu:Id not declared qualified | 
| 156 |  |  |  |  |  |  | # Besides, ValueType is often used on timestamps, which are declared | 
| 157 |  |  |  |  |  |  | # as free-format fields (@*!&$#!&^ design committees!) | 
| 158 | 0 |  |  |  |  |  | my ($wsu10, $xsd) = (WSU_10, SCHEMA2001); | 
| 159 | 0 |  |  |  |  |  | $schema->importDefinitions( <<__PATCH ); | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | xmlns="$xsd" | 
| 162 |  |  |  |  |  |  | xmlns:wsu="$wsu10" | 
| 163 |  |  |  |  |  |  | targetNamespace="$wsu10" | 
| 164 |  |  |  |  |  |  | elementFormDefault="qualified" | 
| 165 |  |  |  |  |  |  | attributeFormDefault="unqualified"> | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | __PATCH | 
| 179 |  |  |  |  |  |  |  | 
| 180 | 0 |  |  |  |  |  | $schema->allowUndeclared(1); | 
| 181 | 0 |  |  |  |  |  | $schema->addCompileOptions('RW' | 
| 182 |  |  |  |  |  |  | , mixed_elements     => 'STRUCTURAL' | 
| 183 |  |  |  |  |  |  | , ignore_unused_tags => qr/^wsu_Id$/ | 
| 184 |  |  |  |  |  |  | ); | 
| 185 |  |  |  |  |  |  |  | 
| 186 | 0 |  |  |  |  |  | $schema->anyElement('ATTEMPT'); | 
| 187 | 0 |  |  |  |  |  | $schema; | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | sub writerHookWsuId($) | 
| 192 | 0 |  |  | 0 | 1 |  | {   my ($self, $type) = @_; | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | my $after = sub | 
| 195 | 0 |  |  | 0 |  |  | { my ($doc, $node, $path, $val) = @_; | 
| 196 |  |  |  |  |  |  |  | 
| 197 | 0 |  |  |  |  |  | my $id = $val->{wsu_Id}; | 
| 198 | 0 | 0 |  |  |  |  | defined $id or return $node; | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | # Some schema explicitly list wsu:Id attributes, we shouldn't add | 
| 201 |  |  |  |  |  |  | # the attribute again. | 
| 202 | 0 | 0 | 0 |  |  |  | if(my $has = $node->getAttributeNS(WSU_10, 'Id') | 
| 203 |  |  |  |  |  |  | || $node->getAttribute('wsu:Id')) | 
| 204 | 0 | 0 |  |  |  |  | {   $has eq $id or warning __x"two wsu:Id attributes: {one} and {two}" | 
| 205 |  |  |  |  |  |  | , one => $id, two => $has; | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 0 |  |  |  |  |  | return $node; | 
| 208 |  |  |  |  |  |  | } | 
| 209 |  |  |  |  |  |  |  | 
| 210 | 0 |  |  |  |  |  | $node->setNamespace(WSU_10, 'wsu', 0); | 
| 211 | 0 |  |  |  |  |  | $node->setAttributeNS(WSU_10, 'Id', $id); | 
| 212 | 0 |  |  |  |  |  | $node; | 
| 213 | 0 |  |  |  |  |  | }; | 
| 214 |  |  |  |  |  |  |  | 
| 215 | 0 |  |  |  |  |  | +{ action => 'WRITER', type => $type, after => $after }; | 
| 216 |  |  |  |  |  |  | } | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | #--------------------------- | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | 1; |