File Coverage

blib/lib/Net/IPP/IPPRequest.pm
Criterion Covered Total %
statement 33 116 28.4
branch 0 36 0.0
condition 0 9 0.0
subroutine 11 18 61.1
pod 0 7 0.0
total 44 186 23.6


line stmt bran cond sub pod time code
1             ###
2             # Copyright (c) 2004 Matthias Hilbig
3             # All rights reserved.
4             #
5             # This program is free software; you may redistribute it and/or modify it
6             # under the same terms as Perl itself.
7             #
8             # Perl API for sending IPP requests
9             #
10             # Uses: LWP::UserAgent
11             # Carp
12             #
13             # Perl files: lib/Net/IPP/IPPRequest.pm - main API file
14             # lib/Net/IPP/IPPAttribute.pm - encodes/decodes IPP attributes
15             # lib/Net/IPP/IPPUtil.pm - helper functions
16             # lib/Net/IPP/IPPMethods.pm - ippRequest wrappers
17             # lib/Net/IPP/IPP.pm - contains all IPP constants
18             # sample/ipptest.pl - IPP lowlevel access example
19             # sample/printerAttributes.pl - show IPP attributes of printer
20             # sample/showJobs.pl - show IPP jobs of printer
21             # sample/monitorState.pl - monitor Status of printer
22             # sample/monitorJobs.pl - monitor Status of jobs
23             # t/codec.t - Testcases for encoding and
24             # decoding of IPP requests
25             # t/transform.t - Testcases for transformValue
26             # method
27             # for Changes look at the Changes file.
28             #
29             #------------------------------------------------------------------------------
30              
31             package Net::IPP::IPPRequest;
32              
33             our $VERSION = "0.1";
34              
35             #TODO: which perl version is required? Maybe 5.6 or something like that
36             #use 5.008;
37              
38 2     2   75770 use strict;
  2         6  
  2         86  
39 2     2   13 use warnings;
  2         4  
  2         65  
40              
41 2     2   10 use Carp;
  2         6  
  2         213  
42 2     2   80372 use LWP::UserAgent;
  2         235636  
  2         85  
43              
44 2     2   2019 use Net::IPP::IPP qw(:all);
  2         7  
  2         2280  
45 2     2   1467 use Net::IPP::IPPAttribute qw(:all);
  2         7  
  2         512  
46 2     2   1295 use Net::IPP::IPPUtil qw(:all);
  2         7  
  2         1230  
