1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "libipp/ipp.h"
6 
7 #include <cstdint>
8 #include <string>
9 #include <vector>
10 
11 #include <gtest/gtest.h>
12 
13 // This file contains unit test based on examples from [rfc8010].
14 
15 namespace ipp {
16 
operator ==(const ipp::Resolution & a,const ipp::Resolution & b)17 static bool operator==(const ipp::Resolution& a, const ipp::Resolution& b) {
18   return (a.size1 == b.size1 && a.size2 == b.size2 && a.units == b.units);
19 }
20 
operator ==(const ipp::RangeOfInteger & a,const ipp::RangeOfInteger & b)21 static bool operator==(const ipp::RangeOfInteger& a,
22                        const ipp::RangeOfInteger& b) {
23   return (a.min_value == b.min_value && a.max_value == b.max_value);
24 }
25 
operator ==(const ipp::DateTime & a,const ipp::DateTime & b)26 static bool operator==(const ipp::DateTime& a, const ipp::DateTime& b) {
27   return (a.UTC_direction == b.UTC_direction && a.UTC_hours == b.UTC_hours &&
28           a.UTC_minutes == b.UTC_minutes && a.day == b.day &&
29           a.hour == b.hour && a.deci_seconds == b.deci_seconds &&
30           a.minutes == b.minutes && a.month == b.month &&
31           a.seconds == b.seconds && a.year == b.year);
32 }
33 
34 }  // namespace ipp
35 
36 namespace {
37 
TwosComplementEncoding(int value)38 uint32_t TwosComplementEncoding(int value) {
39   if (value >= 0)
40     return value;
41   uint32_t binary = -static_cast<int64_t>(value);
42   binary = ~binary;
43   ++binary;
44   return binary;
45 }
46 
47 // Helps build binary representation of frames from examples copied from
48 // [rfc8010].
49 struct BinaryContent {
50   // frame content
51   std::vector<uint8_t> data;
52   // add field with ASCII string
s__anon035c39180111::BinaryContent53   void s(std::string s) {
54     for (auto c : s) {
55       data.push_back(static_cast<uint8_t>(c));
56     }
57   }
58   // add 1 byte
u1__anon035c39180111::BinaryContent59   void u1(int v) {
60     const uint32_t b = TwosComplementEncoding(v);
61     data.push_back(b & 0xffu);
62   }
63   // add 2 bytes
u2__anon035c39180111::BinaryContent64   void u2(int v) {
65     const uint32_t b = TwosComplementEncoding(v);
66     data.push_back((b >> 8) & 0xffu);
67     data.push_back(b & 0xffu);
68   }
69   // add 4 bytes
u4__anon035c39180111::BinaryContent70   void u4(int v) {
71     const uint32_t b = TwosComplementEncoding(v);
72     data.push_back((b >> 24) & 0xffu);
73     data.push_back((b >> 16) & 0xffu);
74     data.push_back((b >> 8) & 0xffu);
75     data.push_back(b & 0xffu);
76   }
77 };
78 
79 // function comparing if two collections have the same content
CompareCollections(ipp::Collection * c1,ipp::Collection * c2)80 void CompareCollections(ipp::Collection* c1, ipp::Collection* c2) {
81   std::vector<ipp::Attribute*> a1 = c1->GetAllAttributes();
82   std::vector<ipp::Attribute*> a2 = c2->GetAllAttributes();
83   ASSERT_EQ(a1.size(), a2.size());
84   for (size_t i = 0; i < a1.size(); ++i) {
85     EXPECT_EQ(a1[i]->GetName(), a2[i]->GetName());
86     EXPECT_EQ(a1[i]->GetType(), a2[i]->GetType());
87     EXPECT_EQ(a1[i]->IsASet(), a2[i]->IsASet());
88     EXPECT_EQ(a1[i]->GetState(), a2[i]->GetState());
89     ASSERT_EQ(a1[i]->GetSize(), a2[i]->GetSize());
90     for (size_t j = 0; j < a1[i]->GetSize(); ++j) {
91       std::string s1, s2;
92       ipp::DateTime d1, d2;
93       ipp::Resolution r1, r2;
94       ipp::RangeOfInteger i1, i2;
95       ipp::StringWithLanguage l1, l2;
96       switch (a1[i]->GetType()) {
97         case ipp::AttrType::text:
98         case ipp::AttrType::name:
99           a1[i]->GetValue(&l1, j);
100           a2[i]->GetValue(&l2, j);
101           EXPECT_EQ(l1.value, l2.value);
102           EXPECT_EQ(l1.language, l2.language);
103           break;
104         case ipp::AttrType::integer:
105         case ipp::AttrType::boolean:
106         case ipp::AttrType::enum_:
107         case ipp::AttrType::octetString:
108         case ipp::AttrType::keyword:
109         case ipp::AttrType::uri:
110         case ipp::AttrType::uriScheme:
111         case ipp::AttrType::charset:
112         case ipp::AttrType::naturalLanguage:
113         case ipp::AttrType::mimeMediaType:
114           a1[i]->GetValue(&s1, j);
115           a2[i]->GetValue(&s2, j);
116           EXPECT_EQ(s1, s2);
117           break;
118         case ipp::AttrType::dateTime:
119           a1[i]->GetValue(&d1, j);
120           a2[i]->GetValue(&d2, j);
121           EXPECT_EQ(d1, d2);
122           break;
123         case ipp::AttrType::resolution:
124           a1[i]->GetValue(&r1, j);
125           a2[i]->GetValue(&r2, j);
126           EXPECT_EQ(r1, r2);
127           break;
128         case ipp::AttrType::rangeOfInteger:
129           a1[i]->GetValue(&i1, j);
130           a2[i]->GetValue(&i2, j);
131           EXPECT_EQ(i1, i2);
132           break;
133         case ipp::AttrType::collection:
134           CompareCollections(a1[i]->GetCollection(j), a2[i]->GetCollection(j));
135           break;
136       }
137     }
138   }
139 }
140 
141 // checks if given frame is a binary representation of given Request
CheckRequest(const BinaryContent & frame,ipp::Request * req,const int32_t request_no=1)142 void CheckRequest(const BinaryContent& frame,
143                   ipp::Request* req,
144                   const int32_t request_no = 1) {
145   // build output frame from Request and compare with the given frame
146   ipp::Client client(ipp::Version::_1_1, request_no - 1);
147   client.BuildRequestFrom(req);
148   EXPECT_TRUE(client.GetErrorLog().empty());
149   std::vector<uint8_t> bin_data;
150   ASSERT_TRUE(client.WriteRequestFrameTo(&bin_data));
151   EXPECT_TRUE(client.GetErrorLog().empty());
152   EXPECT_EQ(client.GetFrameLength(), bin_data.size());
153   EXPECT_EQ(bin_data, frame.data);
154   // parse the given frame and compare obtained object with the given Request
155   ipp::Server server(ipp::Version::_1_1, request_no);
156   auto req2 = ipp::Request::NewRequest(req->GetOperationId());
157   ASSERT_TRUE(server.ReadRequestFrameFrom(bin_data));
158   EXPECT_TRUE(server.GetErrorLog().empty());
159   ASSERT_TRUE(server.ParseRequestAndSaveTo(req2.get()));
160   EXPECT_TRUE(server.GetErrorLog().empty());
161   // ... compares two request objects
162   std::vector<ipp::Group*> groups1 = req->GetAllGroups();
163   std::vector<ipp::Group*> groups2 = req2->GetAllGroups();
164   ASSERT_EQ(groups1.size(), groups2.size());
165   for (size_t i = 0; i < groups1.size(); ++i) {
166     ipp::Group* g1 = groups1[i];
167     ipp::Group* g2 = groups2[i];
168     EXPECT_EQ(g1->GetName(), g2->GetName());
169     ASSERT_EQ(g1->GetSize(), g2->GetSize());
170     for (size_t j = 0; j < g1->GetSize(); ++j)
171       CompareCollections(g1->GetCollection(j), g2->GetCollection(j));
172   }
173   EXPECT_EQ(req->Data(), req2->Data());
174 }
175 
176 // checks if given frame is a binary representation of given Response
CheckResponse(const BinaryContent & frame,ipp::Response * res,const int32_t request_no=1)177 void CheckResponse(const BinaryContent& frame,
178                    ipp::Response* res,
179                    const int32_t request_no = 1) {
180   // build output frame from Response and compare with the given frame
181   ipp::Server server(ipp::Version::_1_1, request_no);
182   server.BuildResponseFrom(res);
183   EXPECT_TRUE(server.GetErrorLog().empty());
184   std::vector<uint8_t> bin_data;
185   ASSERT_TRUE(server.WriteResponseFrameTo(&bin_data));
186   EXPECT_TRUE(server.GetErrorLog().empty());
187   EXPECT_EQ(server.GetFrameLength(), bin_data.size());
188   EXPECT_EQ(bin_data, frame.data);
189   // parse the given frame and compare obtained object with the given Response
190   ipp::Client client(ipp::Version::_1_1, request_no - 1);
191   auto res2 = ipp::Response::NewResponse(res->GetOperationId());
192   ASSERT_TRUE(client.ReadResponseFrameFrom(bin_data));
193   EXPECT_TRUE(client.GetErrorLog().empty());
194   ASSERT_TRUE(client.ParseResponseAndSaveTo(res2.get()));
195   EXPECT_TRUE(client.GetErrorLog().empty());
196   // ... compares two response objects
197   EXPECT_EQ(res->StatusCode(), res2->StatusCode());
198   std::vector<ipp::Group*> groups1 = res->GetAllGroups();
199   std::vector<ipp::Group*> groups2 = res2->GetAllGroups();
200   ASSERT_EQ(groups1.size(), groups2.size());
201   for (size_t i = 0; i < groups1.size(); ++i) {
202     ipp::Group* g1 = groups1[i];
203     ipp::Group* g2 = groups2[i];
204     EXPECT_EQ(g1->GetName(), g2->GetName());
205     ASSERT_EQ(g1->GetSize(), g2->GetSize());
206     for (size_t j = 0; j < g1->GetSize(); ++j)
207       CompareCollections(g1->GetCollection(j), g2->GetCollection(j));
208   }
209   EXPECT_EQ(res->Data(), res2->Data());
210 }
211 
TEST(rfc8010,example1)212 TEST(rfc8010, example1) {
213   // A.1.  Print-Job Request
214   // The following is an example of a Print-Job request with "job-name",
215   // "copies", and "sides" specified.  The "ipp-attribute-fidelity"
216   // attribute is set to 'true' so that the print request will fail if the
217   // "copies" or the "sides" attribute is not supported or their values
218   // are not supported.
219   BinaryContent c;
220   // Octets                                Symbolic Value       Protocol field
221   c.u2(0x0101u);                          // 1.1                  version-number
222   c.u2(0x0002u);                          // Print-Job            operation-id
223   c.u4(0x00000001u);                      // 1                    request-id
224   c.u1(0x01u);                            // start operation-     operation-
225                                           // attributes           attributes-tag
226   c.u1(0x47u);                            // charset type         value-tag
227   c.u2(0x0012u);                          // name-length
228   c.s("attributes-charset");              // attributes-charset   name
229   c.u2(0x0005u);                          // value-length
230   c.s("utf-8");                           // UTF-8                value
231   c.u1(0x48u);                            // natural-language     value-tag
232                                           // type
233   c.u2(0x001bu);                          // name-length
234   c.s("attributes-natural-language");     // attributes-natural-  name
235                                           // language
236   c.u2(0x0005u);                          // value-length
237   c.s("en-us");                           // en-US                value
238   c.u1(0x45u);                            // uri type             value-tag
239   c.u2(0x000bu);                          // name-length
240   c.s("printer-uri");                     // printer-uri          name
241   c.u2(0x002cu);                          // value-length
242   c.s("ipp://printer.example.com/ipp/");  // printer pinetree     value
243   c.s("print/pinetree");
244   c.u1(0x42u);                    // nameWithoutLanguage  value-tag
245                                   // type
246   c.u2(0x0008u);                  // name-length
247   c.s("job-name");                // job-name             name
248   c.u2(0x0006u);                  // value-length
249   c.s("foobar");                  // foobar               value
250   c.u1(0x22u);                    // boolean type         value-tag
251   c.u2(0x0016u);                  // name-length
252   c.s("ipp-attribute-fidelity");  // ipp-attribute-       name
253                                   // fidelity
254   c.u2(0x0001u);                  // value-length
255   c.u1(0x01u);                    // true                 value
256   c.u1(0x02u);                    // start job-attributes job-attributes-
257                                   // tag
258   c.u1(0x21u);                    // integer type         value-tag
259   c.u2(0x0006u);                  // name-length
260   c.s("copies");                  // copies               name
261   c.u2(0x0004u);                  // value-length
262   c.u4(0x00000014u);              // 20                   value
263   c.u1(0x44u);                    // keyword type         value-tag
264   c.u2(0x0005u);                  // name-length
265   c.s("sides");                   // sides                name
266   c.u2(0x0013u);                  // value-length
267   c.s("two-sided-long-edge");     // two-sided-long-edge  value
268   c.u1(0x03u);                    // end-of-attributes    end-of-
269                                   // attributes-tag
270   c.s("%!PDF...");                // <PDF Document>       data
271 
272   ipp::Request_Print_Job r;
273   r.operation_attributes.printer_uri.Set(
274       "ipp://printer.example.com/ipp/print/pinetree");
275   r.operation_attributes.job_name.Set("foobar");
276   r.operation_attributes.ipp_attribute_fidelity.Set(1);
277   r.job_attributes.copies.Set(20);
278   r.job_attributes.sides.Set(ipp::E_sides::two_sided_long_edge);
279   for (char c : std::string("%!PDF..."))
280     r.Data().push_back(static_cast<uint8_t>(c));
281 
282   CheckRequest(c, &r);
283 }
284 
TEST(rfc8010,example2)285 TEST(rfc8010, example2) {
286   // A.2.  Print-Job Response (Successful)
287   // Here is an example of a successful Print-Job response to the previous
288   // Print-Job request.  The Printer supported the "copies" and "sides"
289   // attributes and their supplied values.  The status-code returned is
290   // 'successful-ok'.
291   BinaryContent c;
292   // Octets                                  Symbolic Value     Protocol field
293   c.u2(0x0101u);                       // 1.1                version-number
294   c.u2(0x0000u);                       // successful-ok      status-code
295   c.u4(0x00000001u);                   // 1                  request-id
296   c.u1(0x01u);                         // start operation-   operation-
297                                        // attributes         attributes-tag
298   c.u1(0x47u);                         // charset type       value-tag
299   c.u2(0x0012u);                       // name-length
300   c.s("attributes-charset");           // attributes-charset name
301   c.u2(0x0005u);                       // value-length
302   c.s("utf-8");                        // UTF-8              value
303   c.u1(0x48u);                         // natural-language   value-tag
304                                        // type
305   c.u2(0x001bu);                       // name-length
306   c.s("attributes-natural-language");  // attributes-        name
307                                        // natural-language
308   c.u2(0x0005u);                       // value-length
309   c.s("en-us");                        // en-US              value
310   c.u1(0x41u);                         // textWithoutLanguag value-tag
311                                        // e type
312   c.u2(0x000eu);                       // name-length
313   c.s("status-message");               // status-message     name
314   c.u2(0x000du);                       // value-length
315   c.s("successful-ok");                // successful-ok      value
316   c.u1(0x02u);                         // start job-         job-attributes-
317                                        // attributes         tag
318   c.u1(0x21u);                         // integer            value-tag
319   c.u2(0x0006u);                       // name-length
320   c.s("job-id");                       // job-id             name
321   c.u2(0x0004u);                       // value-length
322   c.u4(147);                           // 147                value
323   c.u1(0x45u);                         // uri type           value-tag
324   c.u2(0x0007u);                       // name-length
325   c.s("job-uri");                      // job-uri            name
326   c.u2(0x0030u);                       // value-length
327   c.s("ipp://printer.example.com/ipp/pr");  // job 147 on         value
328   c.s("int/pinetree/147");                  // pinetree
329   c.u1(0x23u);                              // enum type          value-tag
330   c.u2(0x0009u);                            // name-length
331   c.s("job-state");                         // job-state          name
332   c.u2(0x0004u);                            // value-length
333   c.u4(0x0003u);                            // pending            value
334   c.u1(0x03u);                              // end-of-attributes  end-of-
335                                             // attributes-tag
336 
337   ipp::Response_Print_Job r;
338   r.job_attributes.job_id.Set(147);
339   r.job_attributes.job_uri.Set(
340       "ipp://printer.example.com/ipp/print/pinetree/147");
341   r.job_attributes.job_state.Set(ipp::E_job_state::pending);
342 
343   CheckResponse(c, &r);
344 }
345 
TEST(rfc8010,example3)346 TEST(rfc8010, example3) {
347   // A.3.  Print-Job Response (Failure)
348   // Here is an example of an unsuccessful Print-Job response to the
349   // previous Print-Job request.  It fails because, in this case, the
350   // Printer does not support the "sides" attribute and because the value
351   // '20' for the "copies" attribute is not supported.  Therefore, no Job
352   // is created, and neither a "job-id" nor a "job-uri" operation
353   // attribute is returned.  The error code returned is 'client-error-
354   // attributes-or-values-not-supported' (0x040b).
355   BinaryContent c;
356   // Octets                            Symbolic Value              Protocol
357   // field
358   c.u2(0x0101u);              // 1.1                         version-
359                               // number
360   c.u2(0x040bu);              // client-error-attributes-or- status-code
361                               // values-not-supported
362   c.u4(0x00000001u);          // 1                           request-id
363   c.u1(0x01u);                // start operation-attributes  operation-
364                               // attributes
365                               // tag
366   c.u1(0x47u);                // charset type               value-tag
367   c.u2(0x0012u);              // name-length
368   c.s("attributes-charset");  // attributes-charset         name
369   c.u2(0x0005u);              // value-length
370   c.s("utf-8");               // UTF-8                      value
371   c.u1(0x48u);                // natural-language type      value-tag
372   c.u2(0x001bu);              // name-length
373   c.s("attributes-natural-language");  // attributes-natural-language name
374   c.u2(0x0005u);                       // value-length
375   c.s("en-us");                        // en-US                      value
376   c.u1(0x41u);                         // textWithoutLanguage type   value-tag
377   c.u2(0x000eu);                       // name-length
378   c.s("status-message");               // status-message             name
379   c.u2(0x002fu);                       // value-length
380   c.s("client-error-attributes-or-");  // client-error-attributes-or- value
381   c.s("values-not-supported");         // values-not-supported
382   c.u1(0x05u);        // start unsupported-         unsupported-
383                       // attributes                 attributes
384                       // tag
385   c.u1(0x21u);        // integer type               value-tag
386   c.u2(0x0006u);      // name-length
387   c.s("copies");      // copies                     name
388   c.u2(0x0004u);      // value-length
389   c.u4(0x00000014u);  // 20                         value
390   c.u1(0x10u);        // unsupported (type)         value-tag
391   c.u2(0x0005u);      // name-length
392   c.s("sides");       // sides                      name
393   c.u2(0x0000u);      // value-length
394   c.u1(0x03u);        // end-of-attributes          end-of-
395                       // attributes-
396                       // tag
397 
398   ipp::Response_Print_Job r;
399   r.StatusCode() =
400       ipp::E_status_code::client_error_attributes_or_values_not_supported;
401   ipp::Attribute* a = r.unsupported_attributes.AddUnknownAttribute(
402       "copies", true, ipp::AttrType::integer);
403   a->SetValue(20);
404   a = r.unsupported_attributes.AddUnknownAttribute("sides", true,
405                                                    ipp::AttrType::integer);
406   a->SetState(ipp::AttrState::unsupported);
407 
408   CheckResponse(c, &r);
409 }
410 
TEST(rfc8010,example4)411 TEST(rfc8010, example4) {
412   // A.4.  Print-Job Response (Success with Attributes Ignored)
413   // Here is an example of a successful Print-Job response to a Print-Job
414   // request like the previous Print-Job request, except that the value of
415   // "ipp-attribute-fidelity" is 'false'.  The print request succeeds,
416   // even though, in this case, the Printer supports neither the "sides"
417   // attribute nor the value '20' for the "copies" attribute.  Therefore,
418   // a Job is created and both a "job-id" and a "job-uri" operation
419   // attribute are returned.  The unsupported attributes are also returned
420   // in an Unsupported Attributes group.  The error code returned is
421   // 'successful-ok-ignored-or-substituted-attributes' (0x0001).
422   BinaryContent c;
423   // Octets                            Symbolic Value              Protocol
424   // field
425   c.u2(0x0101u);  // 1.1                         version-number
426   c.u2(0x0001u);  // successful-ok-ignored-or-   status-coder.StatusCode() =
427                   // static_cast<uint16_t>(ipp::E_status_code::successful_ok);
428                   // substituted-attributes
429   c.u4(0x00000001u);           // 1                           request-id
430   c.u1(0x01u);                 // start operation-attributes  operation-
431                                // attributes-tag
432   c.u1(0x47u);                 // charset type                value-tag
433   c.u2(0x0012u);               // name-length
434   c.s("attributes-charset");   // attributes-charset          name
435   c.u2(0x0005u);               // value-length
436   c.s("utf-8");                // UTF-8                       value
437   c.u1(0x48u);                 // natural-language type       value-tag
438   c.u2(0x001bu);               // name-length
439   c.s("attributes-natural-");  // attributes-natural-language name
440   c.s("language");
441   c.u2(0x0005u);                     // value-length
442   c.s("en-us");                      // en-US                       value
443   c.u1(0x41u);                       // textWithoutLanguage type    value-tag
444   c.u2(0x000eu);                     // name-length
445   c.s("status-message");             // status-message              name
446   c.u2(0x002fu);                     // value-length
447   c.s("successful-ok-ignored-or-");  // successful-ok-ignored-or-   value
448   c.s("substituted-attributes");     // substituted-attributes
449   c.u1(0x05u);                       // start unsupported-          unsupported-
450                 // attributes                  attributes tag
451   c.u1(0x21u);                        // integer type                value-tag
452   c.u2(0x0006u);                      // name-length
453   c.s("copies");                      // copies                      name
454   c.u2(0x0004u);                      // value-length
455   c.u4(0x00000014u);                  // 20                          value
456   c.u1(0x10u);                        // unsupported  (type)         value-tag
457   c.u2(0x0005u);                      // name-length
458   c.s("sides");                       // sides                       name
459   c.u2(0x0000u);                      // value-length
460   c.u1(0x02u);                        // start job-attributes        job-
461                                       // attributes-tag
462   c.u1(0x21u);                        // integer                     value-tag
463   c.u2(0x0006u);                      // name-length
464   c.s("job-id");                      // job-id                      name
465   c.u2(0x0004u);                      // value-length
466   c.u4(147);                          // 147                         value
467   c.u1(0x45u);                        // uri type                    value-tag
468   c.u2(0x0007u);                      // name-length
469   c.s("job-uri");                     // job-uri                     name
470   c.u2(0x0030u);                      // value-length
471   c.s("ipp://printer.example.com/");  // job 147 on pinetree         value
472   c.s("ipp/print/pinetree/147");
473   c.u1(0x23u);       // enum  type                  value-tag
474   c.u2(0x0009u);     // name-length
475   c.s("job-state");  // job-state                   name
476   c.u2(0x0004u);     // value-length
477   c.u4(0x0003u);     // pending                     value
478   c.u1(0x03u);       // end-of-attributes           end-of-
479                      // attributes-tag
480 
481   ipp::Response_Print_Job r;
482   r.StatusCode() =
483       ipp::E_status_code::successful_ok_ignored_or_substituted_attributes;
484   ipp::Attribute* a = r.unsupported_attributes.AddUnknownAttribute(
485       "copies", true, ipp::AttrType::integer);
486   a->SetValue(20);
487   a = r.unsupported_attributes.AddUnknownAttribute("sides", true,
488                                                    ipp::AttrType::integer);
489   a->SetState(ipp::AttrState::unsupported);
490   r.job_attributes.job_id.Set(147);
491   r.job_attributes.job_uri.Set(
492       "ipp://printer.example.com/ipp/print/pinetree/147");
493   r.job_attributes.job_state.Set(ipp::E_job_state::pending);
494 
495   CheckResponse(c, &r);
496 }
497 
TEST(rfc8010,example5)498 TEST(rfc8010, example5) {
499   // A.5.  Print-URI Request
500   // The following is an example of Print-URI request with "copies" and
501   // "job-name" parameters:
502   BinaryContent c;
503   // Octets                                Symbolic Value       Protocol field
504   c.u2(0x0101u);                          // 1.1                  version-number
505   c.u2(0x0003u);                          // Print-URI            operation-id
506   c.u4(0x00000001u);                      // 1                    request-id
507   c.u1(0x01u);                            // start operation-     operation-
508                                           // attributes           attributes-tag
509   c.u1(0x47u);                            // charset type         value-tag
510   c.u2(0x0012u);                          // name-length
511   c.s("attributes-charset");              // attributes-charset   name
512   c.u2(0x0005u);                          // value-length
513   c.s("utf-8");                           // UTF-8                value
514   c.u1(0x48u);                            // natural-language     value-tag
515                                           // type
516   c.u2(0x001bu);                          // name-length
517   c.s("attributes-natural-language");     // attributes-natural-  name
518                                           // language
519   c.u2(0x0005u);                          // value-length
520   c.s("en-us");                           // en-US                value
521   c.u1(0x45u);                            // uri type             value-tag
522   c.u2(0x000bu);                          // name-length
523   c.s("printer-uri");                     // printer-uri          name
524   c.u2(0x002cu);                          // value-length
525   c.s("ipp://printer.example.com/ipp/");  // printer pinetree     value
526   c.s("print/pinetree");
527   c.u1(0x45u);                       // uri type             value-tag
528   c.u2(0x000cu);                     // name-length
529   c.s("document-uri");               // document-uri         name
530   c.u2(0x0019u);                     // value-length
531   c.s("ftp://foo.example.com/foo");  // ftp://foo.example.co value
532                                      // m/foo
533   c.u1(0x42u);                       // nameWithoutLanguage  value-tag
534                                      // type
535   c.u2(0x0008u);                     // name-length
536   c.s("job-name");                   // job-name             name
537   c.u2(0x0006u);                     // value-length
538   c.s("foobar");                     // foobar               value
539   c.u1(0x02u);                       // start job-attributes job-attributes-
540                                      // tag
541   c.u1(0x21u);                       // integer type         value-tag
542   c.u2(0x0006u);                     // name-length
543   c.s("copies");                     // copies               name
544   c.u2(0x0004u);                     // value-length
545   c.u4(0x00000001u);                 // 1                    value
546   c.u1(0x03u);                       // end-of-attributes    end-of-
547                                      // attributes-tag
548 
549   ipp::Request_Print_URI r;
550   r.operation_attributes.printer_uri.Set(
551       "ipp://printer.example.com/ipp/print/pinetree");
552   r.operation_attributes.document_uri.Set("ftp://foo.example.com/foo");
553   r.operation_attributes.job_name.Set("foobar");
554   r.job_attributes.copies.Set(1);
555 
556   CheckRequest(c, &r);
557 }
558 
TEST(rfc8010,example6)559 TEST(rfc8010, example6) {
560   // A.6.  Create-Job Request
561   // The following is an example of Create-Job request with no parameters
562   // and no attributes:
563   BinaryContent c;
564   // Octets                                Symbolic Value       Protocol field
565   c.u2(0x0101u);                          // 1.1                  version-number
566   c.u2(0x0005u);                          // Create-Job           operation-id
567   c.u4(0x00000001u);                      // 1                    request-id
568   c.u1(0x01u);                            // start operation-     operation-
569                                           // attributes           attributes-tag
570   c.u1(0x47u);                            // charset type         value-tag
571   c.u2(0x0012u);                          // name-length
572   c.s("attributes-charset");              // attributes-charset   name
573   c.u2(0x0005u);                          // value-length
574   c.s("utf-8");                           // UTF-8                value
575   c.u1(0x48u);                            // natural-language     value-tag
576                                           // type
577   c.u2(0x001bu);                          // name-length
578   c.s("attributes-natural-language");     // attributes-natural-  name
579                                           // language
580   c.u2(0x0005u);                          // value-length
581   c.s("en-us");                           // en-US                value
582   c.u1(0x45u);                            // uri type             value-tag
583   c.u2(0x000bu);                          // name-length
584   c.s("printer-uri");                     // printer-uri          name
585   c.u2(0x002cu);                          // value-length
586   c.s("ipp://printer.example.com/ipp/");  // printer pinetree     value
587   c.s("print/pinetree");
588   c.u1(0x03u);  // end-of-attributes    end-of-
589                 // attributes-tag
590 
591   ipp::Request_Create_Job r;
592   r.operation_attributes.printer_uri.Set(
593       "ipp://printer.example.com/ipp/print/pinetree");
594 
595   CheckRequest(c, &r);
596 }
597 
TEST(rfc8010,example7)598 TEST(rfc8010, example7) {
599   // A.7.  Create-Job Request with Collection Attributes
600   // The following is an example of Create-Job request with the "media-
601   // col" collection attribute [PWG5100.3] with the value "media-
602   // size={x-dimension=21000 y-dimension=29700} media-type='stationery'":
603   BinaryContent c;
604   // Octets                                Symbolic Value       Protocol field
605   c.u2(0x0101u);                          // 1.1                  version-number
606   c.u2(0x0005u);                          // Create-Job           operation-id
607   c.u4(0x00000001u);                      // 1                    request-id
608   c.u1(0x01u);                            // start operation-     operation-
609                                           // attributes           attributes-tag
610   c.u1(0x47u);                            // charset type         value-tag
611   c.u2(0x0012u);                          // name-length
612   c.s("attributes-charset");              // attributes-charset   name
613   c.u2(0x0005u);                          // value-length
614   c.s("utf-8");                           // UTF-8                value
615   c.u1(0x48u);                            // natural-language     value-tag
616                                           // type
617   c.u2(0x001bu);                          // name-length
618   c.s("attributes-natural-language");     // attributes-natural-  name
619                                           // language
620   c.u2(0x0005u);                          // value-length
621   c.s("en-us");                           // en-US                value
622   c.u1(0x45u);                            // uri type             value-tag
623   c.u2(0x000bu);                          // name-length
624   c.s("printer-uri");                     // printer-uri          name
625   c.u2(0x002cu);                          // value-length
626   c.s("ipp://printer.example.com/ipp/");  // printer pinetree     value
627   c.s("print/pinetree");
628   c.u1(static_cast<int>(ipp::GroupTag::job_attributes));  // missing byte ?
629   c.u1(0x34u);         // begCollection        value-tag
630   c.u2(0x0009u);       // 9                    name-length
631   c.s("media-col");    // media-col            name
632   c.u2(0x0000u);       // 0                    value-length
633   c.u1(0x4au);         // memberAttrName       value-tag
634   c.u2(0x0000u);       // 0                    name-length
635   c.u2(0x000au);       // 10                   value-length
636   c.s("media-size");   // media-size           value (member-
637                        // name)
638   c.u1(0x34u);         // begCollection        member-value-tag
639   c.u2(0x0000u);       // 0                    name-length
640   c.u2(0x0000u);       // 0                    member-value-
641                        // length
642   c.u1(0x4au);         // memberAttrName       value-tag
643   c.u2(0x0000u);       // 0                    name-length
644   c.u2(0x000bu);       // 11                   value-length
645   c.s("x-dimension");  // x-dimension          value (member-
646                        // name)
647   c.u1(0x21u);         // integer              member-value-tag
648   c.u2(0x0000u);       // 0                    name-length
649   c.u2(0x0004u);       // 4                    member-value-
650                        // length
651   c.u4(0x00005208u);   // 21000                member-value
652   c.u1(0x4au);         // memberAttrName       value-tag
653   c.u2(0x0000u);       // 0                    name-length
654   c.u2(0x000bu);       // 11                   value-length
655   c.s("y-dimension");  // y-dimension          value (member-
656                        // name)
657   c.u1(0x21u);         // integer              member-value-tag
658   c.u2(0x0000u);       // 0                    name-length
659   c.u2(0x0004u);       // 4                    member-value-
660                        // length
661   c.u4(0x00007404u);   // 29700                member-value
662   c.u1(0x37u);         // endCollection        end-value-tag
663   c.u2(0x0000u);       // 0                    end-name-length
664   c.u2(0x0000u);       // 0                    end-value-length
665   c.u1(0x4au);         // memberAttrName       value-tag
666   c.u2(0x0000u);       // 0                    name-length
667   c.u2(0x000au);       // 10                   value-length
668   c.s("media-type");   // media-type           value (member-
669                        // name)
670   c.u1(0x44u);         // keyword              member-value-tag
671   c.u2(0x0000u);       // 0                    name-length
672   c.u2(0x000au);       // 10                   member-value-
673                        // length
674   c.s("stationery");   // stationery           member-value
675   c.u1(0x37u);         // endCollection        end-value-tag
676   c.u2(0x0000u);       // 0                    end-name-length
677   c.u2(0x0000u);       // 0                    end-value-length
678   c.u1(0x03u);         // end-of-attributes    end-of-
679                        // attributes-tag
680 
681   ipp::Request_Create_Job r;
682   r.operation_attributes.printer_uri.Set(
683       "ipp://printer.example.com/ipp/print/pinetree");
684   r.job_attributes.media_col.media_size.x_dimension.Set(21000);
685   r.job_attributes.media_col.media_size.y_dimension.Set(29700);
686   r.job_attributes.media_col.media_type.Set(ipp::E_media_type::stationery);
687 
688   CheckRequest(c, &r);
689 }
690 
TEST(rfc8010,example8)691 TEST(rfc8010, example8) {
692   // A.8.  Get-Jobs Request
693   // The following is an example of Get-Jobs request with parameters but
694   // no attributes:
695   BinaryContent c;
696   // Octets                                Symbolic Value       Protocol field
697   c.u2(0x0101u);                          // 1.1                  version-number
698   c.u2(0x000au);                          // Get-Jobs             operation-id
699   c.u4(0x0000007bu);                      // 123                  request-id
700   c.u1(0x01u);                            // start operation-     operation-
701                                           // attributes           attributes-tag
702   c.u1(0x47u);                            // charset type         value-tag
703   c.u2(0x0012u);                          // name-length
704   c.s("attributes-charset");              // attributes-charset   name
705   c.u2(0x0005u);                          // value-length
706   c.s("utf-8");                           // UTF-8                value
707   c.u1(0x48u);                            // natural-language     value-tag
708                                           // type
709   c.u2(0x001bu);                          // name-length
710   c.s("attributes-natural-language");     // attributes-natural-  name
711                                           // language
712   c.u2(0x0005u);                          // value-length
713   c.s("en-us");                           // en-US                value
714   c.u1(0x45u);                            // uri type             value-tag
715   c.u2(0x000bu);                          // name-length
716   c.s("printer-uri");                     // printer-uri          name
717   c.u2(0x002cu);                          // value-length
718   c.s("ipp://printer.example.com/ipp/");  // printer pinetree     value
719   c.s("print/pinetree");
720   c.u1(0x21u);                  // integer type         value-tag
721   c.u2(0x0005u);                // name-length
722   c.s("limit");                 // limit                name
723   c.u2(0x0004u);                // value-length
724   c.u4(0x00000032u);            // 50                   value
725   c.u1(0x44u);                  // keyword type         value-tag
726   c.u2(0x0014u);                // name-length
727   c.s("requested-attributes");  // requested-attributes name
728   c.u2(0x0006u);                // value-length
729   c.s("job-id");                // job-id               value
730   c.u1(0x44u);                  // keyword type         value-tag
731   c.u2(0x0000u);                // additional value     name-length
732   c.u2(0x0008u);                // value-length
733   c.s("job-name");              // job-name             value
734   c.u1(0x44u);                  // keyword type         value-tag
735   c.u2(0x0000u);                // additional value     name-length
736   c.u2(0x000fu);                // value-length
737   c.s("document-format");       // document-format      value
738   c.u1(0x03u);                  // end-of-attributes    end-of-
739                                 // attributes-tag
740 
741   ipp::Request_Get_Jobs r;
742   r.operation_attributes.printer_uri.Set(
743       "ipp://printer.example.com/ipp/print/pinetree");
744   r.operation_attributes.limit.Set(50);
745   r.operation_attributes.requested_attributes.Set(
746       {"job-id", "job-name", "document-format"});
747 
748   CheckRequest(c, &r, 123);
749 }
750 
TEST(rfc8010,example9)751 TEST(rfc8010, example9) {
752   // A.9.  Get-Jobs Response
753   // The following is an example of a Get-Jobs response from a previous
754   // request with three Jobs.  The Printer returns no information about
755   // the second Job (because of security reasons):
756   BinaryContent c;
757   // Octets                         Symbolic Value          Protocol field
758   c.u2(0x0101u);               // 1.1                     version-number
759   c.u2(0x0000u);               // successful-ok           status-code
760   c.u4(0x0000007bu);           // 123                     request-id (echoed
761                                // back)
762   c.u1(0x01u);                 // start operation-        operation-attributes-
763                                // attributes              tag
764   c.u1(0x47u);                 // charset type            value-tag
765   c.u2(0x0012u);               // name-length
766   c.s("attributes-charset");   // attributes-charset      name
767   c.u2(0x0005u);               // value-length
768   c.s("utf-8");                // UTF-8                   value
769   c.u1(0x48u);                 // natural-language type   value-tag
770   c.u2(0x001bu);               // name-length
771   c.s("attributes-natural-");  // attributes-natural-     name
772   c.s("language");             // language
773   c.u2(0x0005u);               // value-length
774   c.s("en-us");                // en-US                   value
775   c.u1(0x41u);                 // textWithoutLanguage     value-tag
776                                // type
777   c.u2(0x000eu);               // name-length
778   c.s("status-message");       // status-message          name
779   c.u2(0x000du);               // value-length
780   c.s("successful-ok");        // successful-ok           value
781   c.u1(0x02u);                 // start job-attributes    job-attributes-tag
782                                // (1st  object)
783   c.u1(0x21u);                 // integer type            value-tag
784   c.u2(0x0006u);               // name-length
785   c.s("job-id");               // job-id                  name
786   c.u2(0x0004u);               // value-length
787   c.u4(147);                   // 147                     value
788   c.u1(0x36u);                 // nameWithLanguage        value-tag
789   c.u2(0x0008u);               // name-length
790   c.s("job-name");             // job-name                name
791   c.u2(0x000cu);               // value-length
792   c.u2(0x0005u);               // sub-value-length
793   c.s("fr-ca");                // fr-CA                   value
794   c.u2(0x0003u);               // sub-value-length
795   c.s("fou");                  // fou                     name
796   c.u1(0x02u);                 // start job-attributes    job-attributes-tag
797                                // (2nd object)
798   c.u1(0x02u);                 // start job-attributes    job-attributes-tag
799                                // (3rd object)
800   c.u1(0x21u);                 // integer type            value-tag
801   c.u2(0x0006u);               // name-length
802   c.s("job-id");               // job-id                  name
803   c.u2(0x0004u);               // value-length
804   c.u4(149);                   // 149                     value
805   c.u1(0x36u);                 // nameWithLanguage        value-tag
806   c.u2(0x0008u);               // name-length
807   c.s("job-name");             // job-name                name
808   c.u2(0x0012u);               // value-length
809   c.u2(0x0005u);               // sub-value-length
810   c.s("de-CH");                // de-CH                   value
811   c.u2(0x0009u);               // sub-value-length
812   c.s("isch guet");            // isch guet               name
813   c.u1(0x03u);                 // end-of-attributes       end-of-attributes-tag
814 
815   ipp::Response_Get_Jobs r;
816   r.job_attributes[0].job_id.Set(147);
817   r.job_attributes[0].job_name.Set(ipp::StringWithLanguage("fou", "fr-ca"));
818   r.job_attributes[2].job_id.Set(149);
819   r.job_attributes[2].job_name.Set(
820       ipp::StringWithLanguage("isch guet", "de-CH"));
821 
822   CheckResponse(c, &r, 123);
823 }
824 
825 }  // namespace
826 
main(int argc,char ** argv)827 int main(int argc, char** argv) {
828   ::testing::InitGoogleTest(&argc, argv);
829   return RUN_ALL_TESTS();
830 }
831