// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef REMOTING_PROTOCOL_PAIRING_REGISTRY_H_
#define REMOTING_PROTOCOL_PAIRING_REGISTRY_H_

#include <map>
#include <queue>
#include <string>
#include <vector>

#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"

namespace base {
class DictionaryValue;
class ListValue;
class SingleThreadTaskRunner;
}  // namespace base

namespace tracked_objects {
class Location;
}  // namespace tracked_objects

namespace remoting {
namespace protocol {

// PairingRegistry holds information about paired clients to support
// PIN-less authentication. For each paired client, the registry holds
// the following information:
//   * The name of the client. This is supplied by the client and is not
//     guaranteed to be unique.
//   * The unique id of the client. This is generated on-demand by this
//     class and sent in plain-text by the client during authentication.
//   * The shared secret for the client. This is generated on-demand by this
//     class and used in the SPAKE2 exchange to mutually verify identity.
class PairingRegistry : public base::RefCountedThreadSafe<PairingRegistry> {
 public:
  struct Pairing {
    Pairing();
    Pairing(const base::Time& created_time,
            const std::string& client_name,
            const std::string& client_id,
            const std::string& shared_secret);
    ~Pairing();

    static Pairing Create(const std::string& client_name);
    static Pairing CreateFromValue(const base::DictionaryValue& pairing);

    scoped_ptr<base::DictionaryValue> ToValue() const;

    bool operator==(const Pairing& other) const;

    bool is_valid() const;

    base::Time created_time() const { return created_time_; }
    std::string client_id() const { return client_id_; }
    std::string client_name() const { return client_name_; }
    std::string shared_secret() const { return shared_secret_; }

   private:
    base::Time created_time_;
    std::string client_name_;
    std::string client_id_;
    std::string shared_secret_;
  };

  // Mapping from client id to pairing information.
  typedef std::map<std::string, Pairing> PairedClients;

  // Delegate callbacks.
  typedef base::Callback<void(bool success)> DoneCallback;
  typedef base::Callback<void(scoped_ptr<base::ListValue> pairings)>
      GetAllPairingsCallback;
  typedef base::Callback<void(Pairing pairing)> GetPairingCallback;

  static const char kCreatedTimeKey[];
  static const char kClientIdKey[];
  static const char kClientNameKey[];
  static const char kSharedSecretKey[];

  // Interface representing the persistent storage back-end.
  class Delegate {
   public:
    virtual ~Delegate() {}

    // Retrieves all JSON-encoded pairings from persistent storage.
    virtual scoped_ptr<base::ListValue> LoadAll() = 0;

    // Deletes all pairings in persistent storage.
    virtual bool DeleteAll() = 0;

    // Retrieves the pairing identified by |client_id|.
    virtual Pairing Load(const std::string& client_id) = 0;

    // Saves |pairing| to persistent storage.
    virtual bool Save(const Pairing& pairing) = 0;

    // Deletes the pairing identified by |client_id|.
    virtual bool Delete(const std::string& client_id) = 0;
  };

  PairingRegistry(
      scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner,
      scoped_ptr<Delegate> delegate);

  // Creates a pairing for a new client and saves it to disk.
  //
  // TODO(jamiewalch): Plumb the Save callback into the RequestPairing flow
  // so that the client isn't sent the pairing information until it has been
  // saved.
  Pairing CreatePairing(const std::string& client_name);

  // Gets the pairing for the specified client id. See the corresponding
  // Delegate method for details. If none is found, the callback is invoked
  // with an invalid Pairing.
  void GetPairing(const std::string& client_id,
                  const GetPairingCallback& callback);

  // Gets all pairings with the shared secrets removed as a base::ListValue.
  void GetAllPairings(const GetAllPairingsCallback& callback);

  // Delete a pairing, identified by its client ID. |callback| is called with
  // the result of saving the new config, which occurs even if the client ID
  // did not match any pairing.
  void DeletePairing(const std::string& client_id,
                     const DoneCallback& callback);

  // Clear all pairings from the registry.
  void ClearAllPairings(const DoneCallback& callback);

 protected:
  friend class base::RefCountedThreadSafe<PairingRegistry>;
  virtual ~PairingRegistry();

  // Lets the tests override task posting to make all callbacks synchronous.
  virtual void PostTask(
      const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
      const tracked_objects::Location& from_here,
      const base::Closure& task);

 private:
  FRIEND_TEST_ALL_PREFIXES(PairingRegistryTest, AddPairing);
  friend class NegotiatingAuthenticatorTest;

  // Helper method for unit tests.
  void AddPairing(const Pairing& pairing);

  // Blocking helper methods used to call the delegate.
  void DoLoadAll(
      const protocol::PairingRegistry::GetAllPairingsCallback& callback);
  void DoDeleteAll(
      const protocol::PairingRegistry::DoneCallback& callback);
  void DoLoad(
      const std::string& client_id,
      const protocol::PairingRegistry::GetPairingCallback& callback);
  void DoSave(
      const protocol::PairingRegistry::Pairing& pairing,
      const protocol::PairingRegistry::DoneCallback& callback);
  void DoDelete(
      const std::string& client_id,
      const protocol::PairingRegistry::DoneCallback& callback);

  // "Trampoline" callbacks that schedule the next pending request and then
  // invoke the original caller-supplied callback.
  void InvokeDoneCallbackAndScheduleNext(
      const DoneCallback& callback, bool success);
  void InvokeGetPairingCallbackAndScheduleNext(
      const GetPairingCallback& callback, Pairing pairing);
  void InvokeGetAllPairingsCallbackAndScheduleNext(
      const GetAllPairingsCallback& callback,
      scoped_ptr<base::ListValue> pairings);

  // Sanitize |pairings| by parsing each entry and removing the secret from it.
  void SanitizePairings(const GetAllPairingsCallback& callback,
                        scoped_ptr<base::ListValue> pairings);

  // Queue management methods.
  void ServiceOrQueueRequest(const base::Closure& request);
  void ServiceNextRequest();

  // Task runner on which all public methods of this class should be called.
  scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;

  // Task runner used to run blocking calls to the delegate. A single thread
  // task runner is used to guarantee that one one method of the delegate is
  // called at a time.
  scoped_refptr<base::SingleThreadTaskRunner> delegate_task_runner_;

  scoped_ptr<Delegate> delegate_;

  std::queue<base::Closure> pending_requests_;

  DISALLOW_COPY_AND_ASSIGN(PairingRegistry);
};

}  // namespace protocol
}  // namespace remoting

#endif  // REMOTING_PROTOCOL_PAIRING_REGISTRY_H_