| // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ipp_util.h" |
| |
| #include <map> |
| #include <set> |
| #include <string> |
| |
| #include <arpa/inet.h> |
| #include <base/values.h> |
| |
| #include "smart_buffer.h" |
| #include "usbip_constants.h" |
| |
| namespace { |
| |
| std::vector<IppAttribute> GetAttributes(const base::ListValue* attributes) { |
| std::vector<IppAttribute> ipp_attributes; |
| for (std::size_t i = 0; i < attributes->GetSize(); ++i) { |
| const base::DictionaryValue* attributes_dictionary; |
| if (!attributes->GetDictionary(i, &attributes_dictionary)) { |
| LOG(ERROR) |
| << "Failed to extract dictionary value from attributes list at index " |
| << i; |
| } |
| ipp_attributes.push_back(GetAttribute(attributes_dictionary)); |
| } |
| return ipp_attributes; |
| } |
| |
| // Find first occurrence of |target| in |message| and return the index to where |
| // it begins. If |target| does not appear in |message| then returns -1. |
| ssize_t FindFirstOccurrence(const SmartBuffer& message, |
| const std::string& target, size_t start = 0) { |
| const std::vector<uint8_t>& contents = message.contents(); |
| auto iter = std::search(contents.begin() + start, contents.end(), |
| target.begin(), target.end()); |
| if (iter == contents.end()) { |
| return -1; |
| } |
| return std::distance(contents.begin(), iter); |
| } |
| |
| // Determines if |message| starts with the string |target|. |
| bool StartsWith(const SmartBuffer& message, const std::string& target) { |
| return FindFirstOccurrence(message, target) == 0; |
| } |
| |
| bool MessageContains(const SmartBuffer& message, |
| const std::string& s) { |
| ssize_t pos = FindFirstOccurrence(message, s); |
| return pos != -1; |
| } |
| |
| // Adds the IPP |tag| to |buf|. |
| void AddTag(IppTag tag, SmartBuffer* buf) { |
| uint8_t tag_value = static_cast<uint8_t>(tag); |
| buf->Add(&tag_value, sizeof(tag_value)); |
| } |
| |
| // Adds the IPP |name| to |bytes|. If |include_name| is set to true then the |
| // length of |name| and |name| itself are added to |buf|. Otherwise just a |
| // length with value 0 is added to indicate that there is no value. |
| void AddName(const std::string& name, bool include_name, |
| SmartBuffer* buf) { |
| CHECK_LE(name.size(), USHRT_MAX) << "Name size is too large"; |
| uint16_t name_length = (include_name) ? name.size() : 0; |
| name_length = htons(name_length); |
| buf->Add(&name_length, sizeof(name_length)); |
| if (include_name) { |
| buf->Add(name.c_str(), name.size()); |
| } |
| } |
| |
| // Adds the IPP |value_length| to |buf|. |
| void AddValueLength(size_t value_length, SmartBuffer* buf) { |
| CHECK_LE(value_length, USHRT_MAX) << "Given value length is too large"; |
| uint16_t length = value_length; |
| length = htons(length); |
| buf->Add(&length, sizeof(length)); |
| } |
| |
| // Adds the IPP attribute of type bool to |buf|. |
| void AddBooleanAttribute(bool value, IppTag tag, const std::string& name, |
| bool include_name, SmartBuffer* buf) { |
| AddTag(tag, buf); |
| AddName(name, include_name, buf); |
| AddValueLength(sizeof(value), buf); |
| buf->Add(&value, sizeof(value)); |
| } |
| |
| // Adds the IPP attribute of type int to |buf|. |
| void AddIntAttribute(int value, IppTag tag, const std::string& name, |
| bool include_name, SmartBuffer* buf) { |
| AddTag(tag, buf); |
| AddName(name, include_name, buf); |
| AddValueLength(sizeof(value), buf); |
| // Need to put the int value in network byte order. |
| value = htonl(value); |
| buf->Add(&value, sizeof(value)); |
| } |
| |
| // Adds the IPP attribute of type string to |buf|. |
| void AddStringAttribute(const std::string& value, IppTag tag, |
| const std::string& name, bool include_name, |
| SmartBuffer* buf) { |
| AddTag(tag, buf); |
| AddName(name, include_name, buf); |
| AddValueLength(value.size(), buf); |
| buf->Add(value.c_str(), value.size()); |
| } |
| |
| } // namespace |
| |
| IppAttribute::IppAttribute(const std::string& type, |
| const std::string& name, |
| const base::Value* value) |
| : type_(type), name_(name), value_(value) {} |
| |
| bool IppAttribute::IsList() const { |
| return value_->is_list(); |
| } |
| |
| size_t IppAttribute::GetListSize() const { |
| const base::ListValue* values; |
| CHECK(value_->GetAsList(&values)) |
| << "Failed to retrieve list value from " << name_; |
| return values->GetSize(); |
| } |
| |
| bool IppAttribute::GetBool() const { |
| bool out; |
| CHECK(value_->GetAsBoolean(&out)) |
| << "Failed to retrieve boolean value from " << name_; |
| return out; |
| } |
| |
| int IppAttribute::GetInt() const { |
| int out; |
| CHECK(value_->GetAsInteger(&out)) |
| << "Failed to retrieve integer value from " << name_; |
| return out; |
| } |
| |
| std::string IppAttribute::GetString() const { |
| std::string out; |
| CHECK(value_->GetAsString(&out)) |
| << "Failed to retrieve string value from " << name_; |
| return out; |
| } |
| |
| std::vector<bool> IppAttribute::GetBools() const { |
| const base::ListValue* values; |
| CHECK(value_->GetAsList(&values)) |
| << "Failed to retrieve list value from " << name_; |
| std::vector<bool> booleans(values->GetSize()); |
| for (std::size_t i = 0; i < values->GetSize(); ++i) { |
| bool out; |
| CHECK(values->GetBoolean(i, &out)) |
| << "Failed to retrieve boolean value from " << name_ << " at index " |
| << i; |
| booleans[i] = out; |
| } |
| return booleans; |
| } |
| |
| std::vector<int> IppAttribute::GetInts() const { |
| const base::ListValue* values; |
| CHECK(value_->GetAsList(&values)) |
| << "Failed to retrieve list value from " << name_; |
| std::vector<int> integers(values->GetSize()); |
| for (std::size_t i = 0; i < values->GetSize(); ++i) { |
| int out; |
| CHECK(values->GetInteger(i, &out)) |
| << "Failed to retrieve integer value from " << name_ << " at index " |
| << i; |
| integers[i] = out; |
| } |
| return integers; |
| } |
| |
| std::vector<std::string> IppAttribute::GetStrings() const { |
| const base::ListValue* values; |
| CHECK(value_->GetAsList(&values)) |
| << "Failed to retrieve list value from " << name_; |
| std::vector<std::string> strings(values->GetSize()); |
| for (std::size_t i = 0; i < values->GetSize(); ++i) { |
| std::string out; |
| CHECK(values->GetString(i, &out)) << "Failed to retrieve string value from " |
| << name_ << " at index " << i; |
| strings[i] = out; |
| } |
| return strings; |
| } |
| |
| std::vector<uint8_t> IppAttribute::GetBytes() const { |
| const base::ListValue* values; |
| CHECK(value_->GetAsList(&values)) |
| << "Failed to retrieve list value from " << name_; |
| std::vector<uint8_t> bytes(values->GetSize()); |
| for (std::size_t i = 0; i < values->GetSize(); ++i) { |
| int out; |
| CHECK(values->GetInteger(i, &out)) |
| << "Failed to retrieve byte value from " << name_ << " at index " << i; |
| CHECK_GE(out, 0) << "Retrieved byte value is negative"; |
| CHECK_LE(out, UCHAR_MAX) << "Retrieved byte value is too large"; |
| bytes[i] = static_cast<uint8_t>(out); |
| } |
| return bytes; |
| } |
| |
| bool operator==(const IppAttribute& lhs, const IppAttribute& rhs) { |
| // TODO(valleau): Add comparison for |value_| members once chromelib is |
| // updated and the operator is defined. |
| return lhs.type_ == rhs.type_ && lhs.name_ == rhs.name_; |
| } |
| |
| bool operator!=(const IppAttribute& lhs, const IppAttribute& rhs) { |
| return !(lhs == rhs); |
| } |
| |
| void UnpackIppHeader(IppHeader* header) { |
| header->operation_id = ntohs(header->operation_id); |
| header->request_id = ntohl(header->request_id); |
| } |
| |
| void PackIppHeader(IppHeader* header) { |
| header->operation_id = htons(header->operation_id); |
| header->request_id = htonl(header->request_id); |
| } |
| |
| bool IsHttpChunkedMessage(const SmartBuffer& message) { |
| ssize_t i = FindFirstOccurrence(message, "Transfer-Encoding: chunked"); |
| return i != -1; |
| } |
| |
| bool ContainsHttpHeader(const SmartBuffer& message) { |
| return MessageContains(message, "POST /ipp/print HTTP"); |
| } |
| |
| bool ContainsHttpBody(const SmartBuffer& message) { |
| // We are making the assumption that if |message| does not contain an HTTP |
| // header then |message| is the body of an HTTP message. |
| if (!ContainsHttpHeader(message)) { |
| return true; |
| } |
| // If |message| contains an HTTP header, check to see if there's anything |
| // immediately following it. |
| ssize_t pos = FindFirstOccurrence(message, "\r\n\r\n"); |
| CHECK_GE(pos, 0) << "HTTP header does not contain end-of-header marker"; |
| size_t start = pos + 4; |
| return start < message.size(); |
| } |
| |
| IppHeader GetIppHeader(const SmartBuffer& message) { |
| ssize_t i = FindFirstOccurrence(message, "\r\n\r\n"); |
| size_t offset = 0; |
| if (i != -1) { |
| // If |message| starts with an HTTP header, jump to the beginning of the IPP |
| // message. |
| if (IsHttpChunkedMessage(message)) { |
| // If |message| is an HTTP chunked message header, jump to the beginning |
| // of the first chunk. |
| i = FindFirstOccurrence(message, "\r\n", i + 4); |
| offset = i + 2; |
| } else { |
| offset = i + 4; |
| } |
| } |
| IppHeader header; |
| // Ensure that |message| has enough bytes to copy into |header|. |
| CHECK_GE(message.size(), offset); |
| CHECK_GE(message.size() - offset, sizeof(header)); |
| memset(&header, 0, sizeof(header)); |
| memcpy(&header, message.data() + offset, sizeof(header)); |
| UnpackIppHeader(&header); |
| return header; |
| } |
| |
| size_t ExtractChunkSize(const SmartBuffer& message) { |
| ssize_t end = FindFirstOccurrence(message, "\r\n"); |
| if (end == -1) { |
| return 0; |
| } |
| std::string hex_string; |
| const std::vector<uint8_t>& contents = message.contents(); |
| for (size_t i = 0; i < end; ++i) { |
| hex_string += static_cast<char>(contents[i]); |
| } |
| return std::stoll(hex_string, 0, 16); |
| } |
| |
| SmartBuffer ParseHttpChunkedMessage(SmartBuffer* message) { |
| CHECK_NE(message, nullptr) << "Given null message"; |
| // If |message| starts with the trailing CRLF end-of-chunk indicator from the |
| // previous chunk then erase it. |
| if (StartsWith(*message, "\r\n")) { |
| message->Erase(0, 2); |
| } |
| size_t chunk_size = ExtractChunkSize(*message); |
| LOG(INFO) << "Chunk size: " << chunk_size; |
| ssize_t start = FindFirstOccurrence(*message, "\r\n"); |
| SmartBuffer ret(0); |
| if (start == -1) { |
| return ret; |
| } |
| |
| ret.Add(*message, start + 2, chunk_size); |
| // In case |message| contains multiple chunks, we remove the chunk which was |
| // just parsed. |
| // |
| // The length of the prefix to be deleted is calculated as follows: |
| // start - represents the hex-encoded length value. |
| // chunk_size - the number of bytes read out for the message body. |
| // 2 - the CRLF characters which trail the length. |
| size_t to_erase_length = start + chunk_size + 2; |
| CHECK_GE(message->size(), to_erase_length); |
| message->Erase(0, to_erase_length); |
| // If |message| also contains the trailing CRLF end-of-chunk indicator, then |
| // erase it. |
| if (StartsWith(*message, "\r\n")) { |
| message->Erase(0, 2); |
| } |
| |
| return ret; |
| } |
| |
| bool ContainsFinalChunk(const SmartBuffer& message) { |
| ssize_t i = FindFirstOccurrence(message, "0\r\n\r\n"); |
| return i > 0 && message.size() == i + 5; |
| } |
| |
| bool ProcessMessageChunks(SmartBuffer* message) { |
| CHECK(message != nullptr) << "Process - Given null message"; |
| if (IsHttpChunkedMessage(*message)) { |
| // If |message| contains an HTTP header then we discard it. |
| int start = FindFirstOccurrence(*message, "\r\n\r\n"); |
| CHECK_GT(start, 0) << "Failed to process HTTP chunked message"; |
| message->Erase(0, start + 4); |
| } |
| |
| SmartBuffer chunk; |
| while (message->size() > 0) { |
| chunk = ParseHttpChunkedMessage(message); |
| } |
| return chunk.size() != 0; |
| } |
| |
| void RemoveHttpHeader(SmartBuffer* message) { |
| CHECK(ContainsHttpHeader(*message)) << "Message does not contain HTTP header"; |
| int i = FindFirstOccurrence(*message, "\r\n\r\n"); |
| CHECK_GT(i, 0) << "Failed to find end of HTTP header"; |
| message->Erase(0, i + 4); |
| } |
| |
| SmartBuffer ExtractIppMessage(SmartBuffer* message) { |
| CHECK_NE(message, nullptr) << "Received null message"; |
| return ParseHttpChunkedMessage(message); |
| } |
| |
| SmartBuffer MergeDocument(SmartBuffer* message) { |
| CHECK_NE(message, nullptr) << "Received null message"; |
| SmartBuffer document; |
| while (message->size() > 0) { |
| SmartBuffer chunk = ParseHttpChunkedMessage(message); |
| document.Add(chunk); |
| } |
| return document; |
| } |
| |
| std::string GetHttpResponseHeader(size_t size) { |
| return "HTTP/1.1 200 OK\r\n" |
| "Server: localhost:0\r\n" |
| "Content-Type: application/ipp\r\n" |
| "Content-Length: " + std::to_string(size) + "\r\n" |
| "Connection: close\r\n\r\n"; |
| } |
| |
| IppAttribute GetAttribute(const base::Value* attribute) { |
| const base::DictionaryValue* dictionary; |
| CHECK(attribute->GetAsDictionary(&dictionary)) |
| << "Failed to retrieve dictionary value from attributes"; |
| |
| std::string type; |
| std::string name; |
| const base::Value* value; |
| CHECK(dictionary->GetString(kTypeKey, &type)) |
| << "Failed to retrieve type from attribute"; |
| CHECK(dictionary->GetString(kNameKey, &name)) |
| << "Failed to retrieve name from attribute"; |
| CHECK(dictionary->Get(kValueKey, &value)) |
| << "Failed to extract value from attribute"; |
| |
| return IppAttribute(type, name, value); |
| } |
| |
| std::vector<IppAttribute> GetAttributes(const base::Value* attributes, |
| const std::string& key) { |
| const base::DictionaryValue* dictionary; |
| CHECK(attributes->GetAsDictionary(&dictionary)) |
| << "Failed to retrieve dictionary value from attributes"; |
| |
| const base::ListValue* attributes_list; |
| CHECK(dictionary->GetList(key, &attributes_list)) |
| << "Failed to extract attributes list for key " << key; |
| |
| return GetAttributes(attributes_list); |
| } |
| |
| IppTag GetIppTag(const std::string& name) { |
| std::map<std::string, IppTag> tags = { |
| {kOperationAttributes, IppTag::OPERATION}, |
| {kUnsupportedAttributes, IppTag::UNSUPPORTED_GROUP}, |
| {kPrinterAttributes, IppTag::PRINTER}, |
| {kJobAttributes, IppTag::JOB}, |
| {kUnsupported, IppTag::UNSUPPORTED_VALUE}, |
| {kNoValue, IppTag::NOVALUE}, |
| {kInteger, IppTag::INTEGER}, |
| {kBoolean, IppTag::BOOLEAN}, |
| {kEnum, IppTag::ENUM}, |
| {kOctetString, IppTag::STRING}, |
| {kDateTime, IppTag::DATE}, |
| {kResolution, IppTag::RESOLUTION}, |
| {kRangeOfInteger, IppTag::RANGE}, |
| {kBegCollection, IppTag::BEGIN_COLLECTION}, |
| {kEndCollection, IppTag::END_COLLECTION}, |
| {kTextWithoutLanguage, IppTag::TEXT}, |
| {kNameWithoutLanguage, IppTag::NAME}, |
| {kKeyword, IppTag::KEYWORD}, |
| {kUri, IppTag::URI}, |
| {kCharset, IppTag::CHARSET}, |
| {kNaturalLanguage, IppTag::LANGUAGE}, |
| {kMimeMediaType, IppTag::MIMETYPE}, |
| {kMemberAttrName, IppTag::MEMBERNAME}}; |
| auto iter = tags.find(name); |
| if (iter == tags.end()) { |
| LOG(ERROR) << "Given unknown tag name " << name; |
| exit(1); |
| } |
| return iter->second; |
| } |
| |
| void AddIppHeader(const IppHeader& header, SmartBuffer* buf) { |
| IppHeader header_copy = header; |
| PackIppHeader(&header_copy); |
| buf->Add(&header_copy, sizeof(header_copy)); |
| } |
| |
| void AddEndOfAttributes(SmartBuffer* buf) { |
| IppTag end_tag = IppTag::END; |
| uint8_t tag_value = static_cast<uint8_t>(end_tag); |
| buf->Add(&tag_value, sizeof(tag_value)); |
| } |
| |
| void AddPrinterAttributes(const std::vector<IppAttribute>& ipp_attributes, |
| const std::string& group, SmartBuffer* buf) { |
| std::map<std::string, |
| std::function<void(const IppAttribute&, SmartBuffer*)>> |
| function_map = {{kUnsupported, AddString}, |
| {kNoValue, AddString}, |
| {kInteger, AddInteger}, |
| {kBoolean, AddBoolean}, |
| {kEnum, AddInteger}, |
| {kOctetString, AddOctetString}, |
| {kDateTime, AddDate}, |
| {kResolution, AddResolution}, |
| {kRangeOfInteger, AddRange}, |
| {kBegCollection, AddString}, |
| {kEndCollection, AddString}, |
| {kTextWithoutLanguage, AddString}, |
| {kNameWithoutLanguage, AddString}, |
| {kKeyword, AddString}, |
| {kUri, AddString}, |
| {kCharset, AddString}, |
| {kNaturalLanguage, AddString}, |
| {kMimeMediaType, AddString}, |
| {kMemberAttrName, AddString}}; |
| |
| // Add attribute group tag. |
| IppTag group_tag = GetIppTag(group); |
| uint8_t tag = static_cast<uint8_t>(group_tag); |
| buf->Add(&tag, sizeof(tag)); |
| |
| for (const IppAttribute& attribute : ipp_attributes) { |
| auto iter = function_map.find(attribute.type()); |
| if (iter == function_map.end()) { |
| LOG(ERROR) << "Found attribute with invalid type " << attribute.type(); |
| exit(1); |
| } |
| const auto& add_function = iter->second; |
| add_function(attribute, buf); |
| } |
| } |
| |
| size_t GetBaseAttributeSize(const IppAttribute& attribute) { |
| size_t multiplier = 1; |
| // These types are special cases where although the values may be stored in a |
| // list form, the tag and name fields only appear once. |
| std::set<std::string> exempt = {kDateTime, kOctetString, kResolution, |
| kRangeOfInteger}; |
| if (attribute.IsList() && exempt.find(attribute.type()) == exempt.end()) { |
| multiplier = attribute.GetListSize(); |
| } |
| // There are 3 elements which are repeated multiple times for each value in |
| // |attribute|: |
| // tag - (1 byte) |
| // name-length (2 bytes) |
| // value-length (2 bytes) |
| // |
| // Which makes 5 bytes per value. |
| return (5 * multiplier) + attribute.name().size(); |
| } |
| |
| size_t GetBooleanAttributeSize(const IppAttribute& attribute) { |
| size_t total_size = GetBaseAttributeSize(attribute); |
| if (attribute.IsList()) { |
| total_size += attribute.GetListSize(); |
| } else { |
| total_size += 1; |
| } |
| return total_size; |
| } |
| |
| size_t GetIntAttributeSize(const IppAttribute& attribute) { |
| size_t total_size = GetBaseAttributeSize(attribute); |
| if (attribute.IsList()) { |
| total_size += attribute.GetListSize() * 4; |
| } else { |
| total_size += 4; |
| } |
| return total_size; |
| } |
| |
| size_t GetStringAttributeSize(const IppAttribute& attribute) { |
| size_t total_size = GetBaseAttributeSize(attribute); |
| if (attribute.IsList()) { |
| const std::vector<std::string> values = attribute.GetStrings(); |
| for (const std::string& value : values) { |
| total_size += value.size(); |
| } |
| } else { |
| const std::string value = attribute.GetString(); |
| total_size += value.size(); |
| } |
| return total_size; |
| } |
| |
| size_t GetOctetStringAttributeSize(const IppAttribute& attribute) { |
| size_t total_size = GetBaseAttributeSize(attribute); |
| if (attribute.IsList()) { |
| total_size += attribute.GetListSize(); |
| } else { |
| const std::string value = attribute.GetString(); |
| total_size += value.size(); |
| } |
| return total_size; |
| } |
| |
| size_t GetDateTimeAttributeSize(const IppAttribute& attribute) { |
| return GetBaseAttributeSize(attribute) + kDateTimeSize; |
| } |
| |
| size_t GetResolutionAttributeSize(const IppAttribute& attribute) { |
| return GetBaseAttributeSize(attribute) + kResolutionSize; |
| } |
| |
| size_t GetRangeOfIntegerAttributeSize(const IppAttribute& attribute) { |
| return GetBaseAttributeSize(attribute) + kResolutionSize; |
| } |
| |
| size_t GetAttributesSize(const std::vector<IppAttribute>& attributes) { |
| std::map<std::string, std::function<size_t(const IppAttribute&)>> |
| function_map = {{kUnsupported, GetStringAttributeSize}, |
| {kNoValue, GetStringAttributeSize}, |
| {kInteger, GetIntAttributeSize}, |
| {kBoolean, GetBooleanAttributeSize}, |
| {kEnum, GetIntAttributeSize}, |
| {kOctetString, GetOctetStringAttributeSize}, |
| {kDateTime, GetDateTimeAttributeSize}, |
| {kResolution, GetResolutionAttributeSize}, |
| {kRangeOfInteger, GetRangeOfIntegerAttributeSize}, |
| {kBegCollection, GetStringAttributeSize}, |
| {kEndCollection, GetStringAttributeSize}, |
| {kTextWithoutLanguage, GetStringAttributeSize}, |
| {kNameWithoutLanguage, GetStringAttributeSize}, |
| {kKeyword, GetStringAttributeSize}, |
| {kUri, GetStringAttributeSize}, |
| {kCharset, GetStringAttributeSize}, |
| {kNaturalLanguage, GetStringAttributeSize}, |
| {kMimeMediaType, GetStringAttributeSize}, |
| {kMemberAttrName, GetStringAttributeSize}}; |
| size_t total_size = 0; |
| for (const IppAttribute& attribute : attributes) { |
| auto iter = function_map.find(attribute.type()); |
| if (iter == function_map.end()) { |
| LOG(ERROR) << "Found attribute with invalid type " << attribute.type(); |
| exit(1); |
| } |
| const auto& size_function = iter->second; |
| total_size += size_function(attribute); |
| } |
| return total_size; |
| } |
| |
| void AddBoolean(const IppAttribute& attribute, SmartBuffer* buf) { |
| IppTag tag = GetIppTag(attribute.type()); |
| const std::string name = attribute.name(); |
| if (attribute.IsList()) { |
| std::vector<bool> values = attribute.GetBools(); |
| for (size_t i = 0; i < values.size(); ++i) { |
| // We only include the name of the attribute for the first value. |
| bool include_name = (i == 0); |
| AddBooleanAttribute(values[i], tag, name, include_name, buf); |
| } |
| } else { |
| bool value = attribute.GetBool(); |
| AddBooleanAttribute(value, tag, name, true, buf); |
| } |
| } |
| |
| void AddInteger(const IppAttribute& attribute, SmartBuffer* buf) { |
| IppTag tag = GetIppTag(attribute.type()); |
| const std::string name = attribute.name(); |
| if (attribute.IsList()) { |
| std::vector<int> values = attribute.GetInts(); |
| for (size_t i = 0; i < values.size(); ++i) { |
| // We only include the name of the attribute for the first value. |
| bool include_name = (i == 0); |
| AddIntAttribute(values[i], tag, name, include_name, buf); |
| } |
| } else { |
| int value = attribute.GetInt(); |
| AddIntAttribute(value, tag, name, true, buf); |
| } |
| } |
| |
| void AddString(const IppAttribute& attribute, SmartBuffer* buf) { |
| IppTag tag = GetIppTag(attribute.type()); |
| const std::string name = attribute.name(); |
| if (attribute.IsList()) { |
| std::vector<std::string> values = attribute.GetStrings(); |
| for (size_t i = 0; i < values.size(); ++i) { |
| // We only include the name of the attribute for the first value. |
| bool include_name = (i == 0); |
| AddStringAttribute(values[i], tag, name, include_name, buf); |
| } |
| } else { |
| std::string value = attribute.GetString(); |
| AddStringAttribute(value, tag, name, true, buf); |
| } |
| } |
| |
| void AddOctetString(const IppAttribute& attribute, SmartBuffer* buf) { |
| IppTag tag = GetIppTag(attribute.type()); |
| const std::string name = attribute.name(); |
| if (attribute.IsList()) { |
| std::vector<uint8_t> values = attribute.GetBytes(); |
| AddTag(tag, buf); |
| AddName(name, true, buf); |
| AddValueLength(values.size(), buf); |
| buf->Add(values.data(), values.size()); |
| } else { |
| std::string value = attribute.GetString(); |
| AddStringAttribute(value, tag, name, true, buf); |
| } |
| } |
| |
| void AddDate(const IppAttribute& attribute, SmartBuffer* buf) { |
| IppTag tag = GetIppTag(attribute.type()); |
| const std::string name = attribute.name(); |
| CHECK(attribute.IsList()) << "Date value is in an incorrect format"; |
| |
| AddTag(tag, buf); |
| AddName(name, true, buf); |
| AddValueLength(kDateTimeSize, buf); |
| |
| std::vector<uint8_t> date = attribute.GetBytes(); |
| buf->Add(date.data(), date.size()); |
| } |
| |
| void AddRange(const IppAttribute& attribute, SmartBuffer* buf) { |
| IppTag tag = GetIppTag(attribute.type()); |
| const std::string name = attribute.name(); |
| |
| CHECK(attribute.IsList()) << "Range value is in an incorrect format"; |
| std::vector<int> range = attribute.GetInts(); |
| CHECK_EQ(range.size(), 2) << "Range list is an invalid size"; |
| |
| AddTag(tag, buf); |
| AddName(name, true, buf); |
| AddValueLength(kRangeOfIntegerSize, buf); |
| |
| for (int value : range) { |
| // Need to put the int value in network byte order. |
| value = htonl(value); |
| buf->Add(&value, sizeof(value)); |
| } |
| } |
| |
| void AddResolution(const IppAttribute& attribute, SmartBuffer* buf) { |
| IppTag tag = GetIppTag(attribute.type()); |
| const std::string name = attribute.name(); |
| |
| CHECK(attribute.IsList()) << "Resolution value is in an incorrect format"; |
| std::vector<int> resolution = attribute.GetInts(); |
| CHECK_EQ(resolution.size(), 3) << "Resolution list is an invalid size"; |
| CHECK_LE(resolution[2], UCHAR_MAX) << "Resolution units value is too large"; |
| |
| AddTag(tag, buf); |
| AddName(name, true, buf); |
| AddValueLength(kResolutionSize, buf); |
| |
| for (size_t i = 0; i < resolution.size() - 1; ++i) { |
| int value = resolution[i]; |
| // Need to put the int value in network byte order. |
| value = htonl(value); |
| buf->Add(&value, sizeof(value)); |
| } |
| |
| uint8_t units = resolution[2]; |
| buf->Add(&units, sizeof(units)); |
| } |