File Coverage

blib/lib/Business/cXML/Transmission.pm
Criterion Covered Total %
statement 217 217 100.0
branch 101 102 99.0
condition 19 19 100.0
subroutine 35 35 100.0
pod 21 21 100.0
total 393 394 99.7


line stmt bran cond sub pod time code
1             =encoding utf-8
2              
3             =head1 NAME
4              
5             Business::cXML::Transmission - cXML transmission
6              
7             =head1 SYNOPSIS
8              
9             use Business::cXML::Transmission;
10             $msg = parse Business::cXML::Transmission $incoming_cxml_string;
11              
12             =head1 DESCRIPTION
13              
14             Parser and compiler for cXML transmissions.
15              
16             See: L<http://xml.cxml.org/current/cXMLUsersGuide.pdf>
17              
18             The creation of these transmissions should normally be left to
19             L<Business::cXML>, which does some handy initialization for you. Of main
20             concern for manual processing is our L</payload()>.
21              
22             =cut
23              
24 7     7   159877 use 5.014;
  7         24  
25 7     7   30 use strict;
  7         57  
  7         217  
26              
27              
28             use Business::cXML::Credential;
29 7     7   2494 use Business::cXML::Utils qw(current_datetime cxml_timestamp);
  7         17  
  7         229  
30 7     7   44 use XML::LibXML;
  7         12  
  7         367  
31 7     7   39 use Clone qw(clone);
  7         16  
  7         60  
32 7     7   1117 use DateTime;
  7         15  
  7         245  
33 7     7   34 use HTML::Entities;
  7         17  
  7         138  
34 7     7   3236 use MIME::Base64;
  7         34831  
  7         601  
35 7     7   2551 use Sys::Hostname;
  7         3338  
  7         340  
36 7     7   96  
  7         17  
  7         315  
37             use constant {
38             CXML_CLASS_MESSAGE => 1,
39 7         17621 CXML_CLASS_REQUEST => 2,
40             CXML_CLASS_RESPONSE => 3,
41             };
42 7     7   41  
  7         35  
