// Copyright (c) 2011 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.
//
// Utilities for the SafeBrowsing code.

#ifndef CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_UTIL_H_
#define CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_UTIL_H_
#pragma once

#include <cstring>
#include <deque>
#include <string>
#include <vector>

#include "base/basictypes.h"
#include "chrome/browser/safe_browsing/chunk_range.h"

class GURL;

class SBEntry;

// A truncated hash's type.
typedef int32 SBPrefix;

// Container for holding a chunk URL and the MAC of the contents of the URL.
struct ChunkUrl {
  std::string url;
  std::string mac;
  std::string list_name;
};

// A full hash.
union SBFullHash {
  char full_hash[32];
  SBPrefix prefix;
};

inline bool operator==(const SBFullHash& lhash, const SBFullHash& rhash) {
  return memcmp(lhash.full_hash, rhash.full_hash, sizeof(SBFullHash)) == 0;
}

inline bool operator<(const SBFullHash& lhash, const SBFullHash& rhash) {
  return memcmp(lhash.full_hash, rhash.full_hash, sizeof(SBFullHash)) < 0;
}

// Container for information about a specific host in an add/sub chunk.
struct SBChunkHost {
  SBPrefix host;
  SBEntry* entry;
};

// Container for an add/sub chunk.
struct SBChunk {
  SBChunk();
  ~SBChunk();

  int chunk_number;
  int list_id;
  bool is_add;
  std::deque<SBChunkHost> hosts;
};

// Container for a set of chunks.  Interim wrapper to replace use of
// |std::deque<SBChunk>| with something having safer memory semantics.
// management.
// TODO(shess): |SBEntry| is currently a very roundabout way to hold
// things pending storage.  It could be replaced with the structures
// used in SafeBrowsingStore, then lots of bridging code could
// dissappear.
class SBChunkList {
 public:
  SBChunkList();
  ~SBChunkList();

  // Implement that subset of the |std::deque<>| interface which
  // callers expect.
  bool empty() const { return chunks_.empty(); }
  size_t size() { return chunks_.size(); }

  void push_back(const SBChunk& chunk) { chunks_.push_back(chunk); }
  SBChunk& back() { return chunks_.back(); }
  SBChunk& front() { return chunks_.front(); }
  const SBChunk& front() const { return chunks_.front(); }

  typedef std::vector<SBChunk>::const_iterator const_iterator;
  const_iterator begin() const { return chunks_.begin(); }
  const_iterator end() const { return chunks_.end(); }

  typedef std::vector<SBChunk>::iterator iterator;
  iterator begin() { return chunks_.begin(); }
  iterator end() { return chunks_.end(); }

  SBChunk& operator[](size_t n) { return chunks_[n]; }
  const SBChunk& operator[](size_t n) const { return chunks_[n]; }

  // Calls |SBEvent::Destroy()| before clearing |chunks_|.
  void clear();

 private:
  std::vector<SBChunk> chunks_;

  DISALLOW_COPY_AND_ASSIGN(SBChunkList);
};

// Used when we get a gethash response.
struct SBFullHashResult {
  SBFullHash hash;
  std::string list_name;
  int add_chunk_id;
};

// Contains information about a list in the database.
struct SBListChunkRanges {
  explicit SBListChunkRanges(const std::string& n);

  std::string name;  // The list name.
  std::string adds;  // The ranges for add chunks.
  std::string subs;  // The ranges for sub chunks.
};

// Container for deleting chunks from the database.
struct SBChunkDelete {
  SBChunkDelete();
  ~SBChunkDelete();

  std::string list_name;
  bool is_sub_del;
  std::vector<ChunkRange> chunk_del;
};


// SBEntry ---------------------------------------------------------------------

// Holds information about the prefixes for a hostkey.  prefixes can either be
// 4 bytes (truncated hash) or 32 bytes (full hash).
// For adds:
//   [list id ][chunk id][prefix count (0..n)][prefix1][prefix2]
// For subs:
//   [list id ][chunk id (only used if prefix count is 0][prefix count (0..n)]
//       [add chunk][prefix][add chunk][prefix]
class SBEntry {
 public:
  enum Type {
    ADD_PREFIX,     // 4 byte add entry.
    SUB_PREFIX,     // 4 byte sub entry.
    ADD_FULL_HASH,  // 32 byte add entry.
    SUB_FULL_HASH,  // 32 byte sub entry.
  };

  // Creates a SBEntry with the necessary size for the given number of prefixes.
  // Caller ownes the object and needs to free it by calling Destroy.
  static SBEntry* Create(Type type, int prefix_count);

  // Frees the entry's memory.
  void Destroy();

  void set_list_id(int list_id) { data_.list_id = list_id; }
  int list_id() const { return data_.list_id; }
  void set_chunk_id(int chunk_id) { data_.chunk_id = chunk_id; }
  int chunk_id() const { return data_.chunk_id; }
  int prefix_count() const { return data_.prefix_count; }

  // Returns true if this is a prefix as opposed to a full hash.
  bool IsPrefix() const {
    return type() == ADD_PREFIX || type() == SUB_PREFIX;
  }

  // Returns true if this is an add entry.
  bool IsAdd() const {
    return type() == ADD_PREFIX || type() == ADD_FULL_HASH;
  }

