blob: cab9c88d4d410a049f9acd8a3115cec56a8021b0 [file] [log] [blame]
// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "peerd/peer.h"
#include <base/format_macros.h>
#include <base/guid.h>
#include <base/memory/weak_ptr.h>
#include <base/strings/string_util.h>
#include <chromeos/dbus/exported_object_manager.h>
#include "peerd/dbus_constants.h"
#include "peerd/typedefs.h"
using base::Time;
using base::TimeDelta;
using chromeos::Error;
using chromeos::dbus_utils::AsyncEventSequencer;
using chromeos::dbus_utils::DBusInterface;
using chromeos::dbus_utils::DBusObject;
using chromeos::dbus_utils::ExportedObjectManager;
using dbus::ObjectPath;
using peerd::dbus_constants::kPeerFriendlyName;
using peerd::dbus_constants::kPeerInterface;
using peerd::dbus_constants::kPeerLastSeen;
using peerd::dbus_constants::kPeerNote;
using peerd::dbus_constants::kPeerUUID;
using peerd::dbus_constants::kServicePathFragment;
using std::map;
using std::string;
using std::vector;
using std::unique_ptr;
namespace {
const size_t kMaxFriendlyNameLength = 31;
const size_t kMaxNoteLength = 255;
const char kValidFriendlyNameCharacters[] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"_-,.?! ";
const char kValidNoteCharacters[] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789"
"_-,.?! ";
} // namespace
namespace peerd {
namespace errors {
namespace peer {
const char kInvalidUUID[] = "peer.uuid";
const char kInvalidName[] = "peer.name";
const char kInvalidNote[] = "peer.note";
const char kInvalidTime[] = "peer.time";
const char kUnknownService[] = "peer.unknown_service";
} // namespace peer
} // namespace errors
Peer::Peer(const scoped_refptr<dbus::Bus>& bus,
ExportedObjectManager* object_manager,
const ObjectPath& path)
: bus_(bus),
dbus_object_(new DBusObject{object_manager, bus, path}),
service_path_prefix_(path.value() + kServicePathFragment) {
}
bool Peer::RegisterAsync(
chromeos::ErrorPtr* error,
const std::string& uuid,
const std::string& friendly_name,
const std::string& note,
const Time& last_seen,
const CompletionAction& completion_callback) {
if (!base::IsValidGUID(uuid)) {
Error::AddTo(error, kPeerdErrorDomain, errors::peer::kInvalidUUID,
"Invalid UUID for peer.");
return false;
}
uuid_.SetValue(uuid);
if (!SetFriendlyName(error, friendly_name)) { return false; }
if (!SetNote(error, note)) { return false; }
if (!SetLastSeen(error, last_seen)) { return false; }
DBusInterface* itf = dbus_object_->AddOrGetInterface(kPeerInterface);
itf->AddProperty(kPeerUUID, &uuid_);
itf->AddProperty(kPeerFriendlyName, &name_);
itf->AddProperty(kPeerNote, &note_);
itf->AddProperty(kPeerLastSeen, &last_seen_);
dbus_object_->RegisterAsync(completion_callback);
return true;
}
bool Peer::SetFriendlyName(chromeos::ErrorPtr* error,
const string& friendly_name) {
if (!IsValidFriendlyName(error, friendly_name)) { return false; }
name_.SetValue(friendly_name);
return true;
}
bool Peer::SetNote(chromeos::ErrorPtr* error, const string& note) {
if (!IsValidNote(error, note)) { return false; }
note_.SetValue(note);
return true;
}
bool Peer::SetLastSeen(chromeos::ErrorPtr* error, const Time& last_seen) {
if (!IsValidUpdateTime(error, last_seen)) {
return false;
}
uint64_t milliseconds_since_epoch = 0;
CHECK(TimeToMillisecondsSinceEpoch(last_seen, &milliseconds_since_epoch));
last_seen_.SetValue(milliseconds_since_epoch);
return true;
}
std::string Peer::GetUUID() const {
return uuid_.value();
}
std::string Peer::GetFriendlyName() const {
return name_.value();
}
std::string Peer::GetNote() const {
return note_.value();
}
Time Peer::GetLastSeen() const {
return TimeDelta::FromMilliseconds(last_seen_.value()) + Time::UnixEpoch();
}
bool Peer::IsValidFriendlyName(chromeos::ErrorPtr* error,
const std::string& friendly_name) const {
if (friendly_name.length() > kMaxFriendlyNameLength) {
Error::AddToPrintf(error, kPeerdErrorDomain, errors::peer::kInvalidName,
"Bad length for %s: %" PRIuS,
kPeerFriendlyName, friendly_name.length());
return false;
}
if (!base::ContainsOnlyChars(friendly_name, kValidFriendlyNameCharacters)) {
Error::AddToPrintf(error, kPeerdErrorDomain, errors::peer::kInvalidName,
"Invalid characters in %s.", kPeerFriendlyName);
return false;
}
return true;
}
bool Peer::IsValidNote(chromeos::ErrorPtr* error,
const std::string& note) const {
if (note.length() > kMaxNoteLength) {
Error::AddToPrintf(error, kPeerdErrorDomain, errors::peer::kInvalidNote,
"Bad length for %s: %" PRIuS,
kPeerNote, note.length());
return false;
}
if (!base::ContainsOnlyChars(note, kValidNoteCharacters)) {
Error::AddToPrintf(error, kPeerdErrorDomain, errors::peer::kInvalidNote,
"Invalid characters in %s.", kPeerNote);
return false;
}
return true;
}
bool Peer::IsValidUpdateTime(chromeos::ErrorPtr* error,
const base::Time& last_seen) const {
uint64_t milliseconds_since_epoch = 0;
if (!TimeToMillisecondsSinceEpoch(last_seen, &milliseconds_since_epoch)) {
Error::AddTo(error, kPeerdErrorDomain, errors::peer::kInvalidTime,
"Negative time update is invalid.");
return false;
}
if (milliseconds_since_epoch < last_seen_.value()) {
Error::AddTo(error, kPeerdErrorDomain, errors::peer::kInvalidTime,
"Discarding update to last seen time as stale.");
return false;
}
return true;
}
bool Peer::AddService(chromeos::ErrorPtr* error,
const string& service_id,
const vector<ip_addr>& addresses,
const map<string, string>& service_info) {
ObjectPath service_path(service_path_prefix_.value() +
std::to_string(++services_added_));
// TODO(wiley): There is a potential race here. If we remove the service
// too quickly, we race with DBus export completing. We'll
// have to be careful in Service not to assume export has
// finished.
scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
unique_ptr<Service> new_service{new Service{
bus_, dbus_object_->GetObjectManager().get(), service_path}};
const bool success = new_service->RegisterAsync(
error, service_id, addresses, service_info,
sequencer->GetHandler("Failed exporting service.", true));
if (success) {
services_.emplace(service_id, std::move(new_service));
sequencer->OnAllTasksCompletedCall({});
}
return success;
}
bool Peer::RemoveService(chromeos::ErrorPtr* error,
const string& service_id) {
if (services_.erase(service_id) == 0) {
Error::AddTo(error,
kPeerdErrorDomain,
errors::peer::kUnknownService,
"Unknown service id.");
return false;
}
return true;
}
bool Peer::TimeToMillisecondsSinceEpoch(const Time& time, uint64_t* ret) const {
int64_t time_diff = (time - Time::UnixEpoch()).InMilliseconds();
if (time_diff < 0) {
return false;
}
*ret = static_cast<uint64_t>(time_diff);
return true;
}
} // namespace peerd