43             =head1 METHODS
44              
45             =over
46              
47             =item C<B<new>( [I<$input>] )>
48              
49             Without I<C<$input>>, returns an empty L<Business::cXML::Transmission> ready
50             to be populated.
51              
52             With I<C<$input>>, returns a L<Business::cXML::Transmission> if parsing was
53             possible, or an arrayref with two elements if there was an error. The first
54             element is a status code, and the second contains a string with more details,
55             if available. Input is expected to be a full XML document string, optionally
56             encoded in Base64 (i.e. the contents of a C<cxml-base64> form variable).
57              
58             Possible codes:
59              
60             =over
61              
62             =item C<406>
63              
64             The input XML is invalid
65              
66             =item C<400>
67              
68             The input XML is valid, but the cXML structure is incomprehensible
69              
70             =back
71              
72             =cut
73              
74             my ($class, $input) = @_;
75              
76 52     52 1 457818 my $now = current_datetime();
77             my $self = {
78 52         224 string => undef,
79 52         67888 xml_doc => undef,
80             xml_root => undef,
81             _xml_payload => undef,
82             _payload => undef,
83             _timestamp => cxml_timestamp($now),
84             epoch => $now->strftime('%s'),
85             hostname => hostname,
86             randint => int(rand(99999999)),
87             pid => $$,
88             test => 0,
89             _lang => 'en-US',
90             _id => undef,
91             _inreplyto => undef,
92             status => {
93             code => 200,
94             text => 'OK',
95             description => '',
96             },
97             class => '',
98             _type => '',
99             _from => undef,
100             _to => undef,
101             _sender => undef,
102             };
103             bless $self, $class;
104              
105 52         13206 if ($input) {
106             my $doc;
107 52 100       179 $input = decode_base64($input) unless ($input =~ /^\s*</);
108 29         70 eval {
109 29 100       217 $self->{xml_doc} = ($doc = XML::LibXML->load_xml(string => $input));
110 29         57 };
111 29         227 return [ 400, $@ ] if $@;
112             eval {
113 29 100       11150 my $dtd = XML::LibXML::Dtd->new(undef, "http://xml.cxml.org/schemas/cXML/" . $Business::cXML::CXML_VERSION . "/cXML.dtd");
114 28         62 $doc->validate($dtd);
115 28         134489 };
116 28         17431 if ($@) {
117             eval {
118 28 100       1282 $doc->validate();
119 2         780 };
120 2         8793 return [ 406, $@ ] if $@;
121             };
122 2 50       243  
123             $self->{xml_root} = ($doc = $doc->documentElement);
124             $doc->ferry($self, {
125 26         585 version => '__UNIMPLEMENTED',
126 26         646 payloadID => '_id',
127             # timestamp is implicit
128             signatureVersion => '__UNIMPLEMENTED',
129             'xml:lang' => '_lang',
130             Header => {
131             From => [ '_from', 'Business::cXML::Credential' ],
132             To => [ '_to', 'Business::cXML::Credential' ],
133             Sender => [ '_sender', 'Business::cXML::Credential' ],
134             Path => '__UNIMPLEMENTED',
135             OriginalDocument => '__UNIMPLEMENTED',
136             },
137             Request => [ '__IGNORE', \&_new_payload ],
138             Response => [ '__IGNORE', \&_new_payload ],
139             Message => [ '__IGNORE', \&_new_payload ],
140             'ds:Signature' => '__UNIMPLEMENTED',
141             });
142             $self->_rebuild_payload();
143             } else {
144 26         2606 # Create a brand new XML document from scratch.
145             my $doc = $self->{xml_doc} = XML::LibXML::Document->new('1.0', 'UTF-8');
146             $doc->createInternalSubset('cXML', undef, "http://xml.cxml.org/schemas/cXML/" . $Business::cXML::CXML_VERSION . "/cXML.dtd");
147 23         315 my $root = $self->{xml_root} = $doc->createElement('cXML');
148 23         261 $self->{_id} = $self->{epoch} . '.' . $self->{pid} . '.' . $self->{randint} . '@' . $self->{hostname}; # payloadID/inReplyTo
149 23         348 $root->attr(
150 23         120 payloadID => $self->{_id},
151             timestamp => $self->{_timestamp},
152             'xml:lang' => $self->{_lang},
153             );
154             # UNIMPLEMENTED cXML: version? signatureVersion?
155 23         140 $doc->setDocumentElement($root);
156              
157 23         1133 # Something initially valid which will be replaced by the user
158             $self->{_xml_payload} = $doc->createElement('ProfileRequest');
159             $self->{class} = CXML_CLASS_REQUEST;
160 23         429 $self->{_type} = 'Profile';
161 23         60 };
162 23         61 $self->{_from} = Business::cXML::Credential->new('From') unless defined $self->{_from};
163             $self->{_to} = Business::cXML::Credential->new('To') unless defined $self->{_to};
164 49 100       358 $self->{_sender} = Business::cXML::Credential->new('Sender') unless defined $self->{_sender};
165 49 100       193  
166 49 100       174 return $self;
167             }
168 49         448  
169             my ($self, $msg) = @_;
170             my $status;
171              
172 26     26   3287 $self->is_test(1) if (exists $msg->{deploymentMode} && $msg->{deploymentMode} eq 'test');
173 26         53 $self->{_inreplyto} = $msg->{inReplyTo} if exists $msg->{inReplyTo};
174             # UNIMPLEMENTED Message/Request/Response: Id?
175 26 100 100     78  
176 26 100       1660 foreach ($msg->childNodes) {
177             if ($_->nodeName eq 'Status') {
178             $status = $_;
179 26         775 } elsif ($_->nodeType == XML_ELEMENT_NODE) {
180 80 100       687 $self->{_xml_payload} = $msg = $_;
    100          
181 4         14 };
182             };
183 25         89 my $className;
184             ($self->{_type}, $className) = $msg->nodeName =~ /^(.*)(Request|Response|Message)$/;
185             $self->{class} = CXML_CLASS_MESSAGE if $className eq 'Message';
186 26         101 $self->{class} = CXML_CLASS_REQUEST if $className eq 'Request';
187 26         317 $self->{class} = CXML_CLASS_RESPONSE if $className eq 'Response';
188 26 100       104  
189 26 100       101 if ($status) {
190 26 100       73 $self->status($status->{code}, $status->textContent);
191             } else {
192 26 100       71 $self->status(200);
193 4         28 };
194              
195 22         75 return undef;
196             }
197              
198 26         91 my ($self) = @_;
199              
200             return if defined $self->{_payload};
201              
202 71     71   138 my $class = 'Message';
203             $class = 'Request' if $self->is_request;
204 71 100       202 $class = 'Response' if $self->is_response;
205              
206 44         83 $class = 'Business::cXML::' . $class . '::' . $self->type;
207 44 100       114  
208 44 100       121 eval {
209             my $file = $class;
210 44         178 $file =~ s|::|/|g;
211             require "$file.pm";
212 44         85 $self->{_payload} = $class->new($self->{_xml_payload});
213 44         78 };
214 44         219 # Payload remains safely undef for unknown classes-types.
215 44         4952 }
216 35         379  
217             =item C<B<toForm>( I<%arguments> )>
218              
219             In a scalar context, returns an HTML string representation of the current cXML
220             data structure, in cXML "URL-Form-Encoding" (a C<form> with a hidden
221             C<cxml-base64> value). Returns an empty string if we have an internal error.
222              
223             To help identify problems, in a list context it returns an error string (or
224             C<undef>) and the HTML string (probably empty, depending on the type of error).
225              
226             Possible I<C<%arguments>> keys:
227              
228             =over
229              
230             =item C<B<url>>
231              
232             Mandatory, should be from a C<PunchOutSetupRequest/BrowserFormPost>.
233              
234             =item C<B<target>>
235              
236             Optional, the HTML frame target to specify in the FORM
237              
238             =item C<B<submit_button>>
239              
240             Optional, override submit button HTML with your own
241              
242             =back
243              
244             =cut
245              
246             my ($self, %args) = @_;
247             my $url = encode_entities($args{url} || '');
248             my $submit = '<input type="submit">';
249             $submit = $args{submit_button} if exists $args{submit_button};
250             my $target = '';
251 6     6 1 17 $target = ' target="' . encode_entities($args{target}) . '"' if defined $args{target};
252 6   100     37  
253 6         90 my ($err, $msg) = $self->toString;
254 6 100       15 return ($err, '') if defined $err;
255 6         11  
256 6 100       14 $msg = encode_base64($msg, '');
257             return (undef, "<form method=\"post\" action=\"$url\"$target><input type=\"hidden\" name=\"cxml-base64\" value=\"$msg\">$submit</form>");
258 6         26 }
259 6 100       206  
260             =item C<B<toString>()>
261 5         55  
262 5         35 In a scalar context, returns an XML string representation of the current cXML
263             data structure.
264              
265             In the event that our XML document were not valid, a hard-coded C<500> status
266             C<Response> with explanation will be returned instead of the prepared
267             transmission.
268              
269             To help identify problems, in a list context it returns an error string (or
270             C<undef>) and the XML string.
271              
272             =cut
273              
274             my ($self) = @_;
275             my $head = qq(<?xml version="1.0"?>\n)
276             . qq(<!DOCTYPE cXML SYSTEM "http://xml.cxml.org/schemas/cXML/)
277             . $Business::cXML::CXML_VERSION
278             . qq(/cXML.dtd">\n);
279             eval {
280 41     41   721 $self->{xml_doc}->validate();
281 41         126 };
282             if ($@) {
283             return ($@, $head . qq(<cXML timestamp=") . $self->{_timestamp} . qq(" payloadID=") . $self->{_id} . qq(" xml:lang="en-US"><Response><Status code="500" text="Internal Server Error">) . encode_entities($@) . qq(</Status></Response></cXML>));
284             };
285 41         76 return (undef, $head . $self->{xml_root}->toString);
286 41         165430 }
287              
288 41 100       793 my ($self) = @_;
289 3         513  
290             return (undef, $self->{string}) if defined $self->{string};
291 38         2377  
292             $_->unbindNode() foreach ($self->{xml_root}->childNodes); # Start from guaranteed empty doc
293              
294              
295 45     45 1 22492 unless ($self->is_response) {
296             my $header = $self->{xml_root}->add('Header');
297 45 100       174 $header->add($self->{_from}->to_node($header));
298             $header->add($self->{_to}->to_node($header));
299 41         180 $self->{_sender}->secret(undef) if $self->is_message; # No SharedSecret in Message
300             $header->add($self->{_sender}->to_node($header));
301             # UNIMPLEMENTED: (Path OriginalDocument)?
302 41 100       745 };
303 21         81  
304 21         905 my $wrapper;
305 21         502 my $className;
306 21 100       484 $className = 'Message' if $self->is_message;
307 21         76 $className = 'Request' if $self->is_request;
308             $className = 'Response' if $self->is_response;
309             $wrapper = $self->{xml_root}->add($className);
310             $wrapper->attr(deploymentMode => ($self->{test} ? 'test' : 'production')) unless $self->is_response;
311 41         651 $wrapper->attr(inReplyTo => $self->{_inreplyto}) if $self->is_message && $self->{_inreplyto};
312             # UNIMPLEMENTED Message/Request/Response: Id?
313 41 100       269  
314 41 100       114 if ($self->is_response || ($self->is_message && $self->{status}{code} != 200)) {
315 41 100       90 $wrapper->add('Status', $self->{status}{description},
316 41         136 code => $self->{status}{code},
317 41 100       1487 'xml:lang' => 'en-US', # Our status descriptions are always in English
    100          
318 41 100 100     460 text => $self->{status}{text}
319             );
320             };
321 41 100 100     137  
      100        
322             # No payload on error or ping response
323             return $self->_valid_string if $self->{status}{code} >= 300 || $self->{status}{description} eq 'Pong!';
324              
325             if (ref $self->{_payload}) {
326 21         94 # Optional native payload has precedence over XML payload.
327             $self->{_xml_payload} = $self->{_payload}->to_node($self->{xml_root});
328             };
329             $self->{_xml_payload}->setNodeName($self->{_type} . $className);
330 41 100 100     1968 $wrapper->addChild($self->{_xml_payload});
331              
332 28 100       113 return $self->_valid_string;
333             }
334 18         76  
335             =item C<B<freeze>()>
336 28         357  
337 28         426 Store the results of L</toString()> internally and return it (in a scalar
338             context). This is what L</toString()> will always return until L</thaw()> is
339 28         67 eventually called. Has no effect if the transmission is already frozen.
340              
341             This helps comply with cXML's recommendation that multiple attempts to deliver
342             a transmission have the same C<payloadID> and C<timestamp> values.
343              
344             To help identify problems, in a list context it returns an error string (or
345             C<undef>) and the XML string. Note that multiple calls will only yield an error
346             (if any) on the first call, and C<undef> thereafter.
347              
348             =cut
349              
350             my ($self) = @_;
351             my $err;
352             my $str;
353             ($err, $str) = $self->toString;
354             $self->{string} = $str;
355             return ($err, $self->{string});
356             }
357              
358 6     6 1 34 =item C<B<thaw>()>
359 6         12  
360             Destroy the internally stored results of L</toString()>. Modifications to
361 6         18 internal data will once again produce changes in what L</toString()> returns.
362 6         318  
363 6         26 =cut
364              
365             my ($self) = @_;
366             $self->{string} = undef;
367             }
368              
369             =item C<B<reply_to>( REQUEST )>
370              
371             Initialize L</type>, L</inreplyto>, L</from>, L</to> and L</sender> in
372             reciprocity with request data.
373              
374 2     2 1 618 =cut
375 2         8  
376             my ($self, $req) = @_;
377              
378             $self->{_type} = $req->{_type};
379              
380             $self->inreplyto($req->{_id});
381             $self->is_test($req->is_test);
382              
383             $self->sender->copy($req->to);
384             $self->sender->contact(undef);
385             $self->sender->secret($req->sender->secret);
386 15     15 1 37  
387             $self->from->copy($req->to);
388 15         38 $self->from->contact(undef);
389              
390 15         55 $self->to->copy($req->from);
391 15         53 $self->to->contact(undef);
392             }
393 15         47  
394 15         50 =item C<B<from>( [I<%properties>] )>
395 15         39  
396             =item C<B<to>( [I<%properties>] )>
397 15         60  
398 15         47 =item C<B<sender>( [I<%properties>] )>
399              
400 15         39 Returns the associated L<Business::cXML::Credential> object.
401 15         45  
402             With I<C<%properties>>, it first calls
403             L<Business::cXML::Credential::set()|Business::cXML::Credential/set>. In the
404             case of C<from()>, sets both C<from> and C<sender> objects, therefore if you
405             need to override this behavior, be sure to set C<sender> after C<from>.
406              
407             Note that you could also pass a single L<Business::cXML::Credential> object,
408             in which case it would replace the current one outright. In the case of
409             C<from()>, note that the object reference will be given to C<sender> intact
410             and a clone will be copied into C<from()>.
411              
412             =cut
413              
414             my ($self, %props) = @_;
415             if (ref($_[1])) {
416             $self->{_from} = clone($self->{_sender} = $_[1]);
417             } elsif (%props) {
418             $self->{_sender}->set(%props);
419             $self->{_from}->set(%props);
420             };
421             return $self->{_from};
422             }
423              
424             my ($self, %props) = @_;
425 55     55 1 140 if (ref($_[1])) {
426 55 100       150 $self->{_to} = $_[1];
    100          
427 1         15 } elsif (%props) {
428             $self->{_to}->set(%props);
429 4         37 };
430 4         15 return $self->{_to};
431             }
432 55         240  
433             my ($self, %props) = @_;
434             if (ref($_[1])) {
435             $self->{_sender} = $_[1];
436 67     67 1 143 } elsif (%props) {
437 67 100       178 $self->{_sender}->set(%props);
    100          
438 1         4 };
439             return $self->{_sender};
440 4         19 }
441              
442 67         236 =item C<B<is_test>( [I<$bool>] )>
443              
444             Get/set whether this transmission is in test mode (vs production).
445              
446 69     69 1 168 =cut
447 69 100       186  
    100          
