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.
4
5 #include "google_apis/gcm/base/mcs_util.h"
6
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"
13
14 namespace gcm {
15
16 namespace {
17
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",
26 "mcs_proto.Close",
27 "mcs_proto.MessageStanza",
28 "mcs_proto.PresenceStanza",
29 "mcs_proto.IqStanza",
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"
38 };
39 COMPILE_ASSERT(arraysize(kProtoNames) == kNumProtoTypes,
40 ProtoNamesMustIncludeAllTags);
41
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";
47
48 // Maximum amount of time to save an unsent outgoing message for.
49 const int kMaxTTLSeconds = 4 * 7 * 24 * 60 * 60; // 4 weeks.
50
51 } // namespace
52
BuildLoginRequest(uint64 auth_id,uint64 auth_token,const std::string & version_string)53 scoped_ptr<mcs_proto::LoginRequest> BuildLoginRequest(
54 uint64 auth_id,
55 uint64 auth_token,
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);
60
61 std::string auth_id_str = base::Uint64ToString(auth_id);
62 std::string auth_token_str = base::Uint64ToString(auth_token);
63
64 scoped_ptr<mcs_proto::LoginRequest> login_request(
65 new mcs_proto::LoginRequest());
66
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);
77
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();
82 }
83
BuildStreamAck()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();
91 }
92
BuildSelectiveAck(const std::vector<std::string> & acked_ids)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();
105 }
106
107 // Utility method to build a google::protobuf::MessageLite object from a MCS
108 // tag.
BuildProtobufFromTag(uint8 tag)109 scoped_ptr<google::protobuf::MessageLite> BuildProtobufFromTag(uint8 tag) {
110 switch(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());
123 case kCloseTag:
124 return scoped_ptr<google::protobuf::MessageLite>(
125 new mcs_proto::Close());
126 case kIqStanzaTag:
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());
135 default:
136 return scoped_ptr<google::protobuf::MessageLite>();
137 }
138 }
139
140 // Utility method to extract a MCS tag from a google::protobuf::MessageLite
141 // object.
GetMCSProtoTag(const google::protobuf::MessageLite & message)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]) {
153 return kCloseTag;
154 } else if (type_name == kProtoNames[kIqStanzaTag]) {
155 return kIqStanzaTag;
156 } else if (type_name == kProtoNames[kDataMessageStanzaTag]) {
157 return kDataMessageStanzaTag;
158 } else if (type_name == kProtoNames[kStreamErrorStanzaTag]) {
159 return kStreamErrorStanzaTag;
160 }
161 return -1;
162 }
163
GetPersistentId(const google::protobuf::MessageLite & protobuf)164 std::string GetPersistentId(const google::protobuf::MessageLite& protobuf) {
165 if (protobuf.GetTypeName() == kProtoNames[kIqStanzaTag]) {
166 return reinterpret_cast<const mcs_proto::IqStanza*>(&protobuf)->
167 persistent_id();
168 } else if (protobuf.GetTypeName() == kProtoNames[kDataMessageStanzaTag]) {
169 return reinterpret_cast<const mcs_proto::DataMessageStanza*>(&protobuf)->
170 persistent_id();
171 }
172 // Not all message types have persistent ids. Just return empty string;
173 return "";
174 }
175
SetPersistentId(const std::string & persistent_id,google::protobuf::MessageLite * protobuf)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);
181 return;
182 } else if (protobuf->GetTypeName() == kProtoNames[kDataMessageStanzaTag]) {
183 reinterpret_cast<mcs_proto::DataMessageStanza*>(protobuf)->
184 set_persistent_id(persistent_id);
185 return;
186 }
187 NOTREACHED();
188 }
189
GetLastStreamIdReceived(const google::protobuf::MessageLite & protobuf)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();
206 }
207 // Not all message types have last stream ids. Just return 0.
208 return 0;
209 }
210
SetLastStreamIdReceived(uint32 val,google::protobuf::MessageLite * protobuf)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);
216 return;
217 } else if (protobuf->GetTypeName() == kProtoNames[kHeartbeatPingTag]) {
218 reinterpret_cast<mcs_proto::HeartbeatPing*>(protobuf)->
219 set_last_stream_id_received(val);
220 return;
221 } else if (protobuf->GetTypeName() == kProtoNames[kHeartbeatAckTag]) {
222 reinterpret_cast<mcs_proto::HeartbeatAck*>(protobuf)->
223 set_last_stream_id_received(val);
224 return;
225 } else if (protobuf->GetTypeName() == kProtoNames[kDataMessageStanzaTag]) {
226 reinterpret_cast<mcs_proto::DataMessageStanza*>(protobuf)->
227 set_last_stream_id_received(val);
228 return;
229 } else if (protobuf->GetTypeName() == kProtoNames[kLoginResponseTag]) {
230 reinterpret_cast<mcs_proto::LoginResponse*>(protobuf)->
231 set_last_stream_id_received(val);
232 return;
233 }
234 NOTREACHED();
235 }
236
HasTTLExpired(const google::protobuf::MessageLite & protobuf,base::Clock * clock)237 bool HasTTLExpired(const google::protobuf::MessageLite& protobuf,
238 base::Clock* clock) {
239 if (protobuf.GetTypeName() != kProtoNames[kDataMessageStanzaTag])
240 return false;
241 uint64 ttl = GetTTL(protobuf);
242 uint64 sent =
243 reinterpret_cast<const mcs_proto::DataMessageStanza*>(&protobuf)->sent();
244 DCHECK(sent);
245 return ttl > 0 &&
246 clock->Now() >
247 base::Time::FromInternalValue(
248 (sent + ttl) * base::Time::kMicrosecondsPerSecond);
249 }
250
GetTTL(const google::protobuf::MessageLite & protobuf)251 int GetTTL(const google::protobuf::MessageLite& protobuf) {
252 if (protobuf.GetTypeName() != kProtoNames[kDataMessageStanzaTag])
253 return 0;
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();
260 }
261
262 } // namespace gcm
263