1 // Copyright 2018 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 "components/update_client/protocol_serializer_json.h"
6 
7 #include <memory>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/json/json_writer.h"
12 #include "base/values.h"
13 #include "build/branding_buildflags.h"
14 #include "build/build_config.h"
15 #include "components/update_client/updater_state.h"
16 
17 namespace update_client {
18 
19 using Value = base::Value;
20 
Serialize(const protocol_request::Request & request) const21 std::string ProtocolSerializerJSON::Serialize(
22     const protocol_request::Request& request) const {
23   Value root_node(Value::Type::DICTIONARY);
24   auto* request_node =
25       root_node.SetKey("request", Value(Value::Type::DICTIONARY));
26   request_node->SetKey("protocol", Value(request.protocol_version));
27   request_node->SetKey("dedup", Value("cr"));
28   request_node->SetKey("acceptformat", Value("crx2,crx3"));
29   if (!request.additional_attributes.empty()) {
30     for (const auto& attr : request.additional_attributes)
31       request_node->SetKey(attr.first, Value(attr.second));
32   }
33   request_node->SetKey("sessionid", Value(request.session_id));
34   request_node->SetKey("requestid", Value(request.request_id));
35   request_node->SetKey("@updater", Value(request.updatername));
36   request_node->SetKey("prodversion", Value(request.updaterversion));
37   request_node->SetKey("updaterversion", Value(request.prodversion));
38   request_node->SetKey("lang", Value(request.lang));
39   request_node->SetKey("@os", Value(request.operating_system));
40   request_node->SetKey("arch", Value(request.arch));
41   request_node->SetKey("nacl_arch", Value(request.nacl_arch));
42 #if defined(OS_WIN)
43   if (request.is_wow64)
44     request_node->SetKey("wow64", Value(request.is_wow64));
45 #endif  // OS_WIN
46   if (!request.updaterchannel.empty())
47     request_node->SetKey("updaterchannel", Value(request.updaterchannel));
48   if (!request.prodchannel.empty())
49     request_node->SetKey("prodchannel", Value(request.prodchannel));
50   if (!request.dlpref.empty())
51     request_node->SetKey("dlpref", Value(request.dlpref));
52   if (request.domain_joined) {
53     request_node->SetKey(UpdaterState::kIsEnterpriseManaged,
54                          Value(*request.domain_joined));
55   }
56 
57   // HW platform information.
58   auto* hw_node = request_node->SetKey("hw", Value(Value::Type::DICTIONARY));
59   hw_node->SetKey("physmemory", Value(static_cast<int>(request.hw.physmemory)));
60 
61   // OS version and platform information.
62   auto* os_node = request_node->SetKey("os", Value(Value::Type::DICTIONARY));
63   os_node->SetKey("platform", Value(request.os.platform));
64   os_node->SetKey("arch", Value(request.os.arch));
65   if (!request.os.version.empty())
66     os_node->SetKey("version", Value(request.os.version));
67   if (!request.os.service_pack.empty())
68     os_node->SetKey("sp", Value(request.os.service_pack));
69 
70 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
71   if (request.updater) {
72     const auto& updater = *request.updater;
73     auto* updater_node =
74         request_node->SetKey("updater", Value(Value::Type::DICTIONARY));
75     updater_node->SetKey("name", Value(updater.name));
76     updater_node->SetKey("ismachine", Value(updater.is_machine));
77     updater_node->SetKey("autoupdatecheckenabled",
78                          Value(updater.autoupdate_check_enabled));
79     updater_node->SetKey("updatepolicy", Value(updater.update_policy));
80     if (!updater.version.empty())
81       updater_node->SetKey("version", Value(updater.version));
82     if (updater.last_checked)
83       updater_node->SetKey("lastchecked", Value(*updater.last_checked));
84     if (updater.last_started)
85       updater_node->SetKey("laststarted", Value(*updater.last_started));
86   }
87 #endif
88 
89   std::vector<Value> app_nodes;
90   for (const auto& app : request.apps) {
91     Value app_node(Value::Type::DICTIONARY);
92     app_node.SetKey("appid", Value(app.app_id));
93     app_node.SetKey("version", Value(app.version));
94     if (!app.brand_code.empty())
95       app_node.SetKey("brand", Value(app.brand_code));
96     if (!app.install_source.empty())
97       app_node.SetKey("installsource", Value(app.install_source));
98     if (!app.install_location.empty())
99       app_node.SetKey("installedby", Value(app.install_location));
100     // TODO(crbug/1120685): Test that this is never sent to the server if the
101     // machine is not enterprise managed.
102     if (!app.release_channel.empty())
103       app_node.SetKey("release_channel", Value(app.release_channel));
104     if (!app.cohort.empty())
105       app_node.SetKey("cohort", Value(app.cohort));
106     if (!app.cohort_name.empty())
107       app_node.SetKey("cohortname", Value(app.cohort_name));
108     if (!app.cohort_hint.empty())
109       app_node.SetKey("cohorthint", Value(app.cohort_hint));
110     if (app.enabled)
111       app_node.SetKey("enabled", Value(*app.enabled));
112 
113     if (app.disabled_reasons && !app.disabled_reasons->empty()) {
114       std::vector<Value> disabled_nodes;
115       for (const int disabled_reason : *app.disabled_reasons) {
116         Value disabled_node(Value::Type::DICTIONARY);
117         disabled_node.SetKey("reason", Value(disabled_reason));
118         disabled_nodes.push_back(std::move(disabled_node));
119       }
120       app_node.SetKey("disabled", Value(disabled_nodes));
121     }
122 
123     for (const auto& attr : app.installer_attributes)
124       app_node.SetKey(attr.first, Value(attr.second));
125 
126     if (app.update_check) {
127       auto* update_check_node =
128           app_node.SetKey("updatecheck", Value(Value::Type::DICTIONARY));
129       if (app.update_check->is_update_disabled)
130         update_check_node->SetKey("updatedisabled", Value(true));
131     }
132 
133     if (app.ping) {
134       const auto& ping = *app.ping;
135       auto* ping_node = app_node.SetKey("ping", Value(Value::Type::DICTIONARY));
136       if (!ping.ping_freshness.empty())
137         ping_node->SetKey("ping_freshness", Value(ping.ping_freshness));
138 
139       // Output "ad" or "a" only if the this app has been seen 'active'.
140       if (ping.date_last_active) {
141         ping_node->SetKey("ad", Value(*ping.date_last_active));
142       } else if (ping.days_since_last_active_ping) {
143         ping_node->SetKey("a", Value(*ping.days_since_last_active_ping));
144       }
145 
146       // Output "rd" if valid or "r" as a last resort roll call metric.
147       if (ping.date_last_roll_call)
148         ping_node->SetKey("rd", Value(*ping.date_last_roll_call));
149       else
150         ping_node->SetKey("r", Value(ping.days_since_last_roll_call));
151     }
152 
153     if (!app.fingerprint.empty()) {
154       std::vector<Value> package_nodes;
155       Value package(Value::Type::DICTIONARY);
156       package.SetKey("fp", Value(app.fingerprint));
157       package_nodes.push_back(std::move(package));
158       auto* packages_node =
159           app_node.SetKey("packages", Value(Value::Type::DICTIONARY));
160       packages_node->SetKey("package", Value(package_nodes));
161     }
162 
163     if (app.events) {
164       std::vector<Value> event_nodes;
165       for (const auto& event : *app.events) {
166         DCHECK(event.is_dict());
167         DCHECK(!event.DictEmpty());
168         event_nodes.push_back(event.Clone());
169       }
170       app_node.SetKey("event", Value(event_nodes));
171     }
172 
173     app_nodes.push_back(std::move(app_node));
174   }
175 
176   if (!app_nodes.empty())
177     request_node->SetKey("app", Value(std::move(app_nodes)));
178 
179   std::string msg;
180   return base::JSONWriter::WriteWithOptions(
181              root_node, base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION,
182              &msg)
183              ? msg
184              : std::string();
185 }
186 
187 }  // namespace update_client
188