Home | History | Annotate | Download | only in base
      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 
     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