File Coverage

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