47              
48             require Exporter;
49             our @ISA = ("Exporter");
50             our @EXPORT_OK = qw(ippRequest);
51             our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK );
52              
53              
54             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
55             # ENCODING
56             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
57              
58             ###
59             # encode IPP Header
60             #
61             # Parameter: $operation - IPP operation
62             # $requestId - id of IPP request
63             #
64             # Return: byte encoded IPP Header (length: 8 Byte)
65             #
66              
67             sub encodeIPPHeader($$) {
68 0     0 0   my $operation = shift;
69 0           my $requestId = shift;
70 0 0         print("Operation: $operation, RequestID: $requestId\n") if &DEBUG;
71 0           return pack("CCnN",
72             &IPP_MAJOR_VERSION,
73             &IPP_MINOR_VERSION,
74             $operation,
75             $requestId
76             );
77             }
78              
79              
80             ###
81             # encode an IPP group with all attributes
82             #
83             # Parameter: $attributes - reference to IPP attributes
84             #
85             # Return: byte encoding of attributes
86             #
87              
88             # TODO: RFC requires ascending order of groups(Idea: test if type numbers of following groups are >=
89             # previous group type)
90             sub encodeGroups($) {
91 0     0 0   my $attributes = shift;
92 0           my $bytes;
93            
94 0 0         if (!exists($attributes->{&TYPE})) {
95 0           confess ("Type missing in group.");
96             }
97            
98 0           $bytes = pack("C", $attributes->{&TYPE});
99            
100             #
101             # "attributes-charset" must be first, "attributes-natural-language"
102             # must be second attribute in operation group, also ignore
103             # these two attributes in all groups except the operation group
104 2     2   15 use constant att_charset => "attributes-charset";
  2         4  
  2         142  
105 2     2   10 use constant att_language => "attributes-natural-language";
  2         4  
  2         812  
106              
107 0 0         if ($attributes->{&TYPE} == &OPERATION_ATTRIBUTES) {
108 0 0         if (!exists($attributes->{att_charset})) {
109 0           $bytes .= encodeAttribute(att_charset, "utf-8");
110             } else {
111 0           $bytes .= encodeAttribute(att_charset, $attributes->{att_charset});
112             }
113            
114 0 0         if (!exists($attributes->{att_language})) {
115 0           $bytes .= encodeAttribute(att_language, "en");
116             } else {
117 0           $bytes .= encodeAttribute(att_language, $attributes->{att_language});
118             }
119             }
120              
121             # encode all other attributes
122 0           while (my ($key, $value) = each %{$attributes}) {
  0            
123 0 0 0       if ($key ne &TYPE
      0        
124             and $key ne att_charset
125             and $key ne att_language) {
126 0           $bytes .= encodeAttribute($key, $value);
127             }
128             }
129 0           return $bytes;
130             }
131              
132              
133             ###
134             # convert an IPP Request from Perl encoding to Byte encoding
135             #
136             # Parameter: $request - IPP request
137             #
138             # Return: byte encoding of $request
139             #
140              
141             sub hashToBytes($) {
142 0     0 0   my $request = shift;
143              
144 0 0 0       if (!exists($request->{&OPERATION}) || !exists($request->{&REQUEST_ID})) {
145 0           confess("Operation or Request-ID is missing in request.");
146             }
147              
148 0           my $bytes = encodeIPPHeader($request->{&OPERATION}, $request->{&REQUEST_ID});
149              
150 0           foreach my $group (@{$request->{&GROUPS}}) {
  0            
151 0           $bytes .= encodeGroups($group);
152             }
153              
154 0           $bytes .= pack("C", &END_OF_ATTRIBUTES);
155              
156 0 0         printBytes($bytes) if &DEBUG;
157 0           return $bytes;
158             }
159              
160             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
161             # DECODING
162             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
163              
164             ###
165             # decode IPPHeader to Perl encoding
166             #
167             # Parameter: $bytes - IPP response
168             # $response - reference to hash for decoding
169             #
170             # Return: decoded values are returned in hash referenced by $response
171             #
172             sub decodeIPPHeader($$) {
173 0     0 0   my $bytes = shift;
174 0           my $response = shift;
175            
176 0           my $data;
177 2     2   11 {use bytes; $data = substr($bytes,0,8);}
  2         5  
  2         9  
  0            
  0            
178            
179 0           my ($majorVersion, $minorVersion, $status, $requestId) = unpack("CCnN", $data);
180            
181 0           $response->{&VERSION} = $majorVersion . "." . $minorVersion;
182            
183 0           $response->{&STATUS} = $status;
184            
185 0           $response->{&REQUEST_ID} = $requestId;
186             }
187              
188              
189             ###
190             # decode all IPP Groups from byte encoding to Perl encoding
191             #
192             # Parameter: $bytes - IPP response
193             # $response - reference to hash for decoding
194             #
195             # Return: decoded values are returned in hash referenced by $response
196             #
197             sub decodeIPPGroups($$) {
198 0     0 0   my $bytes = shift;
199 0           my $response = shift;
200            
201 0           $response->{&GROUPS} = [];
202            
203             # begin directly after IPPHeader (length 8 byte)
204 0           my $offset = 8;
205 0           my $currentGroup = "";
206 0           my $type;
207            
208 0           do {
209             {
210 2     2   432 use bytes;
  2         5  
  2         8  
  0            
211 0           $type = ord(substr($bytes, $offset, 1));
212             }
213            
214 0           $offset++;
215            
216 0 0         if (exists($Net::IPP::IPP::group{$type})) {
    0          
217 0 0         print "group $type found\n" if &DEBUG;
218 0 0         if ($currentGroup) {
219 0           push @{$response->{&GROUPS}}, $currentGroup;
  0            
220             }
221            
222 0 0         if ($type != &END_OF_ATTRIBUTES) {
223 0           $currentGroup = {
224             &TYPE => $type
225             };
226             }
227             } elsif ($currentGroup eq "") {
228 0           confess("Expected Group Tag at begin of IPP response.");
229             } else {
230 0           decodeAttribute($bytes, \$offset, $type, $currentGroup);
231             }
232             } while ($type != &END_OF_ATTRIBUTES);
233             }
234              
235             ###
236             # Decode whole IPP response from byte encoding to perl encoding
237             #
238             # Parameter: $bytes - byte encoded IPP response
239             # $response - reference to hash for decoding
240             #
241             # Return: decoded values are returned in hash referenced by $response
242             #
243              
244             sub bytesToHash($$) {
245 0     0 0   my $bytes = shift;
246 0           my $response = shift;
247            
248 0 0         printBytes($bytes) if &DEBUG;
249            
250 0           decodeIPPHeader($bytes, $response);
251            
252 0           decodeIPPGroups($bytes, $response);
253            
254 0           return $response;
255             }
256              
257             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
258             # IPP Request
259             #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
260              
261             my $userAgent = LWP::UserAgent->new;
262             $userAgent->agent("Perl IPP API/$VERSION");
263              
264             ###
265             # Do the actual IPP request
266             #
267             # Parameter: $request - perl encoded IPP request
268             #
269             # Return: perl encoded IPP response
270             #
271              
272             sub ippRequest($) {
273 0     0 0   my $request = shift;
274 0 0         if (!exists($request->{&URL})) {
275 0           confess("Missed URL in request.");
276             }
277 0           my $url = $request->{&URL};
278            
279 0 0         if (exists($request->{&HP_BUGFIX})) {
280 0           $Net::IPP::IPPAttribute::HP_BUGFIX = $request->{&HP_BUGFIX};
281             } else {
282 0           $Net::IPP::IPPAttribute::HP_BUGFIX = 0;
283             }
284              
285             #convert perl structure to IPP request
286 0           my $content = hashToBytes($request);
287            
288 0 0         if (exists($request->{&DATA})) {
289 0           $content .= $request->{&DATA};
290             }
291            
292             # use LWP to send HTTP Post request
293 0           my $httpRequest = HTTP::Request->new(POST => "$url");
294 0           $httpRequest->content_type('application/ipp');
295 0           $httpRequest->content($content);
296 0           my $result = $userAgent->request($httpRequest);
297            
298 0           my $response = {
299             &HTTP_CODE => $result->code(),
300             &HTTP_MESSAGE => $result->message(),
301             };
302            
303 0 0         if ($result->is_success) {
304             #printBytes($result->content);
305              
306             #convert response back to perl structure
307 0           return bytesToHash($result->content, $response);
308             } else {
309 0           return $response;
310             }
311             }
312              
313             1;
314             __END__