Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(430)

Side by Side Diff: common/experiment_labels.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « common/experiment_labels.h ('k') | common/experiment_labels_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2011 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 // ========================================================================
15
16 // Utility class to manage a set of experiment labels. Experiment labels
17 // are a set of key/value pairs used to track an install's "membership" in
18 // A/B experiment groups issued by the Omaha server. Keys are strings with
19 // a limited character set (Perl \w, plus a few characters), while values
20 // consist of a string and an expiration date. Label sets are stored per-app
21 // and transmitted to the server as part of requests (pings / update checks),
22 // and a delta is potentially returned from the server with each response.
23 //
24 // Experiment labels are serialized with key/value separated by an equals, and
25 // value/expiration by a pipe symbol; the expiration is represented in RFC822
26 // format. For example: "test_key=test_value|Fri, 14 Aug 2015 16:13:03 GMT".
27 // If an app participates in multiple experiments simultaneously, labels are
28 // concatenated with semicolon delimiters.
29
30 #include "omaha/common/experiment_labels.h"
31
32 #include "omaha/base/debug.h"
33 #include "omaha/base/safe_format.h"
34 #include "omaha/common/app_registry_utils.h"
35
36 namespace omaha {
37
38 ExperimentLabels::ExperimentLabels() : labels_(), preserve_expired_(false) {}
39
40 ExperimentLabels::~ExperimentLabels() {}
41
42 int ExperimentLabels::NumLabels() const {
43 return labels_.size();
44 }
45
46 bool ExperimentLabels::ContainsKey(const CString& key) const {
47 ASSERT1(!key.IsEmpty());
48 return labels_.find(key) != labels_.end();
49 }
50
51 void ExperimentLabels::GetLabelByIndex(int index, CString* key, CString* value,
52 time64* expiration) const {
53 ASSERT1(index >= 0);
54 ASSERT1(static_cast<LabelMap::size_type>(index) < labels_.size());
55
56 LabelMap::const_iterator cit = labels_.begin();
57 std::advance(cit, static_cast<LabelMap::size_type>(index));
58 if (key) {
59 *key = cit->first;
60 }
61 if (value) {
62 *value = cit->second.first;
63 }
64 if (expiration) {
65 *expiration = cit->second.second;
66 }
67 }
68
69 bool ExperimentLabels::FindLabelByKey(const CString& key, CString* value,
70 time64* expiration) const {
71 ASSERT1(!key.IsEmpty());
72 LabelMap::const_iterator cit = labels_.find(key);
73 if (labels_.end() == cit) {
74 return false;
75 }
76
77 if (value) {
78 *value = cit->second.first;
79 }
80 if (expiration) {
81 *expiration = cit->second.second;
82 }
83 return true;
84 }
85
86 bool ExperimentLabels::SetLabel(const CString& key, const CString& value,
87 time64 expiration) {
88 if (!IsLabelContentValid(key) || !IsLabelContentValid(value)) {
89 return false;
90 }
91 if (expiration < GetCurrent100NSTime() && !preserve_expired_) {
92 return false;
93 }
94 labels_[key] = std::make_pair(value, expiration);
95 return true;
96 }
97
98 bool ExperimentLabels::ClearLabel(const CString& key) {
99 LabelMap::iterator it = labels_.find(key);
100 if (labels_.end() == it) {
101 return false;
102 }
103 labels_.erase(it);
104 return true;
105 }
106
107 void ExperimentLabels::ExpireLabels() {
108 time64 current_time = GetCurrent100NSTime();
109 for (LabelMap::iterator it = labels_.begin(); it != labels_.end(); ++it) {
110 if (it->second.second < current_time) {
111 it = labels_.erase(it);
112 if (it == labels_.end()) {
113 break;
114 }
115 }
116 }
117 }
118
119 void ExperimentLabels::ClearAllLabels() {
120 labels_.clear();
121 }
122
123 CString ExperimentLabels::Serialize() const {
124 CString serialized;
125 time64 current_time = GetCurrent100NSTime();
126 for (LabelMap::const_iterator cit = labels_.begin();
127 cit != labels_.end();
128 ++cit) {
129 if (preserve_expired_ || cit->second.second >= current_time) {
130 if (!serialized.IsEmpty()) {
131 serialized.Append(L";");
132 }
133 FILETIME ft = {};
134 Time64ToFileTime(cit->second.second, &ft);
135 SafeCStringAppendFormat(&serialized, L"%s=%s|%s",
136 cit->first,
137 cit->second.first,
138 ConvertTimeToGMTString(&ft));
139 }
140 }
141 return serialized;
142 }
143
144 bool ExperimentLabels::Deserialize(const CString& label_list) {
145 LabelMap new_labels;
146 if (DoDeserialize(&new_labels, label_list, preserve_expired_)) {
147 std::swap(labels_, new_labels);
148 return true;
149 }
150 return false;
151 }
152
153 bool ExperimentLabels::DeserializeAndApplyDelta(const CString& label_list) {
154 LabelMap merged_labels = labels_;
155 if (DoDeserialize(&merged_labels, label_list, false)) {
156 std::swap(labels_, merged_labels);
157 return true;
158 }
159 return false;
160 }
161
162 void ExperimentLabels::SetPreserveExpiredLabels(bool preserve) {
163 preserve_expired_ = preserve;
164 }
165
166 HRESULT ExperimentLabels::WriteToRegistry(bool is_machine,
167 const CString& app_id) {
168 return app_registry_utils::SetExperimentLabels(is_machine, app_id,
169 Serialize());
170 }
171
172 HRESULT ExperimentLabels::ReadFromRegistry(bool is_machine,
173 const CString& app_id) {
174 CString label_list;
175 HRESULT hr = app_registry_utils::GetExperimentLabels(is_machine, app_id,
176 &label_list);
177 if (FAILED(hr)) {
178 return hr;
179 }
180 return Deserialize(label_list) ? S_OK : E_FAIL;
181 }
182
183 bool ExperimentLabels::IsStringValidLabelSet(const CString& label_list) {
184 ExperimentLabels labels;
185 return labels.Deserialize(label_list);
186 }
187
188 bool ExperimentLabels::IsLabelContentValid(const CString& str) {
189 if (str.IsEmpty()) {
190 return false;
191 }
192 for (int i = 0; i < str.GetLength(); ++i) {
193 wchar_t ch = str[i];
194 if (!((ch >= L'+' && ch <= L'-') ||
195 (ch >= L'0' && ch <= L':') ||
196 (ch >= L'A' && ch <= L'Z') ||
197 (ch >= L'a' && ch <= L'z') ||
198 (ch == L'_') || (ch == L' '))) {
199 return false;
200 }
201 }
202 return true;
203 }
204
205 bool ExperimentLabels::SplitCombinedLabel(const CString& combined, CString* key,
206 CString* value, time64* expiration) {
207 ASSERT1(!combined.IsEmpty());
208 ASSERT1(key);
209 ASSERT1(value);
210 ASSERT1(expiration);
211
212 int value_offset = combined.Find(L'=');
213 if (value_offset <= 0 || value_offset == combined.GetLength()) {
214 return false;
215 }
216 *key = combined.Left(value_offset);
217 if (!IsLabelContentValid(*key)) {
218 return false;
219 }
220 ++value_offset;
221
222 int expiration_offset = combined.Find(L'|', value_offset);
223 if (expiration_offset <= value_offset ||
224 expiration_offset == combined.GetLength()) {
225 return false;
226 }
227 *value = combined.Mid(value_offset, expiration_offset - value_offset);
228 if (!IsLabelContentValid(*value)) {
229 return false;
230 }
231 ++expiration_offset;
232
233 CString expiration_string = combined.Mid(expiration_offset);
234 if (!IsLabelContentValid(expiration_string)) {
235 return false;
236 }
237 SYSTEMTIME system_time = {};
238 if (!RFC822DateToSystemTime(expiration_string, &system_time, false)) {
239 return false;
240 }
241 *expiration = SystemTimeToTime64(&system_time);
242
243 return true;
244 }
245
246 bool ExperimentLabels::DoDeserialize(LabelMap* map, const CString& label_list,
247 bool accept_expired) {
248 ASSERT1(map);
249
250 if (label_list.IsEmpty()) {
251 return true;
252 }
253
254 time64 current_time = GetCurrent100NSTime();
255
256 for (int offset = 0;;) {
257 CString combined_label = label_list.Tokenize(L";", offset);
258 if (combined_label.IsEmpty()) {
259 if (offset < 0) {
260 break; // Natural end-of-string reached.
261 }
262 return false;
263 }
264
265 CString key;
266 CString value;
267 time64 expiration = 0;
268 if (!SplitCombinedLabel(combined_label, &key, &value, &expiration)) {
269 return false;
270 }
271
272 // If the label is well-formatted but expired, we accept the input, but
273 // do not add it to the map and do not emit an error. If there is already
274 // a label in the map with that key, delete it.
275 if (accept_expired || expiration > current_time) {
276 (*map)[key] = std::make_pair(value, expiration);
277 } else {
278 map->erase(key);
279 }
280 }
281
282 return true;
283 }
284
285 } // namespace omaha
286
OLDNEW
« no previous file with comments | « common/experiment_labels.h ('k') | common/experiment_labels_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698