1## Domain Registry Interface, Handling of contact data for .NO
2##
3## Copyright (c) 2008-2010 UNINETT Norid AS, E<lt>http://www.norid.noE<gt>,
4##                    Trond Haugen E<lt>info@norid.noE<gt>.
5##                    All rights reserved.
6##
7## This file is part of Net::DRI
8##
9## Net::DRI is free software; you can redistribute it and/or modify
10## it under the terms of the GNU General Public License as published by
11## the Free Software Foundation; either version 2 of the License, or
12## (at your option) any later version.
13##
14## See the LICENSE file that comes with this distribution for more details.
15#
16#
17#
18###############################################################################
19
20package Net::DRI::Data::Contact::NO;
21
22use strict;
23use warnings;
24use base qw/Net::DRI::Data::Contact/;
25use Email::Valid;
26use Net::DRI::Util;
27use Net::DRI::Exception;
28
29our $VERSION = do { my @r = ( q$Revision: 1.5 $ =~ /\d+/gmx ); sprintf( "%d" . ".%02d" x $#r, @r ); };
30
31__PACKAGE__->register_attributes(qw(type identity mobilephone organization rolecontact xemail xdisclose facets));
32
33=pod
34
35=head1 NAME
36
37Net::DRI::Data::Contact::NO - Handle .NO contact data for Net::DRI
38
39=head1 DESCRIPTION
40
41This subclass of Net::DRI::Data::Contact adds accessors and validation for
42.NO specific data.
43
44=head1 METHODS
45
46The following accessors/mutators can be called in chain, as they all return
47the object itself.
48
49=head2 type()
50
51Mandatory, must be set for all contacts. Specify what type of contact to
52register. Value must be one of: 'person', 'organization' or 'role'.
53
54Example: $co->type('organization')
55
56=head2 identity()
57
58Currently valid for type='organization' only.
59Must then be set to specify the organization number in Br�nn�ysund,
60the Norwegian Business Register.
61
62Example: $co->identity({type=>'organizationNumber', value=>'987654321'});
63
64=head2 mobilephone()
65
66Optional. Set a mobile phone number for the contact.
67
68Example: $co->mobilephone('+47.123456780')
69
70=head2 organization()
71
72Optional. Set one or more organization-elements which specify organizations
73which the contact belongs to. The value should be the local contact id
74of an organization object.
75
76This element can only be used for role and person contacts.
77
78$co->organization('EFA12O');
79
80=head2 rolecontact()
81
82Optional. Set one or more roleContact-elements which specify persons which
83belongs to a role contact. The value should be the local contact id of a
84person object.
85
86This element can only be used for role contacts.
87
88Example: $co->rolecontact(['JD12P', 'JD13P']);
89
90=head2 xemail()
91
92Optional. Set one or more email-elements which specify email addresses in
93addition to the mandatory email element in the standard contact create command.
94
95Example: $co->xemail(['xtra1@example.no', 'xtra2@example.no']);
96
97=head2 xdisclose()
98
99Optional. A disclose-element which must contain the child element mobilePhone.
100This element notes the clients preference to allow or restrict disclosure of
101the mobile phone number. If not present, the servers stated data collection
102policy is used.
103
104Example: $co->xdisclose({mobilePhone=>0});
105
106=head2 facets()
107Facets are some special control attributes that can be used to
108implement a super registrar (admin registrar).
109
110A super registrar can suppress certain checks and perform actions on behalf of a normal registrar.
111
112Facets are key/values pairs.
113Net::DRI will not try to enforce what key/value pairs that are possible,
114but let the registry decide their validity.
115
116Example: $co->facets( { 'skip-manual-review' => 1, 'ignores-exceptions' => 'reg123'} );
117
118
119=head1 SUPPORT
120
121For now, support questions should be sent to:
122
123E<lt>netdri@dotandco.comE<gt>
124
125Please also see the SUPPORT file in the distribution.
126
127=head1 SEE ALSO
128
129http://www.dotandco.com/services/software/Net-DRI/
130
131=head1 AUTHOR
132
133Trond Haugen, E<lt>info@norid.noE<gt>.
134
135=head1 COPYRIGHT
136
137Copyright (c) 2008-2010 UNINETT Norid AS, E<lt>http://www.norid.noE<gt>,
138Trond Haugen E<lt>info@norid.noE<gt>.
139All rights reserved.
140
141This program is free software; you can redistribute it and/or modify
142it under the terms of the GNU General Public License as published by
143the Free Software Foundation; either version 2 of the License, or
144(at your option) any later version.
145
146See the LICENSE file that comes with this distribution for more details.
147
148=cut
149
150####################################################################################################
151
152sub validate {
153    my ( $self, $change ) = @_;
154    $change ||= 0;
155
156    my @errs;
157
158    if ( !$change ) {
159        Net::DRI::Exception::usererr_insufficient_parameters(
160            'Invalid contact information: name/city/cc/email/auth/srid mandatory'
161            )
162            unless $self->name()
163            && $self->city()
164            && $self->cc()
165            && $self->email()
166            && $self->auth()
167            && $self->srid();
168        Net::DRI::Exception::usererr_insufficient_parameters(
169            'Invalid contact information: org is not allowed for .NO')
170            if ( $self->org() );
171        Net::DRI::Exception::usererr_insufficient_parameters(
172            'Invalid contact information: type mandatory')
173            unless ( $self->type() );
174    }
175
176    push @errs,'srid' if ($self->srid() && ! Net::DRI::Util::xml_is_token($self->srid(),3,16));
177    push @errs, 'name'
178        if ( $self->name()
179        && grep { !Net::DRI::Util::xml_is_normalizedstring( $_, 1, 255 ) }
180        ( $self->name() ) );
181    push @errs, 'org'
182        if ( $self->org()
183        && grep { !Net::DRI::Util::xml_is_normalizedstring( $_, undef, 255 ) }
184        ( $self->org() ) );
185
186    my @rs = ( $self->street() );
187
188    foreach my $i ( 0, 1 ) {
189        next unless $rs[$i];
190        push @errs, 'street'
191            if (
192            ( ref( $rs[$i] ) ne 'ARRAY' ) || ( @{ $rs[$i] } > 3 ) || (
193                grep {
194                    !Net::DRI::Util::xml_is_normalizedstring( $_, undef, 255 )
195                } @{ $rs[$i] }
196            )
197            );
198    }
199
200    push @errs, 'city'
201        if ( $self->city()
202        && grep { !Net::DRI::Util::xml_is_normalizedstring( $_, 1, 255 ) }
203        ( $self->city() ) );
204    push @errs, 'sp'
205        if ( $self->sp()
206        && grep { !Net::DRI::Util::xml_is_normalizedstring( $_, undef, 255 ) }
207        ( $self->sp() ) );
208    push @errs, 'pc'
209        if ( $self->pc()
210        && grep { !Net::DRI::Util::xml_is_token( $_, undef, 16 ) }
211        ( $self->pc() ) );
212    push @errs, 'cc'
213        if ( $self->cc() && grep { !Net::DRI::Util::xml_is_token( $_, 2, 2 ) }
214        ( $self->cc() ) );
215    push @errs, 'cc'
216        if ( $self->cc()
217        && grep { !exists( $Net::DRI::Util::CCA2{ uc($_) } ) }
218        ( $self->cc() ) );
219
220    push @errs, 'voice'
221        if ( $self->voice()
222        && !Net::DRI::Util::xml_is_token( $self->voice(), undef, 17 )
223        && $self->voice() !~ m/^\+[0-9]{1,3}\.[0-9]{1,14}(?:x\d+)?$/mx );
224    push @errs, 'fax'
225        if ( $self->fax()
226        && !Net::DRI::Util::xml_is_token( $self->fax(), undef, 17 )
227        && $self->fax() !~ m/^\+[0-9]{1,3}\.[0-9]{1,14}(?:x\d+)?$/mx );
228    push @errs, 'email'
229        if (
230        $self->email()
231        && !(
232            Net::DRI::Util::xml_is_token( $self->email(), 1, undef )
233            && Email::Valid->rfc822( $self->email() )
234        )
235        );
236
237    my $ra = $self->auth();
238    push @errs, 'auth'
239        if ( $ra
240        && ( ref($ra) eq 'HASH' )
241        && exists( $ra->{pw} )
242        && !Net::DRI::Util::xml_is_normalizedstring( $ra->{pw} ) );
243
244    # .NO
245    my $t = $self->type();
246    push @errs, 'type' if ( $t && $t !~ m/^(?:person|organization|role)$/mx );
247
248    $t = $self->identity();
249
250    if ($t) {
251        my $ty = $t->{type};
252        my $va = $t->{value};
253        push @errs, 'identity type'
254            if ( $ty
255            && $ty
256            !~ m/^(?:organizationNumber|localIdentity|nationalIdentityNumber)$/mx
257            );
258
259        # let the server handle further validation of what identity syntax
260        # and values are legal
261    }
262    $t = $self->mobilephone();
263    push @errs, 'mobilephone'
264        if ( $t
265        && !Net::DRI::Util::xml_is_token( $t, undef, 17 )
266        && $t !~ m/^\+[0-9]{1,3}\.[0-9]{1,14}(?:x\d+)?$/mx );
267
268    #
269    foreach my $el ( 'organization', 'rolecontact', 'xemail' ) {
270        if ( $t = $self->$el() ) {    # option, as scalar or array
271            my @em;
272            my $er;
273
274            if ($change) {
275                if ( ref($t) eq 'HASH' ) {
276                    foreach my $s ( 'add', 'del' ) {
277                        my $e = $t->{$s};
278                        if ( ref($e) eq 'ARRAY' ) {
279                            push @em, @$e if (@$e);
280                        } else {
281                            push @em, $e if ($e);
282                        }
283                    }
284                } else {
285                    $er .= ":update needs an add/del hash:";
286                }
287            } else {
288                if ( ref($t) eq 'ARRAY' ) {
289                    push @em, @$t if (@$t);
290                } else {
291                    push @em, $t if ($t);
292                }
293            }
294            foreach my $e (@em) {
295                if ( $el eq 'xemail' ) {
296                    $er .= " $e "
297                        if (
298                        $e
299                        && !(
300                            Net::DRI::Util::xml_is_token( $e, 1, undef )
301                            && Email::Valid->rfc822($e)
302                        )
303                        );
304                } else {
305                    $er .= " $e "
306                        if ( $e
307                        && !Net::DRI::Util::xml_is_token( $e, 3, 16 ) );
308                }
309                push @errs, "xemail:$er" if ($er);
310            }
311        }
312    }
313
314    ## Check that xdisclose only contains mobilePhone
315    if ( my $d = $self->xdisclose() ) {
316        unless ( $d
317            && ( ref($d) eq 'HASH' )
318            && ( scalar( keys(%$d) ) == 1 )
319            && ( $d->{mobilePhone} == 1 || $d->{mobilePhone} == 0 ) )
320        {
321            push @errs, 'xdisclose';
322        }
323    }
324    Net::DRI::Exception::usererr_invalid_parameters(
325        'Invalid contact information: ' . join( '/', @errs ) )
326        if @errs;
327    return 1;    ## everything ok.
328}
329
330sub init {
331    my ( $self, $what, $ndr ) = @_;
332
333    if ( $what eq 'create' ) {
334        my $a = $self->auth();
335        $self->auth( { pw => '' } )
336            unless ( $a && ( ref($a) eq 'HASH' ) && exists( $a->{pw} ) )
337            ;    ## Mandatory in EPP
338        $self->srid('auto')
339            unless defined( $self->srid() );    ## we can not choose the ID
340    }
341    return;
342}
343
344####################################################################################################
3451;
346