• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 24 * 60 * 60;  // 1 day.
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