448 1         4 my ($self, $test) = @_;
449             $self->{test} = ($test ? 1 : 0) if @_ > 1;
450 1         6 return $self->{test};
451             }
452 69         304  
453             =item C<B<timestamp>>
454              
455             Read-only, the transmission's creation date/time.
456              
457             =cut
458              
459              
460             =item C<B<id>>
461              
462 46     46 1 680 Read-only, the transmission's payload ID.
463 46 100       158  
    100          
464 46         109 =cut
465              
466              
467             =item C<B<inreplyto>( [I<$id>] )>
468              
469             Get/set the payload ID of the transmission we're responding to.
470              
471             =cut
472              
473 28     28 1 18157 my ($self, $id) = @_;
474             $self->{_inreplyto} = $id if @_ > 1;
475             return $self->{_inreplyto};
476             }
477              
478             =item C<B<is_message>( [I<$bool>] )>
479              
480             =item C<B<is_request>( [I<$bool>] )>
481 3     3 1 800  
482             =item C<B<is_response>( [I<$bool>] )>
483              
484             Get/set whether this transmission is a C<Message>, C<Request> or C<Response>.
485             The transmission's class is only modified when I<C<$bool>> is true.
486              
487             Setting any of these loses any data currently in L</payload>, so be sure to do
488             it early!
489              
490 17     17 1 52 =cut
491 17 100       77  
492 17         44 my ($self, $bool) = @_;
493             if ($bool) {
494             $self->{class} = CXML_CLASS_MESSAGE;
495             $self->{_payload} = undef;
496             };
497             return $self->{class} == CXML_CLASS_MESSAGE;
498             }
499              
500             my ($self, $bool) = @_;
501             if ($bool) {
502             $self->{class} = CXML_CLASS_REQUEST;
503             $self->{_payload} = undef;
504             };
505             return $self->{class} == CXML_CLASS_REQUEST;
506             }
507              
508             my ($self, $bool) = @_;
509             if ($bool) {
510 127     127 1 445 $self->{class} = CXML_CLASS_RESPONSE;
511 127 100       289 $self->{_payload} = undef;
512 3         7 };
513 3         6 return $self->{class} == CXML_CLASS_RESPONSE;
514             }
515 127         531  
516             =item C<B<lang>( [I<$code>] )>
517              
518             Get/set the language for displayable strings included in this transmission.
519 87     87 1 184 Can be changed, but cannot be unset. Default: C<en-US>. For an incoming
520 87 100       197 transmission, this should be a hint to the user's preferred display language.
521 2         4  
522 2         6 =cut
523              
524 87         291 my ($self, $lang) = @_;
525             if (defined $lang) {
526             $self->{_lang} = $lang;
527             $self->{xml_root}->attr('xml:lang' => $lang);
528 225     225 1 829 };
529 225 100       454 return $self->{_lang};
530 17         36 }
531 17         32  
532             =item C<B<type>( [I<$name>] )>
533 225         792  
534             Get/set the type of document. Can be changed, but cannot be unset. For
535             example: C<Profile> or C<PunchOutSetup>.
536              
537             B<Caution:> Setting a type loses any data currently in L</payload>, so be sure
538             to do it early!
539              
540             =cut
541              
542             my ($self, $type) = @_;
543             if (defined $type) {
544             $self->{_type} = $type;
545 2     2 1 6 $self->{_payload} = undef;
546 2 100       6 };
547 1         3 return $self->{_type};
548 1         9 }
549              
550 2         44 =item C<B<payload>>
551              
552             Read-only. If a native implementation for the current transmission type is
553             available (i.e. L<Business::cXML::Request::PunchOutSetup>), it is made
554             available ready-to-use via this property. For incoming transmission, it is
555             fully populated with parsed data.
556              
557             If accessed after previously using L</xml_payload>, this would cause the
558             native payload to be recreated from the XML payload as it currently stands,
559             preserving any (valid) changes done on the XML side into the native version.
560              
561             =cut
562              
563             my ($self) = @_;
564 77     77 1 1544 $self->_rebuild_payload();
565 77 100       158 return $self->{_payload};
566 5         9 }
567 5         10  
568             =item C<B<xml_payload>>
569 77         268  
570             Read-only. The L<XML::LibXML::Element> representing the "SomethingMessage",
571             "SomethingRequest" or "SomethingResponse" section of the transmission.
572              
573             Its node name is automatically determined in L</toString()>, but you are free
574             to add/change other attributes and child elements. Returns C<undef> for
575             incoming (parsed) transmissions.
576              
577             Accessing this property causes the destruction of L</payload> if it existed.
578             This is in place so that your own parsing of LibXML structures takes
579             precedence over ours to hopefully make future updates seamless in the event of
580             conflicts. Thus, while you can modify the native payload, then modify the XML
581             version, B<switching back again to native would lose all data>.
582              
583             =cut
584              
585             my ($self) = @_;
586 45     45 1 13303 $self->{_payload} = undef;
587 45         106 return $self->{_xml_payload};
588 45         208 }
589              
590             =item C<B<status>( [ I<$code>, [$description] ] )>
591              
592             Get/set transmission's cXML 3-digit status code. (None by default.)
593              
594             I<C<$description>> is an optional explanatory text that may be included in the
595             status of a response.
596              
597             cXML defines the following status codes, which are the only ones accepted.
598              
599             B<Success:>
600              
601             =over
602              
603             =item C<200> OK
604              
605             Request executed and delivered, cXML itself has no error
606              
607             =item C<201> Accepted
608              
609 5     5 1 22 Not yet processed, we'll send a StatusUpdate later
610 5         23  
611 5         20 =item C<204> No Content
612              
613             Request won't get a Response from server (i.e. punch-out cart didn't change)
614              
615             =item C<280> [Described like 201]
616              
617             =item C<281> [Described like 201]
618              
619             =back
620              
621             B<Permanent errors:>
622              
623             =over
624              
625             =item C<400> Bad Request
626              
627             Parsed OK but unacceptable
628              
629             =item C<401> Unauthorized
630              
631             Request/Sender credentials not recognized
632              
633             =item C<402> Payment Required
634              
635             Need complete Payment element
636              
637             =item C<403> Forbidden
638              
639             Insufficient privileges
640              
641             =item C<406> Not Acceptable
642              
643             Request unacceptable, likely parsing failure
644              
645             =item C<409> Conflict
646              
647             Current state incompatible with Request
648              
649             =item C<412> Precondition Failed
650              
651             Unlike 403, the precondition was described in a previous response
652              
653             =item C<417> Expectation Failed
654              
655             Request implied a resource condition that was not met, such as an unknown one
656              
657             =item C<450> Not Implemented
658              
659             Server doesn't implement that Request (so client ignored server's profile?)
660              
661             =item C<475> Signature Required
662              
663             Document missing required digital signature
664              
665             =item C<476> Signature Verification Failed
666              
667             Failed signature or unsupported signature algorithm
668              
669             =item C<477> Signature Unacceptable
670              
671             Valid signature but otherwise rejected
672              
673             =back
674              
675             B<Transient errors:>
676              
677             =over
678              
679             =item C<500> Internal Server Error
680              
681             Server was unable to complete the Request (temporary)
682              
683             =item C<550> Unable to reach cXML server
684              
685             Applies to intermediate hubs (temporary)
686              
687             =item C<551> Unable to forward request
688              
689             Because of supplier misconfiguration (temporary)
690              
691             =item C<560> Temporary server error
692              
693             Maintenance, etc. (temporary)
694              
695             =back
696              
697             =cut
698              
699             my %CXML_STATUS_CODES = (
700             200 => 'OK',
701             201 => 'Accepted',
702             204 => 'No Content',
703             280 => '',
704             281 => '',
705              
706             400 => 'Bad Request',
707             401 => 'Unauthorized',
708             402 => 'Payment Required',
709             403 => 'Forbidden',
710             406 => 'Not Acceptable',
711             409 => 'Conflict',
712             412 => 'Precondition Failed',
713             417 => 'Expectation Failed',
714             450 => 'Not Implemented',
715             475 => 'Signature Required',
716             476 => 'Signature Verification Failed',
717             477 => 'Signature Unacceptable',
718              
719             500 => 'Internal Server Error',
720             550 => 'Unable to reach cXML server',
721             551 => 'Unable to forward request',
722             560 => 'Temporary server error',
723             );
724              
725             my ($self, $code, $desc) = @_;
726             if ($code) {
727             if (exists $CXML_STATUS_CODES{$code}) {
728             $self->{status}{code} = $code;
729             $self->{status}{text} = $CXML_STATUS_CODES{$code};
730             $self->{status}{description} = $desc || '';
731             } else {
732             # We were given an unsupported code, this is BAD!
733             $self->{status}{code} = 500;
734             $self->{status}{text} = $CXML_STATUS_CODES{500};
735             $self->{status}{description} = "Unsupported actual status code '$code'.";
736             };
737             };
738             return $self->{status}{code};
739             }
740              
741             =back
742              
743             =head1 AUTHOR
744              
745             Stéphane Lavergne L<https://github.com/vphantom>
746              
747             =head1 ACKNOWLEDGEMENTS
748              
749             Graph X Design Inc. L<https://www.gxd.ca/> sponsored this project.
750 72     72 1 344  
751 72 100       307 =head1 COPYRIGHT & LICENSE
752 53 100       163  
753 52         165 Copyright (c) 2017-2018 Stéphane Lavergne L<https://github.com/vphantom>
754 52         129  
755 52   100     206 Permission is hereby granted, free of charge, to any person obtaining a copy
756             of this software and associated documentation files (the "Software"), to deal
757             in the Software without restriction, including without limitation the rights
758 1         4 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
759 1         3 copies of the Software, and to permit persons to whom the Software is
760 1         3 furnished to do so, subject to the following conditions:
761              
762             The above copyright notice and this permission notice shall be included in all
763 72         251 copies or substantial portions of the Software.
764              
765             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
766             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
767             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
768             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
769             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
770             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
771             SOFTWARE.
772              
773             =cut
774              
775             1;