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 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 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 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. 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. 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 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 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 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 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 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 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