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