1 // Copyright 2013 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.
5 #include "google_apis/gcm/base/mcs_util.h"
7 #include "base/format_macros.h"
8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/time/clock.h"
12 #include "base/time/time.h"
18 // Type names corresponding to MCSProtoTags. Useful for identifying what type
19 // of MCS protobuf is contained within a google::protobuf::MessageLite object.
20 // WARNING: must match the order in MCSProtoTag.
21 const char* kProtoNames
[] = {
22 "mcs_proto.HeartbeatPing",
23 "mcs_proto.HeartbeatAck",
24 "mcs_proto.LoginRequest",
25 "mcs_proto.LoginResponse",
27 "mcs_proto.MessageStanza",
28 "mcs_proto.PresenceStanza",
30 "mcs_proto.DataMessageStanza",
31 "mcs_proto.BatchPresenceStanza",
32 "mcs_proto.StreamErrorStanza",
33 "mcs_proto.HttpRequest",
34 "mcs_proto.HttpResponse",
35 "mcs_proto.BindAccountRequest",
36 "mcs_proto.BindAccountResponse",
37 "mcs_proto.TalkMetadata"
39 static_assert(arraysize(kProtoNames
) == kNumProtoTypes
,
40 "Proto Names Must Include All Tags");
42 const char kLoginId
[] = "chrome-";
43 const char kLoginDomain
[] = "mcs.android.com";
44 const char kLoginDeviceIdPrefix
[] = "android-";
45 const char kLoginSettingDefaultName
[] = "new_vc";
46 const char kLoginSettingDefaultValue
[] = "1";
48 // Maximum amount of time to save an unsent outgoing message for.
49 const int kMaxTTLSeconds
= 24 * 60 * 60; // 1 day.
53 scoped_ptr
<mcs_proto::LoginRequest
> BuildLoginRequest(
56 const std::string
& version_string
) {
57 // Create a hex encoded auth id for the device id field.
58 std::string auth_id_hex
;
59 auth_id_hex
= base::StringPrintf("%" PRIx64
, auth_id
);
61 std::string auth_id_str
= base::Uint64ToString(auth_id
);
62 std::string auth_token_str
= base::Uint64ToString(auth_token
);
64 scoped_ptr
<mcs_proto::LoginRequest
> login_request(
65 new mcs_proto::LoginRequest());
67 login_request
->set_adaptive_heartbeat(false);
68 login_request
->set_auth_service(mcs_proto::LoginRequest::ANDROID_ID
);
69 login_request
->set_auth_token(auth_token_str
);
70 login_request
->set_id(kLoginId
+ version_string
);
71 login_request
->set_domain(kLoginDomain
);
72 login_request
->set_device_id(kLoginDeviceIdPrefix
+ auth_id_hex
);
73 login_request
->set_network_type(1);
74 login_request
->set_resource(auth_id_str
);
75 login_request
->set_user(auth_id_str
);
76 login_request
->set_use_rmq2(true);
78 login_request
->add_setting();
79 login_request
->mutable_setting(0)->set_name(kLoginSettingDefaultName
);
80 login_request
->mutable_setting(0)->set_value(kLoginSettingDefaultValue
);
81 return login_request
.Pass();
84 scoped_ptr
<mcs_proto::IqStanza
> BuildStreamAck() {
85 scoped_ptr
<mcs_proto::IqStanza
> stream_ack_iq(new mcs_proto::IqStanza());
86 stream_ack_iq
->set_type(mcs_proto::IqStanza::SET
);
87 stream_ack_iq
->set_id("");
88 stream_ack_iq
->mutable_extension()->set_id(kStreamAck
);
89 stream_ack_iq
->mutable_extension()->set_data("");
90 return stream_ack_iq
.Pass();
93 scoped_ptr
<mcs_proto::IqStanza
> BuildSelectiveAck(
94 const std::vector
<std::string
>& acked_ids
) {
95 scoped_ptr
<mcs_proto::IqStanza
> selective_ack_iq(new mcs_proto::IqStanza());
96 selective_ack_iq
->set_type(mcs_proto::IqStanza::SET
);
97 selective_ack_iq
->set_id("");
98 selective_ack_iq
->mutable_extension()->set_id(kSelectiveAck
);
99 mcs_proto::SelectiveAck selective_ack
;
100 for (size_t i
= 0; i
< acked_ids
.size(); ++i
)
101 selective_ack
.add_id(acked_ids
[i
]);
102 selective_ack_iq
->mutable_extension()->set_data(
103 selective_ack
.SerializeAsString());
104 return selective_ack_iq
.Pass();
107 // Utility method to build a google::protobuf::MessageLite object from a MCS
109 scoped_ptr
<google::protobuf::MessageLite
> BuildProtobufFromTag(uint8 tag
) {
111 case kHeartbeatPingTag
:
112 return scoped_ptr
<google::protobuf::MessageLite
>(
113 new mcs_proto::HeartbeatPing());
114 case kHeartbeatAckTag
:
115 return scoped_ptr
<google::protobuf::MessageLite
>(
116 new mcs_proto::HeartbeatAck());
117 case kLoginRequestTag
:
118 return scoped_ptr
<google::protobuf::MessageLite
>(
119 new mcs_proto::LoginRequest());
120 case kLoginResponseTag
:
121 return scoped_ptr
<google::protobuf::MessageLite
>(
122 new mcs_proto::LoginResponse());
124 return scoped_ptr
<google::protobuf::MessageLite
>(
125 new mcs_proto::Close());
127 return scoped_ptr
<google::protobuf::MessageLite
>(
128 new mcs_proto::IqStanza());
129 case kDataMessageStanzaTag
:
130 return scoped_ptr
<google::protobuf::MessageLite
>(
131 new mcs_proto::DataMessageStanza());
132 case kStreamErrorStanzaTag
:
133 return scoped_ptr
<google::protobuf::MessageLite
>(
134 new mcs_proto::StreamErrorStanza());
136 return scoped_ptr
<google::protobuf::MessageLite
>();
140 // Utility method to extract a MCS tag from a google::protobuf::MessageLite
142 int GetMCSProtoTag(const google::protobuf::MessageLite
& message
) {
143 const std::string
& type_name
= message
.GetTypeName();
144 if (type_name
== kProtoNames
[kHeartbeatPingTag
]) {
145 return kHeartbeatPingTag
;
146 } else if (type_name
== kProtoNames
[kHeartbeatAckTag
]) {
147 return kHeartbeatAckTag
;
148 } else if (type_name
== kProtoNames
[kLoginRequestTag
]) {
149 return kLoginRequestTag
;
150 } else if (type_name
== kProtoNames
[kLoginResponseTag
]) {
151 return kLoginResponseTag
;
152 } else if (type_name
== kProtoNames
[kCloseTag
]) {
154 } else if (type_name
== kProtoNames
[kIqStanzaTag
]) {
156 } else if (type_name
== kProtoNames
[kDataMessageStanzaTag
]) {
157 return kDataMessageStanzaTag
;
158 } else if (type_name
== kProtoNames
[kStreamErrorStanzaTag
]) {
159 return kStreamErrorStanzaTag
;
164 std::string
GetPersistentId(const google::protobuf::MessageLite
& protobuf
) {
165 if (protobuf
.GetTypeName() == kProtoNames
[kIqStanzaTag
]) {
166 return reinterpret_cast<const mcs_proto::IqStanza
*>(&protobuf
)->
168 } else if (protobuf
.GetTypeName() == kProtoNames
[kDataMessageStanzaTag
]) {
169 return reinterpret_cast<const mcs_proto::DataMessageStanza
*>(&protobuf
)->
172 // Not all message types have persistent ids. Just return empty string;
176 void SetPersistentId(const std::string
& persistent_id
,
177 google::protobuf::MessageLite
* protobuf
) {
178 if (protobuf
->GetTypeName() == kProtoNames
[kIqStanzaTag
]) {
179 reinterpret_cast<mcs_proto::IqStanza
*>(protobuf
)->
180 set_persistent_id(persistent_id
);
182 } else if (protobuf
->GetTypeName() == kProtoNames
[kDataMessageStanzaTag
]) {
183 reinterpret_cast<mcs_proto::DataMessageStanza
*>(protobuf
)->
184 set_persistent_id(persistent_id
);
190 uint32
GetLastStreamIdReceived(const google::protobuf::MessageLite
& protobuf
) {
191 if (protobuf
.GetTypeName() == kProtoNames
[kIqStanzaTag
]) {
192 return reinterpret_cast<const mcs_proto::IqStanza
*>(&protobuf
)->
193 last_stream_id_received();
194 } else if (protobuf
.GetTypeName() == kProtoNames
[kDataMessageStanzaTag
]) {
195 return reinterpret_cast<const mcs_proto::DataMessageStanza
*>(&protobuf
)->
196 last_stream_id_received();
197 } else if (protobuf
.GetTypeName() == kProtoNames
[kHeartbeatPingTag
]) {
198 return reinterpret_cast<const mcs_proto::HeartbeatPing
*>(&protobuf
)->
199 last_stream_id_received();
200 } else if (protobuf
.GetTypeName() == kProtoNames
[kHeartbeatAckTag
]) {
201 return reinterpret_cast<const mcs_proto::HeartbeatAck
*>(&protobuf
)->
202 last_stream_id_received();
203 } else if (protobuf
.GetTypeName() == kProtoNames
[kLoginResponseTag
]) {
204 return reinterpret_cast<const mcs_proto::LoginResponse
*>(&protobuf
)->
205 last_stream_id_received();
207 // Not all message types have last stream ids. Just return 0.
211 void SetLastStreamIdReceived(uint32 val
,
212 google::protobuf::MessageLite
* protobuf
) {
213 if (protobuf
->GetTypeName() == kProtoNames
[kIqStanzaTag
]) {
214 reinterpret_cast<mcs_proto::IqStanza
*>(protobuf
)->
215 set_last_stream_id_received(val
);
217 } else if (protobuf
->GetTypeName() == kProtoNames
[kHeartbeatPingTag
]) {
218 reinterpret_cast<mcs_proto::HeartbeatPing
*>(protobuf
)->
219 set_last_stream_id_received(val
);
221 } else if (protobuf
->GetTypeName() == kProtoNames
[kHeartbeatAckTag
]) {
222 reinterpret_cast<mcs_proto::HeartbeatAck
*>(protobuf
)->
223 set_last_stream_id_received(val
);
225 } else if (protobuf
->GetTypeName() == kProtoNames
[kDataMessageStanzaTag
]) {
226 reinterpret_cast<mcs_proto::DataMessageStanza
*>(protobuf
)->
227 set_last_stream_id_received(val
);
229 } else if (protobuf
->GetTypeName() == kProtoNames
[kLoginResponseTag
]) {
230 reinterpret_cast<mcs_proto::LoginResponse
*>(protobuf
)->
231 set_last_stream_id_received(val
);
237 bool HasTTLExpired(const google::protobuf::MessageLite
& protobuf
,
238 base::Clock
* clock
) {
239 if (protobuf
.GetTypeName() != kProtoNames
[kDataMessageStanzaTag
])
241 uint64 ttl
= GetTTL(protobuf
);
243 reinterpret_cast<const mcs_proto::DataMessageStanza
*>(&protobuf
)->sent();
247 base::Time::FromInternalValue(
248 (sent
+ ttl
) * base::Time::kMicrosecondsPerSecond
);
251 int GetTTL(const google::protobuf::MessageLite
& protobuf
) {
252 if (protobuf
.GetTypeName() != kProtoNames
[kDataMessageStanzaTag
])
254 const mcs_proto::DataMessageStanza
* data_message
=
255 reinterpret_cast<const mcs_proto::DataMessageStanza
*>(&protobuf
);
256 if (!data_message
->has_ttl())
257 return kMaxTTLSeconds
;
258 DCHECK_LE(data_message
->ttl(), kMaxTTLSeconds
);
259 return data_message
->ttl();