blob: 66e3dfe9d200d8f972d72f08473a94beffbb8fba [file] [log] [blame]
// Copyright 2011 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
// Utility class to manage a set of experiment labels. Experiment labels
// are a set of key/value pairs used to track an install's "membership" in
// A/B experiment groups issued by the Omaha server. Keys are strings with
// a limited character set (Perl \w, plus a few characters), while values
// consist of a string and an expiration date. Label sets are stored per-app
// and transmitted to the server as part of requests (pings / update checks),
// and a delta is potentially returned from the server with each response.
//
// Experiment labels are serialized with key/value separated by an equals, and
// value/expiration by a pipe symbol; the expiration is represented in RFC822
// format. For example: "test_key=test_value|Fri, 14 Aug 2015 16:13:03 GMT".
// If an app participates in multiple experiments simultaneously, labels are
// concatenated with semicolon delimiters.
#ifndef OMAHA_COMMON_EXPERIMENT_LABELS_H_
#define OMAHA_COMMON_EXPERIMENT_LABELS_H_
#include <atlstr.h>
#include <map>
#include <utility>
#include "base/basictypes.h"
#include "omaha/base/time.h"
namespace omaha {
class ExperimentLabels {
public:
ExperimentLabels();
~ExperimentLabels();
// Returns the number of labels in the store.
int NumLabels() const;
// Returns true if a label with this key exists in the store.
bool ContainsKey(const CString& key) const;
// Returns the contents of the Nth label in the experiment set. Output
// parameters are allowed to be NULL. If index is out of range, asserts
// on a debug build.
//
// NOTE: This method is O(n), and indexes are not stable with respect to
// insertion order; it should not be called from any hot path. Unit tests
// and diagnostic tools should use this to enumerate keys.
void GetLabelByIndex(int index,
CString* key,
CString* value,
time64* expiration) const;
// If a label with this key exists, returns true and writes the value and
// expiration to the output parameters. Output parameters are allowed
// to be NULL. If no such key exists, returns false and leaves the outputs
// unchanged.
bool FindLabelByKey(const CString& key,
CString* value,
time64* expiration) const;
// Attempts to set a label in the store, either adding a new one or updating
// the value/expiration of an existing one. Returns true on success.
bool SetLabel(const CString& key, const CString& value, time64 expiration);
// Clears a label in the store. Returns true if a label with that key exists
// (it is removed); returns false if no label with that key exists.
bool ClearLabel(const CString& key);
// Removes any labels from the store whose expiration dates have passed.
void ExpireLabels();
// Removes all labels from the store.
void ClearAllLabels();
// Concatenates and emits all labels in the store in an XML-friendly format.
CString Serialize() const;
// Replaces the current contents of the store with new labels from an
// XML-friendly string. On success, returns true; the store reflects the
// input exactly, with all prior contents are lost. On failure, returns
// false and the store's contents are unchanged.
bool Deserialize(const CString& label_list);
// Applies the contents of a label list as a delta against the current
// contents of the store. On success, returns true, and:
// * Any expired labels in the list are deleted from the store.
// * Unexpired labels in the list will be added to the store. If any keys
// already exist in the store, they are overwritten with the input.
// * Labels that are in the store but not in the input are left unmodified.
// On failure, returns false, and the store's contents are unchanged.
// This function's behavior is not modified by SetPreserveExpiredLabels()!
bool DeserializeAndApplyDelta(const CString& label_list);
// Modifies the store's handling of labels with expiration dates in the past.
// By default, SetLabel() will fail to add expired labels, Deserialize() will
// silently discard them, and Serialize() will not emit them. Following a
// call to SetPreserveExpiredLabels(true), these functions will accept and/or
// include expired labels.
void SetPreserveExpiredLabels(bool preserve);
// Serializes a set of experiment labels to application data in the Registry.
HRESULT WriteToRegistry(bool is_machine, const CString& app_id);
// Deserializes a set of experiment labels from the Registry.
HRESULT ReadFromRegistry(bool is_machine, const CString& app_id);
// Returns true if the supplied string is a valid experiment label set.
static bool IsStringValidLabelSet(const CString& label_list);
private:
typedef std::map<CString, std::pair<CString, time64> > LabelMap;
// Returns true if all characters are in the range [a-zA-Z0-9_\-+,: ].
// (Perl \w, plus the punctuation necessary for RFC822 dates.)
static bool IsLabelContentValid(const CString& str);
// Given an input string of the form "key=value|rfc822_date", converts it
// into separate key, value, and integer expiration dates.
static bool SplitCombinedLabel(const CString& combined,
CString* key,
CString* value,
time64* expiration);
// Splits an input string and applies it against an existing internal map.
static bool DoDeserialize(LabelMap* map,
const CString& label_list,
bool accept_expired);
LabelMap labels_;
bool preserve_expired_;
DISALLOW_COPY_AND_ASSIGN(ExperimentLabels);
};
} // namespace omaha
#endif // OMAHA_COMMON_EXPERIMENT_LABELS_H_