  // Returns true if this is a sub entry.
  bool IsSub() const {
    return type() == SUB_PREFIX || type() == SUB_FULL_HASH;
  }

  // Helper to return the size of the prefixes.
  int HashLen() const {
    return IsPrefix() ? sizeof(SBPrefix) : sizeof(SBFullHash);
  }

  // For add entries, returns the add chunk id.  For sub entries, returns the
  // add_chunk id for the prefix at the given index.
  int ChunkIdAtPrefix(int index) const;

  // Used for sub chunks to set the chunk id at a given index.
  void SetChunkIdAtPrefix(int index, int chunk_id);

  // Return the prefix/full hash at the given index.  Caller is expected to
  // call the right function based on the hash length.
  const SBPrefix& PrefixAt(int index) const;
  const SBFullHash& FullHashAt(int index) const;

  // Return the prefix/full hash at the given index.  Caller is expected to
  // call the right function based on the hash length.
  void SetPrefixAt(int index, const SBPrefix& prefix);
  void SetFullHashAt(int index, const SBFullHash& full_hash);

 private:
  // Container for a sub prefix.
  struct SBSubPrefix {
    int add_chunk;
    SBPrefix prefix;
  };

  // Container for a sub full hash.
  struct SBSubFullHash {
    int add_chunk;
    SBFullHash prefix;
  };

  // Keep the fixed data together in one struct so that we can get its size
  // easily.  If any of this is modified, the database will have to be cleared.
  struct Data {
    int list_id;
    // For adds, this is the add chunk number.
    // For subs: if prefix_count is 0 then this is the add chunk that this sub
    //     refers to.  Otherwise it's ignored, and the add_chunk in sub_prefixes
    //     or sub_full_hashes is used for each corresponding prefix.
    int chunk_id;
    Type type;
    int prefix_count;
  };

  SBEntry();
  ~SBEntry();

  // Helper to return the size of each prefix entry (i.e. for subs this
  // includes an add chunk id).
  static int PrefixSize(Type type);

  // Helper to return how much memory a given Entry would require.
  static int Size(Type type, int prefix_count);

  // Returns how many bytes this entry is.
  int Size() const;

  Type type() const { return data_.type; }

  void set_prefix_count(int count) { data_.prefix_count = count; }
  void set_type(Type type) { data_.type = type; }

  // The prefixes union must follow the fixed data so that they're contiguous
  // in memory.
  Data data_;
  union {
    SBPrefix add_prefixes_[1];
    SBSubPrefix sub_prefixes_[1];
    SBFullHash add_full_hashes_[1];
    SBSubFullHash sub_full_hashes_[1];
  };
};


// Utility functions -----------------------------------------------------------

namespace safe_browsing_util {

// SafeBrowsing list names.
extern const char kMalwareList[];
extern const char kPhishingList[];
// Binary Download list names.
extern const char kBinUrlList[];
extern const char kBinHashList[];
// SafeBrowsing client-side detection whitelist list name.
extern const char kCsdWhiteList[];

enum ListType {
  INVALID = -1,
  MALWARE = 0,
  PHISH = 1,
  BINURL = 2,
  BINHASH = 3,
  CSDWHITELIST = 4,
};

// Maps a list name to ListType.
int GetListId(const std::string& name);
// Maps a ListId to list name. Return false if fails.
bool GetListName(int list_id, std::string* list);


// Canonicalizes url as per Google Safe Browsing Specification.
// See section 6.1 in
// http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec.
void CanonicalizeUrl(const GURL& url, std::string* canonicalized_hostname,
                     std::string* canonicalized_path,
                     std::string* canonicalized_query);

// Given a URL, returns all the hosts we need to check.  They are returned
// in order of size (i.e. b.c is first, then a.b.c).
void GenerateHostsToCheck(const GURL& url, std::vector<std::string>* hosts);

// Given a URL, returns all the paths we need to check.
void GeneratePathsToCheck(const GURL& url, std::vector<std::string>* paths);

int GetHashIndex(const SBFullHash& hash,
                 const std::vector<SBFullHashResult>& full_hashes);

// Given a URL, compare all the possible host + path full hashes to the set of
// provided full hashes.  Returns the index of the match if one is found, or -1
// otherwise.
int GetUrlHashIndex(const GURL& url,
                    const std::vector<SBFullHashResult>& full_hashes);

bool IsPhishingList(const std::string& list_name);
bool IsMalwareList(const std::string& list_name);
bool IsBadbinurlList(const std::string& list_name);
bool IsBadbinhashList(const std::string& list_name);

// Returns 'true' if 'mac' can be verified using 'key' and 'data'.
bool VerifyMAC(const std::string& key,
               const std::string& mac,
               const char* data,
               int data_length);

GURL GeneratePhishingReportUrl(const std::string& report_page,
                               const std::string& url_to_report);

void StringToSBFullHash(const std::string& hash_in, SBFullHash* hash_out);
std::string SBFullHashToString(const SBFullHash& hash_out);
}  // namespace safe_browsing_util

#endif  // CHROME_BROWSER_SAFE_BROWSING_SAFE_BROWSING_UTIL_H_