1// Copyright 2014 The Chromium 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 "components/pairing/proto_decoder.h"
6
7#include "components/pairing/pairing_api.pb.h"
8#include "net/base/io_buffer.h"
9
10namespace {
11enum {
12  MESSAGE_NONE,
13  MESSAGE_HOST_STATUS,
14  MESSAGE_CONFIGURE_HOST,
15  MESSAGE_PAIR_DEVICES,
16  MESSAGE_COMPLETE_SETUP,
17  MESSAGE_ERROR,
18  NUM_MESSAGES,
19};
20}
21
22namespace pairing_chromeos {
23
24ProtoDecoder::ProtoDecoder(Observer* observer)
25    : observer_(observer),
26      next_message_type_(MESSAGE_NONE),
27      next_message_size_(0) {
28  DCHECK(observer_);
29}
30
31ProtoDecoder::~ProtoDecoder() {}
32
33bool ProtoDecoder::DecodeIOBuffer(int size,
34                                  ProtoDecoder::IOBufferRefPtr io_buffer) {
35  // Update the message buffer.
36  message_buffer_.AddIOBuffer(io_buffer, size);
37
38  // If there is no current message, the next byte is the message type.
39  if (next_message_type_ == MESSAGE_NONE) {
40    if (message_buffer_.AvailableBytes() < static_cast<int>(sizeof(uint8_t)))
41      return true;
42
43    uint8_t message_type = MESSAGE_NONE;
44    message_buffer_.ReadBytes(reinterpret_cast<char*>(&message_type),
45                               sizeof(message_type));
46
47    if (message_type == MESSAGE_NONE || message_type >= NUM_MESSAGES) {
48      LOG(ERROR) << "Unknown message type received: " << message_type;
49      return false;
50    }
51    next_message_type_ = message_type;
52  }
53
54  // If the message size isn't set, the next two bytes are the message size.
55  if (next_message_size_ == 0) {
56    if (message_buffer_.AvailableBytes() < static_cast<int>(sizeof(uint16_t)))
57      return true;
58
59    // The size is sent in network byte order.
60    uint8_t high_byte = 0;
61    message_buffer_.ReadBytes(reinterpret_cast<char*>(&high_byte),
62                               sizeof(high_byte));
63    uint8_t low_byte = 0;
64    message_buffer_.ReadBytes(reinterpret_cast<char*>(&low_byte),
65                               sizeof(low_byte));
66
67    next_message_size_ = (high_byte << 8) + low_byte;
68  }
69
70  // If the whole proto buffer is not yet available, return early.
71  if (message_buffer_.AvailableBytes() < next_message_size_)
72    return true;
73
74  std::vector<char> buffer(next_message_size_);
75  message_buffer_.ReadBytes(&buffer[0], next_message_size_);
76
77  switch (next_message_type_) {
78    case MESSAGE_HOST_STATUS: {
79        pairing_api::HostStatus message;
80        message.ParseFromArray(&buffer[0], buffer.size());
81        observer_->OnHostStatusMessage(message);
82      }
83      break;
84    case MESSAGE_CONFIGURE_HOST: {
85        pairing_api::ConfigureHost message;
86        message.ParseFromArray(&buffer[0], buffer.size());
87        observer_->OnConfigureHostMessage(message);
88      }
89      break;
90    case MESSAGE_PAIR_DEVICES: {
91        pairing_api::PairDevices message;
92        message.ParseFromArray(&buffer[0], buffer.size());
93        observer_->OnPairDevicesMessage(message);
94      }
95      break;
96    case MESSAGE_COMPLETE_SETUP: {
97        pairing_api::CompleteSetup message;
98        message.ParseFromArray(&buffer[0], buffer.size());
99        observer_->OnCompleteSetupMessage(message);
100      }
101      break;
102    case MESSAGE_ERROR: {
103        pairing_api::Error message;
104        message.ParseFromArray(&buffer[0], buffer.size());
105        observer_->OnErrorMessage(message);
106      }
107      break;
108
109    default:
110      NOTREACHED();
111      break;
112  }
113
114  // Reset the message data.
115  next_message_type_ = MESSAGE_NONE;
116  next_message_size_ = 0;
117
118  return true;
119}
120
121ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendHostStatus(
122    const pairing_api::HostStatus& message, int* size) {
123  std::string serialized_proto;
124  if (!message.SerializeToString(&serialized_proto)) {
125    NOTREACHED();
126  }
127
128  return SendMessage(MESSAGE_HOST_STATUS, serialized_proto, size);
129}
130
131ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendConfigureHost(
132    const pairing_api::ConfigureHost& message, int* size) {
133  std::string serialized_proto;
134  if (!message.SerializeToString(&serialized_proto)) {
135    NOTREACHED();
136  }
137
138  return SendMessage(MESSAGE_CONFIGURE_HOST, serialized_proto, size);
139}
140
141ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendPairDevices(
142    const pairing_api::PairDevices& message, int* size) {
143  std::string serialized_proto;
144  if (!message.SerializeToString(&serialized_proto)) {
145    NOTREACHED();
146  }
147
148  return SendMessage(MESSAGE_PAIR_DEVICES, serialized_proto, size);
149}
150
151ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendCompleteSetup(
152    const pairing_api::CompleteSetup& message, int* size) {
153  std::string serialized_proto;
154  if (!message.SerializeToString(&serialized_proto)) {
155    NOTREACHED();
156  }
157
158  return SendMessage(MESSAGE_COMPLETE_SETUP, serialized_proto, size);
159}
160
161ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendError(
162    const pairing_api::Error& message, int* size) {
163  std::string serialized_proto;
164  if (!message.SerializeToString(&serialized_proto)) {
165    NOTREACHED();
166  }
167
168  return SendMessage(MESSAGE_ERROR, serialized_proto, size);
169}
170
171ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendMessage(
172    uint8_t message_type,
173    const std::string& message,
174    int* size) {
175  uint16_t message_size = message.size();
176
177  *size = sizeof(message_type) + sizeof(message_size) + message.size();
178  IOBufferRefPtr io_buffer(new net::IOBuffer(*size));
179
180  // Write the message type.
181  int offset = 0;
182  memcpy(&io_buffer->data()[offset], &message_type, sizeof(message_type));
183  offset += sizeof(message_type);
184
185  // Network byte order.
186  // Write the high byte of the size.
187  uint8_t data = (message_size >> 8) & 0xFF;
188  memcpy(&io_buffer->data()[offset], &data, sizeof(data));
189  offset += sizeof(data);
190  // Write the low byte of the size.
191  data = message_size & 0xFF;
192  memcpy(&io_buffer->data()[offset], &data, sizeof(data));
193  offset += sizeof(data);
194
195  // Write the actual message.
196  memcpy(&io_buffer->data()[offset], message.data(), message.size());
197
198  return io_buffer;
199}
200
201}  // namespace pairing_chromeos
202