line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
## Domain Registry Interface, Mark & Signed Mark for EPP (draft-ietf-eppext-tmch-smd-01) |
2
|
|
|
|
|
|
|
## |
3
|
|
|
|
|
|
|
## Copyright (c) 2013-2015 Patrick Mevzek . All rights reserved. |
4
|
|
|
|
|
|
|
## |
5
|
|
|
|
|
|
|
## This file is part of Net::DRI |
6
|
|
|
|
|
|
|
## |
7
|
|
|
|
|
|
|
## Net::DRI is free software; you can redistribute it and/or modify |
8
|
|
|
|
|
|
|
## it under the terms of the GNU General Public License as published by |
9
|
|
|
|
|
|
|
## the Free Software Foundation; either version 2 of the License, or |
10
|
|
|
|
|
|
|
## (at your option) any later version. |
11
|
|
|
|
|
|
|
## |
12
|
|
|
|
|
|
|
## See the LICENSE file that comes with this distribution for more details. |
13
|
|
|
|
|
|
|
#################################################################################################### |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
package Net::DRI::Protocol::EPP::Extensions::ICANN::MarkSignedMark; |
16
|
|
|
|
|
|
|
|
17
|
1
|
|
|
1
|
|
1149
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
18
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
20
|
|
19
|
|
|
|
|
|
|
|
20
|
1
|
|
|
1
|
|
4
|
use Net::DRI::Util; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
15
|
|
21
|
1
|
|
|
1
|
|
4
|
use Net::DRI::Exception; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
15
|
|
22
|
1
|
|
|
1
|
|
4
|
use Net::DRI::Protocol::EPP::Util; |
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
18
|
|
23
|
|
|
|
|
|
|
|
24
|
1
|
|
|
1
|
|
208
|
use XML::LibXML (); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
use Encode; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
#################################################################################################### |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub setup |
30
|
|
|
|
|
|
|
{ |
31
|
|
|
|
|
|
|
my ($class,$po,$version)=@_; |
32
|
|
|
|
|
|
|
$po->ns({ 'mark' => [ 'urn:ietf:params:xml:ns:mark-1.0','mark-1.0.xsd' ], |
33
|
|
|
|
|
|
|
'signedMark' => [ 'urn:ietf:params:xml:ns:signedMark-1.0','signedMark-1.0'] }); |
34
|
|
|
|
|
|
|
return; |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub implements { return 'https://tools.ietf.org/html/draft-ietf-eppext-tmch-smd-01'; } |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
my %xml2perl = ( trademark => 'trademark', |
40
|
|
|
|
|
|
|
treatyOrStatute => 'treaty_statute', |
41
|
|
|
|
|
|
|
court => 'court', |
42
|
|
|
|
|
|
|
markName => 'mark_name', |
43
|
|
|
|
|
|
|
goodsAndServices=> 'goods_services', |
44
|
|
|
|
|
|
|
apId => 'application_id', |
45
|
|
|
|
|
|
|
apDate => 'application_date', |
46
|
|
|
|
|
|
|
regNum => 'registration_number', |
47
|
|
|
|
|
|
|
regDate => 'registration_date', |
48
|
|
|
|
|
|
|
exDate => 'expiration_date', |
49
|
|
|
|
|
|
|
refNum => 'reference_number', |
50
|
|
|
|
|
|
|
proDate => 'protection_date', |
51
|
|
|
|
|
|
|
execDate => 'execution_date', |
52
|
|
|
|
|
|
|
courtName => 'court_name', |
53
|
|
|
|
|
|
|
); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
#################################################################################################### |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub build_marks |
58
|
|
|
|
|
|
|
{ |
59
|
|
|
|
|
|
|
my ($po,$rd)=@_; |
60
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('A ref array must be passed for marks, or a standalone ref hash for only one mark') unless defined $rd && (ref $rd eq 'ARRAY' || ref $rd eq 'HASH'); |
61
|
|
|
|
|
|
|
my @r; |
62
|
|
|
|
|
|
|
foreach my $m (ref $rd eq 'ARRAY' ? @$rd : $rd) |
63
|
|
|
|
|
|
|
{ |
64
|
|
|
|
|
|
|
push @r,['mark:mark',{ 'xmlns:mark' => $po->ns()->{'mark'}->[0]},build_mark($m)]; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
return @r; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub build_mark |
70
|
|
|
|
|
|
|
{ |
71
|
|
|
|
|
|
|
my ($rd)=@_; |
72
|
|
|
|
|
|
|
my @r; |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters() unless defined $rd && ref $rd eq 'HASH'; |
75
|
|
|
|
|
|
|
my $type=$rd->{type}; |
76
|
|
|
|
|
|
|
$type='' unless defined $type; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
if ($type eq 'trademark' || exists $rd->{jurisdiction}) |
79
|
|
|
|
|
|
|
{ |
80
|
|
|
|
|
|
|
return _build_trademark($rd); |
81
|
|
|
|
|
|
|
} elsif ($type eq 'treaty_statute' || exists $rd->{protection}) |
82
|
|
|
|
|
|
|
{ |
83
|
|
|
|
|
|
|
return _build_treaty($rd); |
84
|
|
|
|
|
|
|
} elsif ($type eq 'court' || exists $rd->{court_name}) |
85
|
|
|
|
|
|
|
{ |
86
|
|
|
|
|
|
|
return _build_court($rd); |
87
|
|
|
|
|
|
|
} else |
88
|
|
|
|
|
|
|
{ |
89
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters(qq{Unrecognized type "$type" of mark, and no "jurisdiction", "protection" or "court_name" element}); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
return; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub _build_addr |
95
|
|
|
|
|
|
|
{ |
96
|
|
|
|
|
|
|
my ($contact)=@_; |
97
|
|
|
|
|
|
|
my (@r,$v); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
$v=scalar $contact->street(); |
100
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('Contact address must have from 1 to 3 street elements') unless defined $v && ref $v eq 'ARRAY' && @$v >=1 && @$v <= 3; |
101
|
|
|
|
|
|
|
push @r,map { ['mark:street',$_] } @$v; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
$v=scalar $contact->city(); |
104
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('Contact address must have a city') unless defined $v; |
105
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Contact address city must be an XML token string') unless Net::DRI::Util::xml_is_token($v); |
106
|
|
|
|
|
|
|
push @r,['mark:city',$v]; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
$v=scalar $contact->sp(); |
109
|
|
|
|
|
|
|
if (defined $v && length $v) |
110
|
|
|
|
|
|
|
{ |
111
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Contact address sp must be an XML token string') unless Net::DRI::Util::xml_is_token($v); |
112
|
|
|
|
|
|
|
push @r,['mark:sp',$v]; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
$v=scalar $contact->pc(); |
116
|
|
|
|
|
|
|
if (defined $v && length $v) |
117
|
|
|
|
|
|
|
{ |
118
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Contact address pc must be an XML token string with 16 characters or less') unless Net::DRI::Util::xml_is_token($v,0,16); |
119
|
|
|
|
|
|
|
push @r,['mark:pc',$v]; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
$v=scalar $contact->cc(); |
123
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('Contact address must have a cc') unless defined $v; |
124
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Contact address cc must be an XML token string of 2 characters') unless Net::DRI::Util::xml_is_token($v,2,2); |
125
|
|
|
|
|
|
|
push @r,['mark:cc',$v]; |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
return @r; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub _build_contact |
131
|
|
|
|
|
|
|
{ |
132
|
|
|
|
|
|
|
my ($type,$contact)=@_; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Element must be contact object, not: '.$contact) unless Net::DRI::Util::isa_contact($contact); |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
my (@r,$v); |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
$v=scalar $contact->name(); |
139
|
|
|
|
|
|
|
if (defined $v && length $v) |
140
|
|
|
|
|
|
|
{ |
141
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Name of contact must be an XML token string, not: '.$v) unless Net::DRI::Util::xml_is_token($v); |
142
|
|
|
|
|
|
|
push @r,['mark:name',$v]; |
143
|
|
|
|
|
|
|
} else |
144
|
|
|
|
|
|
|
{ |
145
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('Name is mandatory for a contact') if ($type eq 'contact'); |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
$v=scalar $contact->org(); |
149
|
|
|
|
|
|
|
if (defined $v && length $v) |
150
|
|
|
|
|
|
|
{ |
151
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Org of contact must be an XML token string, not: '.$v) unless Net::DRI::Util::xml_is_token($v); |
152
|
|
|
|
|
|
|
push @r,['mark:org',$v]; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
push @r,['mark:addr',_build_addr($contact)]; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
$v=$contact->voice(); |
158
|
|
|
|
|
|
|
if (defined $v && length $v) |
159
|
|
|
|
|
|
|
{ |
160
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Voice of contact must be an XML token string verifying pattern "(\+[0-9]{1,3}\.[0-9]{1,14})?"') unless Net::DRI::Util::xml_is_token($v,0,17) && $v=~m/^\+[0-9]{1,3}\.[0-9]{1,14}$/; |
161
|
|
|
|
|
|
|
push @r,Net::DRI::Protocol::EPP::Util::build_tel('mark:voice',$v); |
162
|
|
|
|
|
|
|
} else |
163
|
|
|
|
|
|
|
{ |
164
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('Voice is mandatory for a contact') if ($type eq 'contact'); |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
$v=$contact->fax(); |
168
|
|
|
|
|
|
|
if (defined $v && length $v) |
169
|
|
|
|
|
|
|
{ |
170
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Fax of contact must be an XML token string verifying pattern "(\+[0-9]{1,3}\.[0-9]{1,14})?"') unless Net::DRI::Util::xml_is_token($v,0,17) && $v=~m/^\+[0-9]{1,3}\.[0-9]{1,14}$/; |
171
|
|
|
|
|
|
|
push @r,Net::DRI::Protocol::EPP::Util::build_tel('mark:fax',$v); |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
$v=$contact->email(); |
175
|
|
|
|
|
|
|
if (defined $v && length $v) |
176
|
|
|
|
|
|
|
{ |
177
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Email of contact must be an XML token string with at least 1 character, not: '.$v) unless Net::DRI::Util::xml_is_token($v,1); |
178
|
|
|
|
|
|
|
push @r,['mark:email',$v]; |
179
|
|
|
|
|
|
|
} else |
180
|
|
|
|
|
|
|
{ |
181
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('Email is mandatory for a contact') if ($type eq 'contact'); |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
return @r; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub _add_token |
188
|
|
|
|
|
|
|
{ |
189
|
|
|
|
|
|
|
my ($rd,$key,$optional)=@_; |
190
|
|
|
|
|
|
|
my $pkey=exists $xml2perl{$key} ? $xml2perl{$key} : $key; |
191
|
|
|
|
|
|
|
if (Net::DRI::Util::has_key($rd,$pkey)) |
192
|
|
|
|
|
|
|
{ |
193
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters(qq{Value for "$pkey" key must be an XML token string}) unless Net::DRI::Util::xml_is_token($rd->{$pkey}); |
194
|
|
|
|
|
|
|
return ['mark:'.$key,$rd->{$pkey}]; |
195
|
|
|
|
|
|
|
} else |
196
|
|
|
|
|
|
|
{ |
197
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters(qq{"$pkey" key must exist}) unless (defined $optional && $optional); |
198
|
|
|
|
|
|
|
return; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub _add_datetime |
203
|
|
|
|
|
|
|
{ |
204
|
|
|
|
|
|
|
my ($rd,$key,$optional)=@_; |
205
|
|
|
|
|
|
|
my $pkey=exists $xml2perl{$key} ? $xml2perl{$key} : $key; |
206
|
|
|
|
|
|
|
if (Net::DRI::Util::has_key($rd,$pkey)) |
207
|
|
|
|
|
|
|
{ |
208
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters(qq{Value for "$pkey" key must be a DateTime object}) unless Net::DRI::Util::is_class($rd->{$pkey},'DateTime'); |
209
|
|
|
|
|
|
|
return ['mark:'.$key,Net::DRI::Util::dto2zstring($rd->{$pkey})]; |
210
|
|
|
|
|
|
|
} else |
211
|
|
|
|
|
|
|
{ |
212
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters(qq{"$pkey" key must exist}) unless (defined $optional && $optional); |
213
|
|
|
|
|
|
|
return; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub _build_common1 |
218
|
|
|
|
|
|
|
{ |
219
|
|
|
|
|
|
|
my ($rd)=@_; |
220
|
|
|
|
|
|
|
my @r; |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
push @r,_add_token($rd,'id'); |
223
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Value for "id" key must match pattern "\d+-\d+"') unless $rd->{id}=~m/^\d+-\d+$/; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
push @r,_add_token($rd,'markName'); |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('"contact" key must exist') unless Net::DRI::Util::has_key($rd,'contact'); |
228
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Value for "contact" key must be a ContactSet object') unless Net::DRI::Util::isa_contactset($rd->{contact}); |
229
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('Value for "contact" key must have at least one contact of type holder_owner, holder_assignee or holder_licensee') unless grep { /^(?:holder_owner|holder_assignee|holder_licensee)$/ } $rd->{contact}->types(); |
230
|
|
|
|
|
|
|
foreach my $type (qw/owner assignee licensee/) |
231
|
|
|
|
|
|
|
{ |
232
|
|
|
|
|
|
|
my @o=$rd->{contact}->get('holder_'.$type); |
233
|
|
|
|
|
|
|
next unless @o; |
234
|
|
|
|
|
|
|
foreach my $c (@o) |
235
|
|
|
|
|
|
|
{ |
236
|
|
|
|
|
|
|
push @r,['mark:holder',{ entitlement => $type },_build_contact('holder',$c)]; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
foreach my $type (qw/owner agent thirdparty/) |
240
|
|
|
|
|
|
|
{ |
241
|
|
|
|
|
|
|
my @o=$rd->{contact}->get('contact_'.$type); |
242
|
|
|
|
|
|
|
next unless @o; |
243
|
|
|
|
|
|
|
foreach my $c (@o) |
244
|
|
|
|
|
|
|
{ |
245
|
|
|
|
|
|
|
push @r,['mark:contact',{ type => $type },_build_contact('contact',$c)]; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
return @r; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub _build_common2 |
253
|
|
|
|
|
|
|
{ |
254
|
|
|
|
|
|
|
my ($rd)=@_; |
255
|
|
|
|
|
|
|
my @r; |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
if (Net::DRI::Util::has_key($rd,'label')) |
258
|
|
|
|
|
|
|
{ |
259
|
|
|
|
|
|
|
foreach my $label (ref $rd->{label} eq 'ARRAY' ? @{$rd->{label}} : ($rd->{label})) |
260
|
|
|
|
|
|
|
{ |
261
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters(qq{Label "$label" must be an XML token string from 1 to 63 characters}) unless Net::DRI::Util::xml_is_token($label,1,63); |
262
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters(qq{Label "$label" must pass regex "[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?"}) unless $label=~m/^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$/; |
263
|
|
|
|
|
|
|
push @r,['mark:label',$label]; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
push @r,_add_token($rd,'goodsAndServices'); |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
return @r; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub _build_common3 |
273
|
|
|
|
|
|
|
{ |
274
|
|
|
|
|
|
|
my ($rd)=@_; |
275
|
|
|
|
|
|
|
my @r; |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
push @r,_add_token($rd,'refNum'); |
278
|
|
|
|
|
|
|
push @r,_add_datetime($rd,'proDate'); |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
return @r; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
sub _build_trademark |
284
|
|
|
|
|
|
|
{ |
285
|
|
|
|
|
|
|
my ($rd)=@_; |
286
|
|
|
|
|
|
|
my @r; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
push @r,_build_common1($rd); ## id/markName/holder/contact |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
push @r,_add_token($rd,'jurisdiction'); |
291
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters(qq{Value for "jurisdiction" key must be an XML token string of 2 characters}) unless Net::DRI::Util::xml_is_token($rd->{jurisdiction},2,2); |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
if (Net::DRI::Util::has_key($rd,'class')) |
294
|
|
|
|
|
|
|
{ |
295
|
|
|
|
|
|
|
foreach my $class (ref $rd->{class} eq 'ARRAY' ? @{$rd->{class}} : ($rd->{class})) |
296
|
|
|
|
|
|
|
{ |
297
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Class must be an integer, not: '.$class) unless $class=~m/^\d+$/; |
298
|
|
|
|
|
|
|
push @r,['mark:class',$class]; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
push @r,_build_common2($rd); ## label/goodsAndServices |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
push @r,_add_token($rd,'apId',1); |
305
|
|
|
|
|
|
|
push @r,_add_datetime($rd,'apDate',1); |
306
|
|
|
|
|
|
|
push @r,_add_token($rd,'regNum'); |
307
|
|
|
|
|
|
|
push @r,_add_datetime($rd,'regDate'); |
308
|
|
|
|
|
|
|
push @r,_add_datetime($rd,'exDate',1); |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
return ['mark:trademark',@r]; |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
sub _build_treaty |
314
|
|
|
|
|
|
|
{ |
315
|
|
|
|
|
|
|
my ($rd)=@_; |
316
|
|
|
|
|
|
|
my @r; |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
push @r,_build_common1($rd); ## id/markName/holder/contact |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_insufficient_parameters('Key "protection" must exist') unless Net::DRI::Util::has_key($rd,'protection'); |
321
|
|
|
|
|
|
|
foreach my $rprot (ref $rd->{protection} eq 'ARRAY' ? @{$rd->{protection}} : ($rd->{protection})) |
322
|
|
|
|
|
|
|
{ |
323
|
|
|
|
|
|
|
my @pro; |
324
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters('Each protection item must be a ref hash, not: '.$rprot) unless ref $rprot eq 'HASH'; |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
push @r,_add_token($rprot,'cc'); |
327
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters(qq{Value for "cc" key must be an XML token string of 2 characters}) unless Net::DRI::Util::xml_is_token($rprot->{cc},2,2); |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
push @r,_add_token($rprot,'region',1); |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
if (Net::DRI::Util::has_key($rprot,'ruling')) |
332
|
|
|
|
|
|
|
{ |
333
|
|
|
|
|
|
|
foreach my $ruling (ref $rprot->{ruling} eq 'ARRAY' ? @{$rprot->{ruling}} : ($rprot->{ruling})) |
334
|
|
|
|
|
|
|
{ |
335
|
|
|
|
|
|
|
push @r,_add_token({ ruling => $ruling },'ruling'); |
336
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters(qq{Each "ruling" item must be an XML token string of 2 characters}) unless Net::DRI::Util::xml_is_token($ruling,2,2); |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
push @r,['mark:protection',@pro]; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
push @r,_build_common2($rd); ## label/goodsAndServices |
344
|
|
|
|
|
|
|
push @r,_build_common3($rd); ## refNum/proDate |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
push @r,_add_token($rd,'title'); |
347
|
|
|
|
|
|
|
push @r,_add_datetime($rd,'execDate'); |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
return ['mark:treatyOrStatute',@r]; |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
sub _build_court |
353
|
|
|
|
|
|
|
{ |
354
|
|
|
|
|
|
|
my ($rd)=@_; |
355
|
|
|
|
|
|
|
my @r; |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
push @r,_build_common1($rd); ## id/markName/holder/contact |
358
|
|
|
|
|
|
|
push @r,_build_common2($rd); ## label/goodsAndServices |
359
|
|
|
|
|
|
|
push @r,_build_common3($rd); ## refNum/proDate |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
push @r,_add_token($rd,'cc'); |
362
|
|
|
|
|
|
|
Net::DRI::Exception::usererr_invalid_parameters(qq{Value for "cc" key must be an XML token string of 2 characters}) unless Net::DRI::Util::xml_is_token($rd->{cc},2,2); |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
if (Net::DRI::Util::has_key($rd,'region')) |
365
|
|
|
|
|
|
|
{ |
366
|
|
|
|
|
|
|
foreach my $region (ref $rd->{region} eq 'ARRAY' ? @{$rd->{region}} : ($rd->{region})) |
367
|
|
|
|
|
|
|
{ |
368
|
|
|
|
|
|
|
push @r,_add_token({ region => $region },'region'); |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
push @r,_add_token($rd,'courtName'); |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
return ['mark:court',@r]; |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
#################################################################################################### |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
sub parse_tel |
380
|
|
|
|
|
|
|
{ |
381
|
|
|
|
|
|
|
my ($node)=@_; |
382
|
|
|
|
|
|
|
my $r=$node->textContent(); |
383
|
|
|
|
|
|
|
$r.='x'.$node->getAttribute('x') if $node->hasAttribute('x'); |
384
|
|
|
|
|
|
|
return $r; |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
sub parse_contact |
388
|
|
|
|
|
|
|
{ |
389
|
|
|
|
|
|
|
my ($po,$start)=@_; |
390
|
|
|
|
|
|
|
my $contact=$po->create_local_object('contact'); |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
foreach my $el (Net::DRI::Util::xml_list_children($start)) |
393
|
|
|
|
|
|
|
{ |
394
|
|
|
|
|
|
|
my ($name,$node)=@$el; |
395
|
|
|
|
|
|
|
if ($name=~m/^(?:name|org|email)$/) |
396
|
|
|
|
|
|
|
{ |
397
|
|
|
|
|
|
|
$contact->$name($node->textContent()); |
398
|
|
|
|
|
|
|
} elsif ($name=~m/^(?:voice|fax)$/) |
399
|
|
|
|
|
|
|
{ |
400
|
|
|
|
|
|
|
$contact->$name(parse_tel($node)); |
401
|
|
|
|
|
|
|
} elsif ($name eq 'addr') |
402
|
|
|
|
|
|
|
{ |
403
|
|
|
|
|
|
|
my @street; |
404
|
|
|
|
|
|
|
foreach my $subel (Net::DRI::Util::xml_list_children($node)) |
405
|
|
|
|
|
|
|
{ |
406
|
|
|
|
|
|
|
my ($addrname,$addrnode)=@$subel; |
407
|
|
|
|
|
|
|
if ($addrname eq 'street') |
408
|
|
|
|
|
|
|
{ |
409
|
|
|
|
|
|
|
push @street,$addrnode->textContent(); |
410
|
|
|
|
|
|
|
} elsif ($addrname=~m/^(?:city|sp|pc|cc)$/) |
411
|
|
|
|
|
|
|
{ |
412
|
|
|
|
|
|
|
$contact->$addrname($addrnode->textContent()); |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
$contact->street(\@street); |
416
|
|
|
|
|
|
|
} elsif ($name=~m/^(?:voice|fax)$/) |
417
|
|
|
|
|
|
|
{ |
418
|
|
|
|
|
|
|
$contact->$name(Net::DRI::Protocol::EPP::Util::parse_tel($node)); |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
return $contact; |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
sub parse_mark |
426
|
|
|
|
|
|
|
{ |
427
|
|
|
|
|
|
|
my ($po,$start)=@_; |
428
|
|
|
|
|
|
|
my @marks; |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
foreach my $el (Net::DRI::Util::xml_list_children($start)) |
431
|
|
|
|
|
|
|
{ |
432
|
|
|
|
|
|
|
my ($name,$node)=@$el; |
433
|
|
|
|
|
|
|
if ($name=~m/^(?:trademark|treatyOrStatute|court)$/) |
434
|
|
|
|
|
|
|
{ |
435
|
|
|
|
|
|
|
my %m=(type => $xml2perl{$name}); |
436
|
|
|
|
|
|
|
my (@class,@label,@protection,@region); |
437
|
|
|
|
|
|
|
my $cs=$po->create_local_object('contactset'); |
438
|
|
|
|
|
|
|
foreach my $subel (Net::DRI::Util::xml_list_children($node)) |
439
|
|
|
|
|
|
|
{ |
440
|
|
|
|
|
|
|
my ($mname,$mnode)=@$subel; |
441
|
|
|
|
|
|
|
if ($mname=~m/^(id|markName|jurisdiction|goodsAndServices|apId|regNum|refNum|title|cc|courtName)$/) |
442
|
|
|
|
|
|
|
{ |
443
|
|
|
|
|
|
|
$m{exists $xml2perl{$mname} ? $xml2perl{$mname} : $mname}=$mnode->textContent(); |
444
|
|
|
|
|
|
|
} elsif ($mname eq 'holder') |
445
|
|
|
|
|
|
|
{ |
446
|
|
|
|
|
|
|
my $type='holder_'.$mnode->getAttribute('entitlement'); ## owner, assignee, licensee |
447
|
|
|
|
|
|
|
$cs->add(parse_contact($po,$mnode),$type); |
448
|
|
|
|
|
|
|
} elsif ($mname eq 'contact') |
449
|
|
|
|
|
|
|
{ |
450
|
|
|
|
|
|
|
my $type='contact_'.$mnode->getAttribute('type'); ## owner, agent, thirdparty |
451
|
|
|
|
|
|
|
$cs->add(parse_contact($po,$mnode),$type); |
452
|
|
|
|
|
|
|
} elsif ($mname eq 'class') |
453
|
|
|
|
|
|
|
{ |
454
|
|
|
|
|
|
|
push @class,$mnode->textContent(); |
455
|
|
|
|
|
|
|
} elsif ($mname eq 'label') |
456
|
|
|
|
|
|
|
{ |
457
|
|
|
|
|
|
|
push @label,$mnode->textContent(); |
458
|
|
|
|
|
|
|
} elsif ($mname=~m/^(?:apDate|regDate|exDate|proDate|execDate)$/) |
459
|
|
|
|
|
|
|
{ |
460
|
|
|
|
|
|
|
$m{$xml2perl{$mname}}=$po->parse_iso8601($mnode->textContent()); |
461
|
|
|
|
|
|
|
} elsif ($mname eq 'protection') |
462
|
|
|
|
|
|
|
{ |
463
|
|
|
|
|
|
|
my %p; |
464
|
|
|
|
|
|
|
foreach my $pel (Net::DRI::Util::xml_list_children($mnode)) |
465
|
|
|
|
|
|
|
{ |
466
|
|
|
|
|
|
|
my ($pname,$pnode)=@$pel; |
467
|
|
|
|
|
|
|
if ($pname=~m/^(cc|region)$/) |
468
|
|
|
|
|
|
|
{ |
469
|
|
|
|
|
|
|
$p{$pname}=$pnode->textContent(); |
470
|
|
|
|
|
|
|
} elsif ($pname eq 'ruling') |
471
|
|
|
|
|
|
|
{ |
472
|
|
|
|
|
|
|
push @{$p{ruling}},$pnode->textContent(); |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
push @protection,\%p; |
476
|
|
|
|
|
|
|
} elsif ($mname eq 'region') |
477
|
|
|
|
|
|
|
{ |
478
|
|
|
|
|
|
|
push @region,$mnode->textContent(); |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
$m{contact}=$cs; |
482
|
|
|
|
|
|
|
$m{class}=\@class if @class; |
483
|
|
|
|
|
|
|
$m{label}=\@label if @label; |
484
|
|
|
|
|
|
|
$m{protection}=\@protection if @protection; |
485
|
|
|
|
|
|
|
$m{region}=\@region if @region; |
486
|
|
|
|
|
|
|
if (exists $m{goods_services}) |
487
|
|
|
|
|
|
|
{ |
488
|
|
|
|
|
|
|
$m{goods_services}=~s/\n +/ /g; |
489
|
|
|
|
|
|
|
$m{goods_services}=~s/ +$//s; |
490
|
|
|
|
|
|
|
} |
491
|
|
|
|
|
|
|
push @marks,\%m; |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
return \@marks; |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
sub lined_content |
499
|
|
|
|
|
|
|
{ |
500
|
|
|
|
|
|
|
my ($node,$signs,@keys)=@_; |
501
|
|
|
|
|
|
|
my $r=Net::DRI::Util::xml_traverse($node,$signs,@keys); |
502
|
|
|
|
|
|
|
return unless defined $r; |
503
|
|
|
|
|
|
|
$r=$r->textContent(); |
504
|
|
|
|
|
|
|
$r=~s/\s+//g; |
505
|
|
|
|
|
|
|
return $r; |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub parse_signed_mark |
509
|
|
|
|
|
|
|
{ |
510
|
|
|
|
|
|
|
my ($po,$start)=@_; |
511
|
|
|
|
|
|
|
my %smark; |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
foreach my $el (Net::DRI::Util::xml_list_children($start)) |
514
|
|
|
|
|
|
|
{ |
515
|
|
|
|
|
|
|
my ($name,$node)=@$el; |
516
|
|
|
|
|
|
|
if ($name eq 'id') |
517
|
|
|
|
|
|
|
{ |
518
|
|
|
|
|
|
|
$smark{id}=$node->textContent(); |
519
|
|
|
|
|
|
|
} elsif ($name eq 'issuerInfo') |
520
|
|
|
|
|
|
|
{ |
521
|
|
|
|
|
|
|
my %issuer=(id => $node->getAttribute('issuerID')); |
522
|
|
|
|
|
|
|
foreach my $iel (Net::DRI::Util::xml_list_children($node)) |
523
|
|
|
|
|
|
|
{ |
524
|
|
|
|
|
|
|
my ($iname,$inode)=@$iel; |
525
|
|
|
|
|
|
|
if ($iname=~m/^(?:org|email|url)$/) |
526
|
|
|
|
|
|
|
{ |
527
|
|
|
|
|
|
|
$issuer{$iname}=$inode->textContent(); |
528
|
|
|
|
|
|
|
} elsif ($iname eq 'voice') |
529
|
|
|
|
|
|
|
{ |
530
|
|
|
|
|
|
|
$issuer{$iname}=parse_tel($inode); |
531
|
|
|
|
|
|
|
} |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
$smark{issuer}=\%issuer; |
534
|
|
|
|
|
|
|
} elsif ($name eq 'notBefore') |
535
|
|
|
|
|
|
|
{ |
536
|
|
|
|
|
|
|
$smark{'creation_date'}=$po->parse_iso8601($node->textContent()); |
537
|
|
|
|
|
|
|
} elsif ($name eq 'notAfter') |
538
|
|
|
|
|
|
|
{ |
539
|
|
|
|
|
|
|
$smark{'expiration_date'}=$po->parse_iso8601($node->textContent()); |
540
|
|
|
|
|
|
|
} elsif ($name eq 'mark') |
541
|
|
|
|
|
|
|
{ |
542
|
|
|
|
|
|
|
$smark{mark}=parse_mark($po,$node); |
543
|
|
|
|
|
|
|
} elsif ($name eq 'Signature') |
544
|
|
|
|
|
|
|
{ |
545
|
|
|
|
|
|
|
my $signs='http://www.w3.org/2000/09/xmldsig#'; |
546
|
|
|
|
|
|
|
my %s=(id => $start->getAttribute('id')); |
547
|
|
|
|
|
|
|
$s{'value'}=lined_content($node,$signs,qw/SignatureValue/); |
548
|
|
|
|
|
|
|
## TODO: handle other algorithms |
549
|
|
|
|
|
|
|
$s{'key'}={ algorithm => 'rsa', |
550
|
|
|
|
|
|
|
x509_certificate => lined_content($node,$signs,qw/KeyInfo X509Data X509Certificate/), |
551
|
|
|
|
|
|
|
}; |
552
|
|
|
|
|
|
|
$s{'validated'}=_validate_xmldsig($start); |
553
|
|
|
|
|
|
|
$smark{'signature'}=\%s; |
554
|
|
|
|
|
|
|
} |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
return \%smark; |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
sub _validate_xmldsig |
560
|
|
|
|
|
|
|
{ |
561
|
|
|
|
|
|
|
my ($xml,$rs)=@_; |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
require XML::LibXML::XPathContext; |
564
|
|
|
|
|
|
|
require Digest::SHA; |
565
|
|
|
|
|
|
|
require Crypt::OpenSSL::X509; |
566
|
|
|
|
|
|
|
require Crypt::OpenSSL::RSA; |
567
|
|
|
|
|
|
|
require MIME::Base64; |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
my $xpc=XML::LibXML::XPathContext->new(); |
570
|
|
|
|
|
|
|
$xpc->registerNs('ds','http://www.w3.org/2000/09/xmldsig#'); |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
foreach my $node ($xpc->findnodes('//ds:Reference',$xml)) |
573
|
|
|
|
|
|
|
{ |
574
|
|
|
|
|
|
|
my $for=$node->getAttribute('URI'); |
575
|
|
|
|
|
|
|
$for=~s/^#//; |
576
|
|
|
|
|
|
|
my $cnode=$xpc->findnodes("//*[\@id='${for}' or \@Id='${for}']",$xml); |
577
|
|
|
|
|
|
|
return 0 unless $cnode->size() == 1; |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
$cnode=$cnode->get_node(1); ## node on which we perform the digest operation |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
my %algos=map { $_->getAttribute('Algorithm') => 1 } $xpc->findnodes('ds:Transforms/ds:Transform',$node); |
582
|
|
|
|
|
|
|
return 0 unless exists $algos{'http://www.w3.org/2001/10/xml-exc-c14n#'}; |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
my $xmlstring=$cnode->toStringEC14N(0,exists $algos{'http://www.w3.org/2000/09/xmldsig#enveloped-signature'} ? q{(. | .//node() | .//@* | .//namespace::*)[not(self::comment() or ancestor-or-self::ds:Signature)]} : undef,$xpc); |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
return 0 unless defined $xmlstring && $xpc->findnodes('ds:DigestValue',$node)->get_node(1)->textContent() eq _sha256b64padded($xmlstring); |
587
|
|
|
|
|
|
|
} |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
my $cert=$xpc->findnodes('//ds:X509Certificate',$xml)->get_node(1)->textContent(); |
590
|
|
|
|
|
|
|
$cert=~s/ /\n/g; |
591
|
|
|
|
|
|
|
my $certobj=Crypt::OpenSSL::X509->new_from_string("-----BEGIN CERTIFICATE-----\n".$cert."\n-----END CERTIFICATE-----", Crypt::OpenSSL::X509::FORMAT_PEM()); |
592
|
|
|
|
|
|
|
my $key=Crypt::OpenSSL::RSA->new_public_key($certobj->pubkey()); |
593
|
|
|
|
|
|
|
$key->use_sha256_hash(); |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
my $xmlsi=$xpc->find('//ds:SignedInfo',$xml)->get_node(1); |
596
|
|
|
|
|
|
|
$xmlsi->setNamespace('http://www.w3.org/2000/09/xmldsig#','ds',0); |
597
|
|
|
|
|
|
|
my $sigval=$xpc->findnodes('//ds:SignatureValue',$xmlsi)->get_node(1)->textContent(); |
598
|
|
|
|
|
|
|
$sigval=~s!\s+!!g; |
599
|
|
|
|
|
|
|
my $verify=$key->verify($xmlsi->toStringEC14N(0), MIME::Base64::decode_base64($sigval)); |
600
|
|
|
|
|
|
|
return (defined $verify && $verify) ? 1 : 0; |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
sub _sha256b64padded |
604
|
|
|
|
|
|
|
{ |
605
|
|
|
|
|
|
|
my ($in)=@_; |
606
|
|
|
|
|
|
|
my $out = Digest::SHA::sha256_base64($in); |
607
|
|
|
|
|
|
|
while (length($out) % 4) { $out .= '='; } |
608
|
|
|
|
|
|
|
return $out; |
609
|
|
|
|
|
|
|
} |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
sub parse_encoded_signed_mark |
612
|
|
|
|
|
|
|
{ |
613
|
|
|
|
|
|
|
my ($po,$start)=@_; |
614
|
|
|
|
|
|
|
my $content; |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
if (ref $start) |
617
|
|
|
|
|
|
|
{ |
618
|
|
|
|
|
|
|
my $encoding=$start->hasAttribute('encoding') ? $start->getAttribute('encoding') : 'base64'; |
619
|
|
|
|
|
|
|
Net::DRI::Exception::err_invalid_parameter('For encoded signed mark, only base64 encoding is supported') unless $encoding eq 'base64'; |
620
|
|
|
|
|
|
|
$content=$start->textContent(); |
621
|
|
|
|
|
|
|
} else |
622
|
|
|
|
|
|
|
{ |
623
|
|
|
|
|
|
|
my @a=grep { /-----BEGIN ENCODED SMD-----/ .. /-----END ENCODED SMD-----/ } split(/\n/,$start); |
624
|
|
|
|
|
|
|
$content=join("\n",@a[1..($#a-1)]); |
625
|
|
|
|
|
|
|
} |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
require MIME::Base64; |
628
|
|
|
|
|
|
|
my $xml=MIME::Base64::decode_base64($content); |
629
|
|
|
|
|
|
|
$xml=Encode::decode('UTF-8',$xml,Encode::FB_CROAK | Encode::LEAVE_SRC); |
630
|
|
|
|
|
|
|
my $root=XML::LibXML->load_xml(no_cdata => 1, no_blanks => 1, no_network => 1, string => $xml)->documentElement(); |
631
|
|
|
|
|
|
|
Net::DRI::Exception::err_invalid_parameter('Decoding should give a signedMark root element') unless $root->localname() eq 'signedMark'; |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
return parse_signed_mark($po,$root); |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
#################################################################################################### |
637
|
|
|
|
|
|
|
1; |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
__END__ |