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_frame_builder.h"
6 
7 #include <algorithm>
8 
9 #include "libipp/ipp_attribute.h"
10 #include "libipp/ipp_encoding.h"
11 
12 namespace ipp {
13 
14 namespace {
15 
16 // Saves 1-,2- or 4-bytes integer to buf using two's-complement binary encoding.
17 // The "buf" parameter is always resized to BytesCount. Returns false when
18 // given integer is out of range. In this case 0 is saved to buf.
19 template <size_t BytesCount>
SaveInteger(int v,std::vector<uint8_t> * buf)20 bool SaveInteger(int v, std::vector<uint8_t>* buf) {
21   buf->resize(BytesCount);
22   uint8_t* ptr = buf->data();
23   if (!WriteInteger<BytesCount>(&ptr, v)) {
24     buf->clear();
25     buf->resize(BytesCount, 0);
26     return false;
27   }
28   return true;
29 }
30 
31 // Saves simple string to buf, buf is resized accordingly.
SaveOctetString(const std::string & s,std::vector<uint8_t> * buf)32 void SaveOctetString(const std::string& s, std::vector<uint8_t>* buf) {
33   buf->assign(s.begin(), s.end());
34 }
35 
36 // Writes textWithLanguage/nameWithLanguage (see [rfc8010]) to buf, which is
37 // resized accordingly. Returns false if given string(s) is too long. In this
38 // case an empty string is saved to the buffer.
SaveStringWithLanguage(const ipp::StringWithLanguage & s,std::vector<uint8_t> * buf)39 bool SaveStringWithLanguage(const ipp::StringWithLanguage& s,
40                             std::vector<uint8_t>* buf) {
41   buf->clear();
42   buf->resize(4 + s.value.size() + s.language.size());
43   uint8_t* ptr = buf->data();
44   if (!WriteInteger<2>(&ptr, s.language.size())) {
45     std::vector<uint8_t> empty_buffer(4, 0);
46     buf->swap(empty_buffer);
47     return false;
48   }
49   ptr = std::copy(s.language.begin(), s.language.end(), ptr);
50   if (!WriteInteger<2>(&ptr, s.value.size())) {
51     std::vector<uint8_t> empty_buffer(4, 0);
52     buf->swap(empty_buffer);
53     return false;
54   }
55   std::copy(s.value.begin(), s.value.end(), ptr);
56   return true;
57 }
58 
59 // Saves dateTime (see [rfc8010]) to buf, which is resized accordingly. Returns
60 // false when the given dateTime is incorrect; in this case |buf| is set to
61 // 2000/1/1 00:00:00 +00:00.
SaveDateTime(const ipp::DateTime & v,std::vector<uint8_t> * buf)62 bool SaveDateTime(const ipp::DateTime& v, std::vector<uint8_t>* buf) {
63   buf->resize(11);
64   uint8_t* ptr = buf->data();
65   if (WriteInteger<2>(&ptr, v.year) && WriteInteger<1>(&ptr, v.month) &&
66       WriteInteger<1>(&ptr, v.day) && WriteInteger<1>(&ptr, v.hour) &&
67       WriteInteger<1>(&ptr, v.minutes) && WriteInteger<1>(&ptr, v.seconds) &&
68       WriteInteger<1>(&ptr, v.deci_seconds) &&
69       WriteInteger<1>(&ptr, v.UTC_direction) &&
70       WriteInteger<1>(&ptr, v.UTC_hours) &&
71       WriteInteger<1>(&ptr, v.UTC_minutes))
72     return true;
73   buf->clear();
74   buf->resize(11, 0);
75   ptr = buf->data();
76   WriteInteger<int16_t>(&ptr, 2000);
77   (*buf)[8] = '+';
78   return false;
79 }
80 
81 // Writes resolution (see [rfc8010]) to buf, which is resized accordingly.
82 // Returns false when given value is incorrect; in this case |buf| is set to
83 // 256x256dpi.
SaveResolution(const ipp::Resolution & v,std::vector<uint8_t> * buf)84 bool SaveResolution(const ipp::Resolution& v, std::vector<uint8_t>* buf) {
85   buf->resize(9);
86   uint8_t* ptr = buf->data();
87   if (WriteInteger<4>(&ptr, v.size1) && WriteInteger<4>(&ptr, v.size2) &&
88       (v.units == ipp::Resolution::kDotsPerCentimeter ||
89        v.units == ipp::Resolution::kDotsPerInch)) {
90     WriteInteger<int8_t>(&ptr, v.units);
91     return true;
92   }
93   buf->clear();
94   // set to 256x256 dpi
95   buf->resize(9, 0);
96   (*buf)[2] = (*buf)[6] = 1;
97   (*buf)[8] = ipp::Resolution::kDotsPerInch;
98   return false;
99 }
100 
101 // Writes rangeOfInteger (see [rfc8010]) to buf, which is resized accordingly.
102 // Returns false when given value is incorrect; in this case |buf| is set to 0.
SaveRangeOfInteger(const ipp::RangeOfInteger & v,std::vector<uint8_t> * buf)103 bool SaveRangeOfInteger(const ipp::RangeOfInteger& v,
104                         std::vector<uint8_t>* buf) {
105   buf->resize(8);
106   uint8_t* ptr = buf->data();
107   if (WriteInteger<4>(&ptr, v.min_value) && WriteInteger<4>(&ptr, v.max_value))
108     return true;
109   buf->clear();
110   buf->resize(8, 0);
111   return false;
112 }
113 
114 }  //  namespace
115 
LogFrameBuilderError(const std::string & message)116 void FrameBuilder::LogFrameBuilderError(const std::string& message) {
117   Log l;
118   l.message = "Error when building frame: " + message + ".";
119   errors_->push_back(l);
120 }
121 
SaveAttrValue(Attribute * attr,size_t index,uint8_t * tag,std::vector<uint8_t> * buf)122 void FrameBuilder::SaveAttrValue(Attribute* attr,
123                                  size_t index,
124                                  uint8_t* tag,
125                                  std::vector<uint8_t>* buf) {
126   *tag = static_cast<uint8_t>(attr->GetType());
127   bool result = true;
128   switch (attr->GetType()) {
129     case AttrType::boolean: {
130       int v = 0;
131       attr->GetValue(&v, index);
132       if (v != 0)
133         v = 1;
134       result = SaveInteger<1>(v, buf);
135       break;
136     }
137     case AttrType::integer:
138     case AttrType::enum_: {
139       int v = 0;
140       attr->GetValue(&v, index);
141       result = SaveInteger<4>(v, buf);
142       break;
143     }
144     case AttrType::dateTime: {
145       DateTime v;
146       attr->GetValue(&v, index);
147       result = SaveDateTime(v, buf);
148       break;
149     }
150     case AttrType::resolution: {
151       Resolution v;
152       attr->GetValue(&v, index);
153       result = SaveResolution(v, buf);
154       break;
155     }
156     case AttrType::rangeOfInteger: {
157       RangeOfInteger v;
158       attr->GetValue(&v, index);
159       result = SaveRangeOfInteger(v, buf);
160       break;
161     }
162     case AttrType::text: {
163       StringWithLanguage s;
164       attr->GetValue(&s, index);
165       if (s.language.empty()) {
166         *tag = textWithoutLanguage_value_tag;
167         SaveOctetString(s, buf);
168       } else {
169         result = SaveStringWithLanguage(s, buf);
170       }
171     } break;
172     case AttrType::name: {
173       StringWithLanguage s;
174       attr->GetValue(&s, index);
175       if (s.language.empty()) {
176         *tag = nameWithoutLanguage_value_tag;
177         SaveOctetString(s, buf);
178       } else {
179         result = SaveStringWithLanguage(s, buf);
180       }
181     } break;
182     case AttrType::octetString:
183     case AttrType::keyword:
184     case AttrType::uri:
185     case AttrType::uriScheme:
186     case AttrType::charset:
187     case AttrType::naturalLanguage:
188     case AttrType::mimeMediaType: {
189       std::string s = "";
190       attr->GetValue(&s, index);
191       SaveOctetString(s, buf);
192       break;
193     }
194     default:
195       LogFrameBuilderError(
196           "Internal error: cannot recognize value type of the attribute " +
197           attr->GetName());
198       buf->clear();
199       break;
200   }
201   if (!result)
202     LogFrameBuilderError("Incorrect value of the attribute " + attr->GetName() +
203                          ". Default value was written instead");
204 }
205 
SaveCollection(Collection * coll,std::list<TagNameValue> * data_chunks)206 void FrameBuilder::SaveCollection(Collection* coll,
207                                   std::list<TagNameValue>* data_chunks) {
208   // get list of all attributes
209   std::vector<Attribute*> attrs = coll->GetAllAttributes();
210   // save the attributes
211   for (Attribute* attr : attrs) {
212     if (attr->GetState() == AttrState::unset)
213       continue;
214     TagNameValue tnv;
215     tnv.tag = memberAttrName_value_tag;
216     tnv.name.clear();
217     SaveOctetString(attr->GetName(), &tnv.value);
218     data_chunks->push_back(tnv);
219     for (size_t val_index = 0; val_index < attr->GetSize(); ++val_index) {
220       if (attr->GetState() != AttrState::set) {
221         tnv.tag = static_cast<uint8_t>(attr->GetState());
222         tnv.value.clear();
223       } else if (attr->GetType() == AttrType::collection) {
224         tnv.tag = begCollection_value_tag;
225         tnv.value.clear();
226         data_chunks->push_back(tnv);
227         SaveCollection(attr->GetCollection(val_index), data_chunks);
228         tnv.tag = endCollection_value_tag;
229         tnv.value.clear();
230       } else {
231         SaveAttrValue(attr, val_index, &tnv.tag, &tnv.value);
232       }
233       data_chunks->push_back(tnv);
234     }
235   }
236 }
237 
SaveGroup(Collection * coll,std::list<TagNameValue> * data_chunks)238 void FrameBuilder::SaveGroup(Collection* coll,
239                              std::list<TagNameValue>* data_chunks) {
240   // get list of all attributes
241   std::vector<Attribute*> attrs = coll->GetAllAttributes();
242   // save the attributes
243   for (Attribute* attr : attrs) {
244     if (attr->GetState() == AttrState::unset)
245       continue;
246     TagNameValue tnv;
247     SaveOctetString(attr->GetName(), &tnv.name);
248     if (attr->GetState() != AttrState::set) {
249       tnv.tag = static_cast<uint8_t>(attr->GetState());
250       tnv.value.clear();
251       data_chunks->push_back(tnv);
252       continue;
253     }
254     for (size_t val_index = 0; val_index < attr->GetSize(); ++val_index) {
255       if (attr->GetType() == AttrType::collection) {
256         tnv.tag = begCollection_value_tag;
257         tnv.value.clear();
258         data_chunks->push_back(tnv);
259         SaveCollection(attr->GetCollection(val_index), data_chunks);
260         tnv.tag = endCollection_value_tag;
261         tnv.name.clear();
262         tnv.value.clear();
263       } else {
264         SaveAttrValue(attr, val_index, &tnv.tag, &tnv.value);
265       }
266       data_chunks->push_back(tnv);
267       tnv.name.clear();
268     }
269   }
270 }
271 
BuildFrameFromPackage(Package * package)272 void FrameBuilder::BuildFrameFromPackage(Package* package) {
273   frame_->groups_tags_.clear();
274   frame_->groups_content_.clear();
275   // save frame data
276   std::vector<Group*> groups = package->GetAllGroups();
277   for (Group* grp : groups) {
278     for (size_t i = 0; true; ++i) {
279       Collection* coll = grp->GetCollection(i);
280       if (coll == nullptr)
281         break;
282       frame_->groups_tags_.push_back(static_cast<uint8_t>(grp->GetName()));
283       frame_->groups_content_.resize(frame_->groups_tags_.size());
284       SaveGroup(coll, &(frame_->groups_content_.back()));
285       // skip if it this is a single empty group
286       if (frame_->groups_content_.back().empty() && !grp->IsASet()) {
287         frame_->groups_tags_.pop_back();
288         frame_->groups_content_.pop_back();
289       }
290     }
291   }
292   frame_->data_ = package->Data();
293 }
294 
WriteFrameToBuffer(uint8_t * ptr)295 bool FrameBuilder::WriteFrameToBuffer(uint8_t* ptr) {
296   bool error_in_header = true;
297   if (!WriteInteger<1>(&ptr, frame_->major_version_number_)) {
298     LogFrameBuilderError("major-version-number is out of range");
299   } else if (!WriteInteger<1>(&ptr, frame_->minor_version_number_)) {
300     LogFrameBuilderError("minor-version-number is out of range");
301   } else if (!WriteInteger<2>(&ptr, frame_->operation_id_or_status_code_)) {
302     LogFrameBuilderError("operation-id or status-code is out of range");
303   } else if (!WriteInteger<4>(&ptr, frame_->request_id_)) {
304     LogFrameBuilderError("request-id is out of range");
305   } else {
306     error_in_header = false;
307   }
308   if (error_in_header)
309     return false;
310   for (size_t i = 0; i < frame_->groups_tags_.size(); ++i) {
311     if (frame_->groups_tags_[i] > max_begin_attribute_group_tag) {
312       LogFrameBuilderError("begin-attribute-group-tag is out of range");
313       return false;
314     }
315     // write a group to the buffer
316     if (!WriteInteger<1>(&ptr, frame_->groups_tags_[i]) ||
317         !WriteTNVsToBuffer(frame_->groups_content_[i], &ptr))
318       return false;
319   }
320   WriteInteger<int8_t>(&ptr, end_of_attributes_tag);
321   std::copy(frame_->data_.begin(), frame_->data_.end(), ptr);
322   return true;
323 }
324 
WriteTNVsToBuffer(const std::list<TagNameValue> & tnvs,uint8_t ** ptr)325 bool FrameBuilder::WriteTNVsToBuffer(const std::list<TagNameValue>& tnvs,
326                                      uint8_t** ptr) {
327   for (auto& tnv : tnvs) {
328     if (!WriteInteger<1>(ptr, tnv.tag)) {
329       LogFrameBuilderError("value-tag is out of range");
330       return false;
331     }
332     if (!WriteInteger<2>(ptr, tnv.name.size())) {
333       LogFrameBuilderError("name-length is out of range");
334       return false;
335     }
336     *ptr = std::copy(tnv.name.begin(), tnv.name.end(), *ptr);
337     if (!WriteInteger<2>(ptr, tnv.value.size())) {
338       LogFrameBuilderError("value-length is out of range");
339       return false;
340     }
341     *ptr = std::copy(tnv.value.begin(), tnv.value.end(), *ptr);
342   }
343   return true;
344 }
345 
GetFrameLength() const346 std::size_t FrameBuilder::GetFrameLength() const {
347   // Header has always 8 bytes (ipp_version + operation_id/status + request_id).
348   size_t length = 8;
349   // The header is followed by a list of groups.
350   for (const auto& tnvs : frame_->groups_content_) {
351     // Each group starts with 1-byte group-tag ...
352     length += 1;
353     // ... and consists of list of tag-name-value.
354     for (const auto& tnv : tnvs)
355       // Tag + name_size + name + value_size + value.
356       length += (1 + 2 + tnv.name.size() + 2 + tnv.value.size());
357   }
358   // end-of-attributes-tag + blob_with_data.
359   length += 1 + frame_->data_.size();
360   return length;
361 }
362 
363 }  // namespace ipp
364