<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "auth/Auth.h"
#include "common/ceph_argparse.h"
#include "common/common_init.h"
#include "common/ConfUtils.h"
#include "common/version.h"
#include "common/config.h"
#include "include/intarith.h"
#include "include/str_list.h"
#include "msg/msg_types.h"

#include <deque>
#include <stdarg.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <vector>

/*
 * Ceph argument parsing library
 *
 * We probably should eventually replace this with something standard like popt.
 * Until we do that, though, this file is the place for argv parsing
 * stuff to live.
 */

#undef dout
#undef pdout
#undef derr
#undef generic_dout
#undef dendl

static bool cmd_is_char(const char *cmd)
{
  return ((cmd[0] == '-') &&
    cmd[1] && !cmd[2]);
}

bool ceph_argparse_cmd_equals(const char *cmd, const char *opt, char char_opt,
			      unsigned int *val_pos)
{
  unsigned int i;
  unsigned int len = strlen(opt);

  *val_pos = 0;

  if (!*cmd)
    return false;

  if (char_opt && cmd_is_char(cmd))
    return (char_opt == cmd[1]);

  if ((cmd[0] != '-') || (cmd[1] != '-'))
    return false;

  for (i=0; i<len; i++) {
    if ((opt[i] == '_') || (opt[i] == '-')) {
      switch (cmd[i+2]) {
      case '-':
      case '_':
        continue;
      default:
        break;
      }
    }

    if (cmd[i+2] != opt[i])
      return false;
  }

  if (cmd[i+2] == '=')
    *val_pos = i+3;
  else if (cmd[i+2])
    return false;

  return true;
}

bool ceph_argparse_cmdline_val(void *field, int type, const char *val)
{
  switch (type) {
  case OPT_BOOL:
    if (strcasecmp(val, "false") == 0)
      *(bool *)field = false;
    else if (strcasecmp(val, "true") == 0)
      *(bool *)field = true;
    else
      *(bool *)field = (bool)atoi(val);
    break;
  case OPT_INT:
    *(int *)field = atoi(val);
    break;
  case OPT_LONGLONG:
    *(long long *)field = atoll(val);
    break;
  case OPT_STR:
    if (val)
      *(char **)field = strdup(val);
    else
      *(char **)field = NULL;
    break;
  case OPT_FLOAT:
    *(float *)field = atof(val);
    break;
  case OPT_DOUBLE:
    *(double *)field = strtod(val, NULL);
    break;
  case OPT_ADDR:
    ((entity_addr_t *)field)->parse(val);
    break;
  default:
    return false;
  }

  return true;
}

void env_to_vec(std::vector<const char*>& args)
{
  char *p = getenv("CEPH_ARGS");
  if (!p) return;

  static char buf[1000];
  int len = MIN(strlen(p), sizeof(buf)-1);  // bleh.
  memcpy(buf, p, len);
  buf[len] = 0;
  //cout << "CEPH_ARGS='" << p << ";" << endl;

  p = buf;
  while (*p && p < buf + len) {
    char *e = p;
    while (*e && *e != ' ')
      e++;
    *e = 0;
    args.push_back(p);
    //cout << "arg " << p << std::endl;
    p = e+1;
  }
}

void env_to_deq(std::deque<const char*>& args)
{
  char *p = getenv("CEPH_ARGS");
  if (!p) return;

  static char buf[1000];
  int len = MIN(strlen(p), sizeof(buf)-1);  // bleh.
  memcpy(buf, p, len);
  buf[len] = 0;

  p = buf;
  while (*p && p < buf + len) {
    char *e = p;
    while (*e && *e != ' ')
      e++;
    *e = 0;
    args.push_back(p);
    p = e+1;
  }
}

void argv_to_vec(int argc, const char **argv,
                 std::vector<const char*>& args)
{
  for (int i=1; i<argc; i++)
    args.push_back(argv[i]);
}

void argv_to_deq(int argc, const char **argv,
                 std::deque<const char*>& args)
{
  for (int i=1; i<argc; i++)
    args.push_back(argv[i]);
}

void vec_to_argv(std::vector<const char*>& args,
                 int& argc, const char **&argv)
{
  const char *myname = "asdf";
  if (argc && argv)
    myname = argv[0];
  argv = (const char**)malloc(sizeof(char*) * argc);
  argc = 1;
  argv[0] = myname;

  for (unsigned i=0; i<args.size(); i++)
    argv[argc++] = args[i];
}

bool parse_ip_port_vec(const char *s, vector<entity_addr_t>& vec)
{
  const char *p = s;
  const char *end = p + strlen(p);
  while (p < end) {
    entity_addr_t a;
    //cout << " parse at '" << p << "'" << std::endl;
    if (!a.parse(p, &p)) {
      //dout(0) << " failed to parse address '" << p << "'" << dendl;
      return false;
    }
    //cout << " got " << a << ", rest is '" << p << "'" << std::endl;
    vec.push_back(a);
    while (*p == ',' || *p == ' ')
      p++;
  }
  return true;
}

// The defaults for CephInitParameters
CephInitParameters::CephInitParameters(uint32_t module_type_, const char *conf_file_)
  : module_type(module_type_),
    conf_file(conf_file_)
{
  const char *c = getenv("CEPH_CONF");
  if (c)
    conf_file = c;
  name.set(module_type, "admin");
}

std::list<std::string> CephInitParameters::
get_conf_files() const
{
  std::list<std::string> ret;
  get_str_list(conf_file, ret);
  return ret;
}

static void dashes_to_underscores(const char *input, char *output)
{
  char c = 0;
  char *o = output;
  const char *i = input;
  // first two characters are copied as-is
  *o = *i++;
  if (*o++ == '\0')
    return;
  *o = *i++;
  if (*o++ == '\0')
    return;
  for (; ((c = *i)); ++i) {
    if (c == '=') {
      strcpy(o, i);
      return;
    }
    if (c == '-')
      *o++ = '_';
    else
      *o++ = c;
  }
  *o++ = '\0';
}

bool ceph_argparse_flag(std::vector<const char*> &args,
	std::vector<const char*>::iterator &i, ...)
{
  const char *first = *i;
  char tmp[strlen(first)+1];
  dashes_to_underscores(first, tmp);
  first = tmp;
  const char *a;
  va_list ap;

  va_start(ap, i);
  while (1) {
    a = va_arg(ap, char*);
    if (a == NULL)
      return false;
    char a2[strlen(a)+1];
    dashes_to_underscores(a, a2);
    if (strcmp(a2, first) == 0) {
      i = args.erase(i);
      return true;
    }
  }
}

bool ceph_argparse_witharg(std::vector<const char*> &args,
	std::vector<const char*>::iterator &i, std::string *ret, ...)
{
  const char *first = *i;
  char tmp[strlen(first)+1];
  dashes_to_underscores(first, tmp);
  first = tmp;
  const char *a;
  va_list ap;
  int strlen_a;

  // does this argument match any of the possibilities?
  va_start(ap, ret);
  while (1) {
    a = va_arg(ap, char*);
    if (a == NULL)
      return false;
    strlen_a = strlen(a);
    if (strncmp(a, first, strlen(a)) == 0) {
      if (first[strlen_a] == '=') {
	*ret = first + strlen_a + 1;
	i = args.erase(i);
	return true;
      }
      else if (first[strlen_a] == '\0') {
	// find second part (or not)
	if (i+1 == args.end()) {
	  cerr << "Option " << *i << " requires an argument." << std::endl;
	  _exit(1);
	}
	i = args.erase(i);
	*ret = *i;
	i = args.erase(i);
	return true;
      }
    }
  }
}

CephInitParameters ceph_argparse_early_args
	  (std::vector<const char*>& args, uint32_t module_type, int flags)
{
  const char *conf = (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE) ?
    "" : CEPH_CONF_FILE_DEFAULT;
  CephInitParameters iparams(module_type, conf);
  std::string val;
  for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
    if (strcmp(*i, "--") == 0)
      break;
    else if (ceph_argparse_flag(args, i, "--version", "-v", (char*)NULL)) {
      cout << pretty_version_to_str() << std::endl;
      _exit(0);
    }
    else if (ceph_argparse_witharg(args, i, &val, "--conf", "-c", (char*)NULL)) {
      iparams.conf_file = val;
    }
    else if ((module_type != CEPH_ENTITY_TYPE_CLIENT) &&
	     (ceph_argparse_witharg(args, i, &val, "-i", (char*)NULL))) {
      iparams.name.set_id(val);
    }
    else if (ceph_argparse_witharg(args, i, &val, "--id", (char*)NULL)) {
      iparams.name.set_id(val);
    }
    else if (ceph_argparse_witharg(args, i, &val, "--name", "-n", (char*)NULL)) {
      if (!iparams.name.from_str(val)) {
	cerr << "You must pass a string of the form TYPE.ID to "
	  << "the --name option. Valid types are: "
	  << EntityName::get_valid_types_as_str() << std::endl;
	_exit(1);
      }
    }
    else {
      // ignore
      ++i;
    }
  }
  return iparams;
}

static void generic_usage(bool is_server)
{
  cout << "\
--conf/-c        Read configuration from the given configuration file\n\
-d               Run in foreground, log to stderr.\n\
-f               Run in foreground, log to usual location.\n\
--id/-i          set ID portion of my name\n\
--name/-n        set name (TYPE.ID)\n\
--version        show version and quit\n\
" << std::endl;

  if (is_server) {
    cout << "   --debug_ms N\n";
    cout << "        set message debug level (e.g. 1)\n";
  }
}

void generic_server_usage()
{
  generic_usage(true);
  exit(1);
}
void generic_client_usage()
{
  generic_usage(false);
  exit(1);
}
<commit_msg>ceph_argparse_witharg: fix dashes in args<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "auth/Auth.h"
#include "common/ceph_argparse.h"
#include "common/common_init.h"
#include "common/ConfUtils.h"
#include "common/version.h"
#include "common/config.h"
#include "include/intarith.h"
#include "include/str_list.h"
#include "msg/msg_types.h"

#include <deque>
#include <stdarg.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <vector>

/*
 * Ceph argument parsing library
 *
 * We probably should eventually replace this with something standard like popt.
 * Until we do that, though, this file is the place for argv parsing
 * stuff to live.
 */

#undef dout
#undef pdout
#undef derr
#undef generic_dout
#undef dendl

static bool cmd_is_char(const char *cmd)
{
  return ((cmd[0] == '-') &&
    cmd[1] && !cmd[2]);
}

bool ceph_argparse_cmd_equals(const char *cmd, const char *opt, char char_opt,
			      unsigned int *val_pos)
{
  unsigned int i;
  unsigned int len = strlen(opt);

  *val_pos = 0;

  if (!*cmd)
    return false;

  if (char_opt && cmd_is_char(cmd))
    return (char_opt == cmd[1]);

  if ((cmd[0] != '-') || (cmd[1] != '-'))
    return false;

  for (i=0; i<len; i++) {
    if ((opt[i] == '_') || (opt[i] == '-')) {
      switch (cmd[i+2]) {
      case '-':
      case '_':
        continue;
      default:
        break;
      }
    }

    if (cmd[i+2] != opt[i])
      return false;
  }

  if (cmd[i+2] == '=')
    *val_pos = i+3;
  else if (cmd[i+2])
    return false;

  return true;
}

bool ceph_argparse_cmdline_val(void *field, int type, const char *val)
{
  switch (type) {
  case OPT_BOOL:
    if (strcasecmp(val, "false") == 0)
      *(bool *)field = false;
    else if (strcasecmp(val, "true") == 0)
      *(bool *)field = true;
    else
      *(bool *)field = (bool)atoi(val);
    break;
  case OPT_INT:
    *(int *)field = atoi(val);
    break;
  case OPT_LONGLONG:
    *(long long *)field = atoll(val);
    break;
  case OPT_STR:
    if (val)
      *(char **)field = strdup(val);
    else
      *(char **)field = NULL;
    break;
  case OPT_FLOAT:
    *(float *)field = atof(val);
    break;
  case OPT_DOUBLE:
    *(double *)field = strtod(val, NULL);
    break;
  case OPT_ADDR:
    ((entity_addr_t *)field)->parse(val);
    break;
  default:
    return false;
  }

  return true;
}

void env_to_vec(std::vector<const char*>& args)
{
  char *p = getenv("CEPH_ARGS");
  if (!p) return;

  static char buf[1000];
  int len = MIN(strlen(p), sizeof(buf)-1);  // bleh.
  memcpy(buf, p, len);
  buf[len] = 0;
  //cout << "CEPH_ARGS='" << p << ";" << endl;

  p = buf;
  while (*p && p < buf + len) {
    char *e = p;
    while (*e && *e != ' ')
      e++;
    *e = 0;
    args.push_back(p);
    //cout << "arg " << p << std::endl;
    p = e+1;
  }
}

void env_to_deq(std::deque<const char*>& args)
{
  char *p = getenv("CEPH_ARGS");
  if (!p) return;

  static char buf[1000];
  int len = MIN(strlen(p), sizeof(buf)-1);  // bleh.
  memcpy(buf, p, len);
  buf[len] = 0;

  p = buf;
  while (*p && p < buf + len) {
    char *e = p;
    while (*e && *e != ' ')
      e++;
    *e = 0;
    args.push_back(p);
    p = e+1;
  }
}

void argv_to_vec(int argc, const char **argv,
                 std::vector<const char*>& args)
{
  for (int i=1; i<argc; i++)
    args.push_back(argv[i]);
}

void argv_to_deq(int argc, const char **argv,
                 std::deque<const char*>& args)
{
  for (int i=1; i<argc; i++)
    args.push_back(argv[i]);
}

void vec_to_argv(std::vector<const char*>& args,
                 int& argc, const char **&argv)
{
  const char *myname = "asdf";
  if (argc && argv)
    myname = argv[0];
  argv = (const char**)malloc(sizeof(char*) * argc);
  argc = 1;
  argv[0] = myname;

  for (unsigned i=0; i<args.size(); i++)
    argv[argc++] = args[i];
}

bool parse_ip_port_vec(const char *s, vector<entity_addr_t>& vec)
{
  const char *p = s;
  const char *end = p + strlen(p);
  while (p < end) {
    entity_addr_t a;
    //cout << " parse at '" << p << "'" << std::endl;
    if (!a.parse(p, &p)) {
      //dout(0) << " failed to parse address '" << p << "'" << dendl;
      return false;
    }
    //cout << " got " << a << ", rest is '" << p << "'" << std::endl;
    vec.push_back(a);
    while (*p == ',' || *p == ' ')
      p++;
  }
  return true;
}

// The defaults for CephInitParameters
CephInitParameters::CephInitParameters(uint32_t module_type_, const char *conf_file_)
  : module_type(module_type_),
    conf_file(conf_file_)
{
  const char *c = getenv("CEPH_CONF");
  if (c)
    conf_file = c;
  name.set(module_type, "admin");
}

std::list<std::string> CephInitParameters::
get_conf_files() const
{
  std::list<std::string> ret;
  get_str_list(conf_file, ret);
  return ret;
}

static void dashes_to_underscores(const char *input, char *output)
{
  char c = 0;
  char *o = output;
  const char *i = input;
  // first two characters are copied as-is
  *o = *i++;
  if (*o++ == '\0')
    return;
  *o = *i++;
  if (*o++ == '\0')
    return;
  for (; ((c = *i)); ++i) {
    if (c == '=') {
      strcpy(o, i);
      return;
    }
    if (c == '-')
      *o++ = '_';
    else
      *o++ = c;
  }
  *o++ = '\0';
}

bool ceph_argparse_flag(std::vector<const char*> &args,
	std::vector<const char*>::iterator &i, ...)
{
  const char *first = *i;
  char tmp[strlen(first)+1];
  dashes_to_underscores(first, tmp);
  first = tmp;
  const char *a;
  va_list ap;

  va_start(ap, i);
  while (1) {
    a = va_arg(ap, char*);
    if (a == NULL)
      return false;
    char a2[strlen(a)+1];
    dashes_to_underscores(a, a2);
    if (strcmp(a2, first) == 0) {
      i = args.erase(i);
      return true;
    }
  }
}

bool ceph_argparse_witharg(std::vector<const char*> &args,
	std::vector<const char*>::iterator &i, std::string *ret, ...)
{
  const char *first = *i;
  char tmp[strlen(first)+1];
  dashes_to_underscores(first, tmp);
  first = tmp;
  const char *a;
  va_list ap;
  int strlen_a;

  // does this argument match any of the possibilities?
  va_start(ap, ret);
  while (1) {
    a = va_arg(ap, char*);
    if (a == NULL)
      return false;
    strlen_a = strlen(a);
    char a2[strlen_a+1];
    dashes_to_underscores(a, a2);
    if (strncmp(a2, first, strlen(a2)) == 0) {
      if (first[strlen_a] == '=') {
	*ret = first + strlen_a + 1;
	i = args.erase(i);
	return true;
      }
      else if (first[strlen_a] == '\0') {
	// find second part (or not)
	if (i+1 == args.end()) {
	  cerr << "Option " << *i << " requires an argument." << std::endl;
	  _exit(1);
	}
	i = args.erase(i);
	*ret = *i;
	i = args.erase(i);
	return true;
      }
    }
  }
}

CephInitParameters ceph_argparse_early_args
	  (std::vector<const char*>& args, uint32_t module_type, int flags)
{
  const char *conf = (flags & CINIT_FLAG_NO_DEFAULT_CONFIG_FILE) ?
    "" : CEPH_CONF_FILE_DEFAULT;
  CephInitParameters iparams(module_type, conf);
  std::string val;
  for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
    if (strcmp(*i, "--") == 0)
      break;
    else if (ceph_argparse_flag(args, i, "--version", "-v", (char*)NULL)) {
      cout << pretty_version_to_str() << std::endl;
      _exit(0);
    }
    else if (ceph_argparse_witharg(args, i, &val, "--conf", "-c", (char*)NULL)) {
      iparams.conf_file = val;
    }
    else if ((module_type != CEPH_ENTITY_TYPE_CLIENT) &&
	     (ceph_argparse_witharg(args, i, &val, "-i", (char*)NULL))) {
      iparams.name.set_id(val);
    }
    else if (ceph_argparse_witharg(args, i, &val, "--id", (char*)NULL)) {
      iparams.name.set_id(val);
    }
    else if (ceph_argparse_witharg(args, i, &val, "--name", "-n", (char*)NULL)) {
      if (!iparams.name.from_str(val)) {
	cerr << "You must pass a string of the form TYPE.ID to "
	  << "the --name option. Valid types are: "
	  << EntityName::get_valid_types_as_str() << std::endl;
	_exit(1);
      }
    }
    else {
      // ignore
      ++i;
    }
  }
  return iparams;
}

static void generic_usage(bool is_server)
{
  cout << "\
--conf/-c        Read configuration from the given configuration file\n\
-d               Run in foreground, log to stderr.\n\
-f               Run in foreground, log to usual location.\n\
--id/-i          set ID portion of my name\n\
--name/-n        set name (TYPE.ID)\n\
--version        show version and quit\n\
" << std::endl;

  if (is_server) {
    cout << "   --debug_ms N\n";
    cout << "        set message debug level (e.g. 1)\n";
  }
}

void generic_server_usage()
{
  generic_usage(true);
  exit(1);
}
void generic_client_usage()
{
  generic_usage(false);
  exit(1);
}
<|endoftext|>


<commit_before>#include "addresstablemodel.h"
#include "guiutil.h"
#include "walletmodel.h"

#include "wallet/wallet.h"
#include "base58.h"
#include "util.h"

#include <QFont>
#include <QColor>

const QString AddressTableModel::Send = "S";
const QString AddressTableModel::Receive = "R";

struct AddressTableEntry
{
    enum Type {
        Sending,
        Receiving
    };

    Type type;
    QString label;
    QString address;

    AddressTableEntry() {}
    AddressTableEntry(Type type, const QString &label, const QString &address):
        type(type), label(label), address(address) {}
};

struct AddressTableEntryLessThan
{
    bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
    {
        return a.address < b.address;
    }
    bool operator()(const AddressTableEntry &a, const QString &b) const
    {
        return a.address < b;
    }
    bool operator()(const QString &a, const AddressTableEntry &b) const
    {
        return a < b.address;
    }
};

// Private implementation
class AddressTablePriv
{
public:
    CWallet *wallet;
    QList<AddressTableEntry> cachedAddressTable;
    AddressTableModel *parent;

    AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
        wallet(wallet), parent(parent) {}

    void refreshAddressTable()
    {
        cachedAddressTable.clear();
        {
            LOCK(wallet->cs_wallet);
            for (auto const &item : wallet->mapAddressBook)
            {
                const CBitcoinAddress& address = item.first;
                const std::string& strName = item.second;
                isminetype fMine = IsMine(*wallet, address.Get());
                cachedAddressTable.append(AddressTableEntry((fMine != ISMINE_NO) ? AddressTableEntry::Receiving : AddressTableEntry::Sending,
                                  QString::fromStdString(strName),
                                  QString::fromStdString(address.ToString())));
            }
        }
        // std::lower_bound() and std::upper_bound() require our cachedAddressTable list to be sorted in asc order
        std::sort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
    }

    void updateEntry(const QString &address, const QString &label, bool isMine, int status)
    {
        // Find address / label in model
        QList<AddressTableEntry>::iterator lower = std::lower_bound(
            cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
        QList<AddressTableEntry>::iterator upper = std::upper_bound(
            cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
        int lowerIndex = (lower - cachedAddressTable.begin());
        int upperIndex = (upper - cachedAddressTable.begin());
        bool inModel = (lower != upper);
        AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending;

        switch(status)
        {
        case CT_NEW:
            if(inModel)
            {
                LogPrintf("Warning: AddressTablePriv::updateEntry: Got CT_NEW, but entry is already in model");
                break;
            }
            parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
            cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
            parent->endInsertRows();
            break;
        case CT_UPDATED:
            if(!inModel)
            {
                LogPrintf("Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model");
                break;
            }
            lower->type = newEntryType;
            lower->label = label;
            parent->emitDataChanged(lowerIndex);
            break;
        case CT_DELETED:
            if(!inModel)
            {
                LogPrintf("Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry is not in model");
                break;
            }
            parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
            cachedAddressTable.erase(lower, upper);
            parent->endRemoveRows();
            break;
        }
    }

    int size()
    {
        return cachedAddressTable.size();
    }

    AddressTableEntry *index(int idx)
    {
        if(idx >= 0 && idx < cachedAddressTable.size())
        {
            return &cachedAddressTable[idx];
        }
        else
        {
            return nullptr;
        }
    }
};

AddressTableModel::AddressTableModel(CWallet* wallet, WalletModel* parent)
               : QAbstractTableModel(parent)
               , walletModel(parent)
               , wallet(wallet)
               , priv(nullptr)
{
    columns << tr("Label") << tr("Address");
    priv = new AddressTablePriv(wallet, this);
    priv->refreshAddressTable();
}

AddressTableModel::~AddressTableModel()
{
    delete priv;
}

int AddressTableModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid()) {
        return 0;
    }
    return priv->size();
}

int AddressTableModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid()) {
        return 0;
    }
    return columns.length();
}

QVariant AddressTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());

    if(role == Qt::DisplayRole || role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Label:
            if(rec->label.isEmpty() && role == Qt::DisplayRole)
            {
                return tr("(no label)");
            }
            else
            {
                return rec->label;
            }
        case Address:
            return rec->address;
        }
    }
    else if (role == Qt::FontRole)
    {
        QFont font;
        if(index.column() == Address)
        {
            font = GUIUtil::bitcoinAddressFont();
        }
        return font;
    }
    else if (role == TypeRole)
    {
        switch(rec->type)
        {
        case AddressTableEntry::Sending:
            return Send;
        case AddressTableEntry::Receiving:
            return Receive;
        default: break;
        }
    }
    return QVariant();
}

bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(!index.isValid())
        return false;
    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());

    editStatus = OK;

    auto address_count = [this](const QVariant &value) {
        LOCK(wallet->cs_wallet);

        return wallet->mapAddressBook.count(CBitcoinAddress(value.toString().toStdString()).Get());
    };

    if (role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Label:
            // Do nothing, if old label == new label
            if(rec->label == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }
            wallet->SetAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get(), value.toString().toStdString());
            break;
        case Address:
            // Do nothing, if old address == new address
            if(CBitcoinAddress(rec->address.toStdString()) == CBitcoinAddress(value.toString().toStdString()))
            {
                editStatus = NO_CHANGES;
                return false;
            }
            // Refuse to set invalid address, set error status and return false
            else if(!walletModel->validateAddress(value.toString()))
            {
                editStatus = INVALID_ADDRESS;
                return false;
            }
            // Check for duplicate addresses to prevent accidental deletion of addresses, if you try
            // to paste an existing address over another address (with a different label)
            else if(address_count(value))
            {
                editStatus = DUPLICATE_ADDRESS;
                return false;
            }
            // Double-check that we're not overwriting a receiving address
            else if(rec->type == AddressTableEntry::Sending)
            {
                {
                    LOCK(wallet->cs_wallet);
                    // Remove old entry
                    wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get());
                    // Add new entry with new address
                    wallet->SetAddressBookName(CBitcoinAddress(value.toString().toStdString()).Get(), rec->label.toStdString());
                }
            }
            break;
        }
        return true;
    }
    return false;
}

QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return columns[section];
        }
    }
    return QVariant();
}

Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return Qt::NoItemFlags;
    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());

    Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    // Can edit address and label for sending addresses,
    // and only label for receiving addresses.
    if(rec->type == AddressTableEntry::Sending ||
      (rec->type == AddressTableEntry::Receiving && index.column()==Label))
    {
        retval |= Qt::ItemIsEditable;
    }
    return retval;
}

QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    AddressTableEntry *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status)
{
    // Update address book model from Bitcoin core
    priv->updateEntry(address, label, isMine, status);
}

QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
{
    std::string strLabel = label.toStdString();
    std::string strAddress = address.toStdString();

    editStatus = OK;

    if(type == Send)
    {
        if(!walletModel->validateAddress(address))
        {
            editStatus = INVALID_ADDRESS;
            return QString();
        }
        // Check for duplicate addresses
        {
            LOCK(wallet->cs_wallet);
            if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
            {
                editStatus = DUPLICATE_ADDRESS;
                return QString();
            }
        }
    }
    else if(type == Receive)
    {
        // Generate a new address to associate with given label
        WalletModel::UnlockContext ctx(walletModel->requestUnlock());
        if(!ctx.isValid())
        {
            // Unlock wallet failed or was cancelled
            editStatus = WALLET_UNLOCK_FAILURE;
            return QString();
        }
        CPubKey newKey;
        if(!wallet->GetKeyFromPool(newKey, true))
        {
            editStatus = KEY_GENERATION_FAILURE;
            return QString();
        }
        strAddress = CBitcoinAddress(newKey.GetID()).ToString();
    }
    else
    {
        return QString();
    }

    // Add entry
    {
        LOCK(wallet->cs_wallet);
        wallet->SetAddressBookName(CBitcoinAddress(strAddress).Get(), strLabel);
    }
    return QString::fromStdString(strAddress);
}

bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
{
    Q_UNUSED(parent);
    AddressTableEntry *rec = priv->index(row);
    if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
    {
        // Can only remove one row at a time, and cannot remove rows not in model.
        // Also refuse to remove receiving addresses.
        return false;
    }
    {
        LOCK(wallet->cs_wallet);
        wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get());
    }
    return true;
}

/* Look up label for address in address book, if not found return empty string.
 */
QString AddressTableModel::labelForAddress(const QString &address) const
{
    {
        LOCK(wallet->cs_wallet);
        CBitcoinAddress address_parsed(address.toStdString());
        std::map<CTxDestination, std::string>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
        if (mi != wallet->mapAddressBook.end())
        {
            return QString::fromStdString(mi->second);
        }
    }
    return QString();
}

int AddressTableModel::lookupAddress(const QString &address) const
{
    QModelIndexList lst = match(index(0, Address, QModelIndex()),
                                Qt::EditRole, address, 1, Qt::MatchExactly);
    if(lst.isEmpty())
    {
        return -1;
    }
    else
    {
        return lst.at(0).row();
    }
}

void AddressTableModel::emitDataChanged(int idx)
{
    emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}
<commit_msg>qt, refactor: Use enum type as switch argument in AddressTableModel<commit_after>#include "addresstablemodel.h"
#include "guiutil.h"
#include "walletmodel.h"

#include "wallet/wallet.h"
#include "base58.h"
#include "util.h"

#include <QFont>
#include <QColor>

const QString AddressTableModel::Send = "S";
const QString AddressTableModel::Receive = "R";

struct AddressTableEntry
{
    enum Type {
        Sending,
        Receiving
    };

    Type type;
    QString label;
    QString address;

    AddressTableEntry() {}
    AddressTableEntry(Type type, const QString &label, const QString &address):
        type(type), label(label), address(address) {}
};

struct AddressTableEntryLessThan
{
    bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
    {
        return a.address < b.address;
    }
    bool operator()(const AddressTableEntry &a, const QString &b) const
    {
        return a.address < b;
    }
    bool operator()(const QString &a, const AddressTableEntry &b) const
    {
        return a < b.address;
    }
};

// Private implementation
class AddressTablePriv
{
public:
    CWallet *wallet;
    QList<AddressTableEntry> cachedAddressTable;
    AddressTableModel *parent;

    AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
        wallet(wallet), parent(parent) {}

    void refreshAddressTable()
    {
        cachedAddressTable.clear();
        {
            LOCK(wallet->cs_wallet);
            for (auto const &item : wallet->mapAddressBook)
            {
                const CBitcoinAddress& address = item.first;
                const std::string& strName = item.second;
                isminetype fMine = IsMine(*wallet, address.Get());
                cachedAddressTable.append(AddressTableEntry((fMine != ISMINE_NO) ? AddressTableEntry::Receiving : AddressTableEntry::Sending,
                                  QString::fromStdString(strName),
                                  QString::fromStdString(address.ToString())));
            }
        }
        // std::lower_bound() and std::upper_bound() require our cachedAddressTable list to be sorted in asc order
        std::sort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
    }

    void updateEntry(const QString &address, const QString &label, bool isMine, int status)
    {
        // Find address / label in model
        QList<AddressTableEntry>::iterator lower = std::lower_bound(
            cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
        QList<AddressTableEntry>::iterator upper = std::upper_bound(
            cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
        int lowerIndex = (lower - cachedAddressTable.begin());
        int upperIndex = (upper - cachedAddressTable.begin());
        bool inModel = (lower != upper);
        AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending;

        switch(status)
        {
        case CT_NEW:
            if(inModel)
            {
                LogPrintf("Warning: AddressTablePriv::updateEntry: Got CT_NEW, but entry is already in model");
                break;
            }
            parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
            cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
            parent->endInsertRows();
            break;
        case CT_UPDATED:
            if(!inModel)
            {
                LogPrintf("Warning: AddressTablePriv::updateEntry: Got CT_UPDATED, but entry is not in model");
                break;
            }
            lower->type = newEntryType;
            lower->label = label;
            parent->emitDataChanged(lowerIndex);
            break;
        case CT_DELETED:
            if(!inModel)
            {
                LogPrintf("Warning: AddressTablePriv::updateEntry: Got CT_DELETED, but entry is not in model");
                break;
            }
            parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
            cachedAddressTable.erase(lower, upper);
            parent->endRemoveRows();
            break;
        }
    }

    int size()
    {
        return cachedAddressTable.size();
    }

    AddressTableEntry *index(int idx)
    {
        if(idx >= 0 && idx < cachedAddressTable.size())
        {
            return &cachedAddressTable[idx];
        }
        else
        {
            return nullptr;
        }
    }
};

AddressTableModel::AddressTableModel(CWallet* wallet, WalletModel* parent)
               : QAbstractTableModel(parent)
               , walletModel(parent)
               , wallet(wallet)
               , priv(nullptr)
{
    columns << tr("Label") << tr("Address");
    priv = new AddressTablePriv(wallet, this);
    priv->refreshAddressTable();
}

AddressTableModel::~AddressTableModel()
{
    delete priv;
}

int AddressTableModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid()) {
        return 0;
    }
    return priv->size();
}

int AddressTableModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid()) {
        return 0;
    }
    return columns.length();
}

QVariant AddressTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());

    const auto column = static_cast<ColumnIndex>(index.column());
    if (role == Qt::DisplayRole || role == Qt::EditRole) {
        switch (column) {
        case Label:
            if (rec->label.isEmpty() && role == Qt::DisplayRole) {
                return tr("(no label)");
            } else {
                return rec->label;
            }
        case Address:
            return rec->address;
        } // no default case, so the compiler can warn about missing cases
        assert(false);
    } else if (role == Qt::FontRole) {
        QFont font;
        if(index.column() == Address)
        {
            font = GUIUtil::bitcoinAddressFont();
        }
        return font;
    } else if (role == TypeRole) {
        switch(rec->type)
        {
        case AddressTableEntry::Sending:
            return Send;
        case AddressTableEntry::Receiving:
            return Receive;
        } // no default case, so the compiler can warn about missing cases
        assert(false);
    }
    return QVariant();
}

bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(!index.isValid())
        return false;
    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());

    editStatus = OK;

    auto address_count = [this](const QVariant &value) {
        LOCK(wallet->cs_wallet);

        return wallet->mapAddressBook.count(CBitcoinAddress(value.toString().toStdString()).Get());
    };

    if (role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Label:
            // Do nothing, if old label == new label
            if(rec->label == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }
            wallet->SetAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get(), value.toString().toStdString());
            break;
        case Address:
            // Do nothing, if old address == new address
            if(CBitcoinAddress(rec->address.toStdString()) == CBitcoinAddress(value.toString().toStdString()))
            {
                editStatus = NO_CHANGES;
                return false;
            }
            // Refuse to set invalid address, set error status and return false
            else if(!walletModel->validateAddress(value.toString()))
            {
                editStatus = INVALID_ADDRESS;
                return false;
            }
            // Check for duplicate addresses to prevent accidental deletion of addresses, if you try
            // to paste an existing address over another address (with a different label)
            else if(address_count(value))
            {
                editStatus = DUPLICATE_ADDRESS;
                return false;
            }
            // Double-check that we're not overwriting a receiving address
            else if(rec->type == AddressTableEntry::Sending)
            {
                {
                    LOCK(wallet->cs_wallet);
                    // Remove old entry
                    wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get());
                    // Add new entry with new address
                    wallet->SetAddressBookName(CBitcoinAddress(value.toString().toStdString()).Get(), rec->label.toStdString());
                }
            }
            break;
        }
        return true;
    }
    return false;
}

QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            return columns[section];
        }
    }
    return QVariant();
}

Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return Qt::NoItemFlags;
    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());

    Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    // Can edit address and label for sending addresses,
    // and only label for receiving addresses.
    if(rec->type == AddressTableEntry::Sending ||
      (rec->type == AddressTableEntry::Receiving && index.column()==Label))
    {
        retval |= Qt::ItemIsEditable;
    }
    return retval;
}

QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    AddressTableEntry *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status)
{
    // Update address book model from Bitcoin core
    priv->updateEntry(address, label, isMine, status);
}

QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
{
    std::string strLabel = label.toStdString();
    std::string strAddress = address.toStdString();

    editStatus = OK;

    if(type == Send)
    {
        if(!walletModel->validateAddress(address))
        {
            editStatus = INVALID_ADDRESS;
            return QString();
        }
        // Check for duplicate addresses
        {
            LOCK(wallet->cs_wallet);
            if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
            {
                editStatus = DUPLICATE_ADDRESS;
                return QString();
            }
        }
    }
    else if(type == Receive)
    {
        // Generate a new address to associate with given label
        WalletModel::UnlockContext ctx(walletModel->requestUnlock());
        if(!ctx.isValid())
        {
            // Unlock wallet failed or was cancelled
            editStatus = WALLET_UNLOCK_FAILURE;
            return QString();
        }
        CPubKey newKey;
        if(!wallet->GetKeyFromPool(newKey, true))
        {
            editStatus = KEY_GENERATION_FAILURE;
            return QString();
        }
        strAddress = CBitcoinAddress(newKey.GetID()).ToString();
    }
    else
    {
        return QString();
    }

    // Add entry
    {
        LOCK(wallet->cs_wallet);
        wallet->SetAddressBookName(CBitcoinAddress(strAddress).Get(), strLabel);
    }
    return QString::fromStdString(strAddress);
}

bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
{
    Q_UNUSED(parent);
    AddressTableEntry *rec = priv->index(row);
    if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
    {
        // Can only remove one row at a time, and cannot remove rows not in model.
        // Also refuse to remove receiving addresses.
        return false;
    }
    {
        LOCK(wallet->cs_wallet);
        wallet->DelAddressBookName(CBitcoinAddress(rec->address.toStdString()).Get());
    }
    return true;
}

/* Look up label for address in address book, if not found return empty string.
 */
QString AddressTableModel::labelForAddress(const QString &address) const
{
    {
        LOCK(wallet->cs_wallet);
        CBitcoinAddress address_parsed(address.toStdString());
        std::map<CTxDestination, std::string>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
        if (mi != wallet->mapAddressBook.end())
        {
            return QString::fromStdString(mi->second);
        }
    }
    return QString();
}

int AddressTableModel::lookupAddress(const QString &address) const
{
    QModelIndexList lst = match(index(0, Address, QModelIndex()),
                                Qt::EditRole, address, 1, Qt::MatchExactly);
    if(lst.isEmpty())
    {
        return -1;
    }
    else
    {
        return lst.at(0).row();
    }
}

void AddressTableModel::emitDataChanged(int idx)
{
    emit dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}
<|endoftext|>


<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <cassert>
#include <iostream>
#include <map>
#include <memory>
#include <numeric>
#include <ostream>
#include <vector>

#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/filesystem.hpp>

#include "moana_loader.h"
#include "model.h"
#include "obj_grammar.h"
#include "sg.h"

namespace visionaray
{

//-------------------------------------------------------------------------------------------------
// Extract base path from json file
// As a heuristic, traverse the path from left to right until we find a folder json/
// The parent folder of the latter is the base path
//

inline boost::filesystem::path get_base_path(std::string const& filename)
{
    boost::filesystem::path p(filename);

    while (p.stem().string() != "json" || p.empty())
    {
        p = p.parent_path();
    }

    return p.parent_path();
}

//-------------------------------------------------------------------------------------------------
// Map obj indices to unsigned base-0 indices
//

template <typename Int>
inline Int remap_index(Int idx, Int size)
{
    return idx < 0 ? size + idx : idx - 1;
}

static void store_faces(
        std::shared_ptr<sg::triangle_mesh>& tm,
        vertex_vector const&                vertices,
        normal_vector const&                normals,
        face_vector const&                  faces
        )
{

    auto vertices_size = static_cast<int>(vertices.size());
    size_t last = 2;
    auto i1 = remap_index(faces[0].vertex_index, vertices_size);

    // simply construct new vertices for each obj face we encounter
    // ..too hard to keep track of v/vn combinations..
    while (last != faces.size())
    {
        // triangle indices
        auto i2 = remap_index(faces[last - 1].vertex_index, vertices_size);
        auto i3 = remap_index(faces[last].vertex_index, vertices_size);

        // no texture coordinates
        // ...

        // normal indices
        auto normals_size = static_cast<int>(normals.size());
        auto ni1 = remap_index(*faces[0].normal_index, normals_size);
        auto ni2 = remap_index(*faces[last - 1].normal_index, normals_size);
        auto ni3 = remap_index(*faces[last].normal_index, normals_size);

        tm->vertices.push_back({
            vertices[i1],
            normals[ni1],
            vec2(0.0f), // no tex coords
            vec4(0.0f)  // base color undefined
            });

        tm->vertices.push_back({
            vertices[i2],
            normals[ni2],
            vec2(0.0f), // no tex coords
            vec4(0.0f)  // base color undefined
            });

        tm->vertices.push_back({
            vertices[i3],
            normals[ni3],
            vec2(0.0f), // no tex coords
            vec4(0.0f)  // base color undefined
            });

        ++last;
    }
}

static void load_obj(
        std::string const& filename,
        std::map<std::string, std::shared_ptr<sg::disney_material>> const& materials,
        std::vector<std::shared_ptr<sg::node>>& objs
        )
{
    namespace qi = boost::spirit::qi;

    using boost::string_ref;

    boost::iostreams::mapped_file_source file(filename);

    obj_grammar grammar;

    string_ref text(file.data(), file.size());
    auto it = text.cbegin();

    // containers for parsing

    vertex_vector    vertices;
    tex_coord_vector tex_coords;
    normal_vector    normals;
    face_vector      faces;

    string_ref comment;
    string_ref mtl_file;
    string_ref mtl_name;

    std::shared_ptr<sg::surface_properties> surf = nullptr;
    std::shared_ptr<sg::triangle_mesh> tm = nullptr;

    while (it != text.cend())
    {
        faces.clear();
        if ( qi::phrase_parse(it, text.cend(), grammar.r_usemtl, qi::blank, mtl_name) )
        {
            objs.push_back(std::make_shared<sg::surface_properties>());
            objs.back()->add_child(std::make_shared<sg::triangle_mesh>());

            surf = std::dynamic_pointer_cast<sg::surface_properties>(objs.back());
            tm = std::dynamic_pointer_cast<sg::triangle_mesh>(surf->children().back());

            std::string usemtl = mtl_name.to_string();
            auto it = materials.find(usemtl);
            if (it != materials.end())
            {
                surf->material() = it->second;
            }
        }
        else if ( qi::phrase_parse(it, text.cend(), grammar.r_vertices, qi::blank, vertices) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), grammar.r_normals, qi::blank, normals) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), grammar.r_face, qi::blank, faces) )
        {
            store_faces(tm, vertices, normals, faces);
        }
        else if ( qi::phrase_parse(it, text.cend(), grammar.r_unhandled, qi::blank) )
        {
        }
        else
        {
            ++it;
        }
    }

    for (auto obj : objs)
    {
        auto surf = std::dynamic_pointer_cast<sg::surface_properties>(obj);

        for (auto c : surf->children())
        {
            auto tm = std::dynamic_pointer_cast<sg::triangle_mesh>(c);

            // Fill with 0,1,2,3,4,..
            tm->indices.resize(tm->vertices.size());
            std::iota(tm->indices.begin(), tm->indices.end(), 0);
        }
    }
}

static void load_instanced_primitive_json_file(
        boost::filesystem::path const& island_base_path,
        std::string const& filename,
        std::shared_ptr<sg::node> root,
        std::map<std::string, std::shared_ptr<sg::disney_material>>& materials
        )
{
    std::cout << "Load instanced primitive json file: " << (island_base_path / filename).string() << '\n';
    std::ifstream stream((island_base_path / filename).string());
    if (stream.fail())
    {
        std::cerr << "Cannot open " << filename << '\n';
        return;
    }

    boost::property_tree::ptree pt;
    boost::property_tree::read_json(stream, pt);

    for (auto& v : pt)
    {
        // Instance geometry
        std::string obj_file = v.first.data();
        std::vector<std::shared_ptr<sg::node>> objs;
        load_obj((island_base_path / obj_file).string(), materials, objs);

        // Instance transforms
        for (auto& t : v.second)
        {
            auto transform = std::make_shared<sg::transform>();
            int i = 0;
            for (auto& item : t.second)
            {
                transform->matrix().data()[i++] = item.second.get_value<float>();
                assert(i <= 16);
            }

            for (auto obj : objs)
            {
                transform->add_child(obj);
            }
            root->add_child(transform);
        }
    }
}

void load_material_file(
        std::string const& filename,
        std::map<std::string, std::shared_ptr<sg::disney_material>>& materials
        )
{
    std::cout << "Load material file: " << filename << '\n';
    std::ifstream stream(filename);
    if (stream.fail())
    {
        std::cerr << "Cannot open " << filename << '\n';
        return;
    }

    boost::property_tree::ptree pt;
    boost::property_tree::read_json(stream, pt);

    for (auto& v : pt)
    {
        std::string material_name = v.first.data();

        std::shared_ptr<sg::disney_material> mat = std::make_shared<sg::disney_material>();

        auto& bc = v.second.get_child("baseColor");

        int i = 0;
        for (auto& item : bc)
        {
            mat->base_color[i++] = item.second.get_value<float>();
            assert(i <= 4);
        }

        materials.insert({ material_name, mat });
    }
}

void load_moana(std::string const& filename, model& mod)
{
    std::cout << "Load moana file: " << filename << '\n';
    std::ifstream stream(filename);
    if (stream.fail())
    {
        std::cerr << "Cannot open " << filename << '\n';
        return;
    }

    boost::filesystem::path island_base_path = get_base_path(filename);

    if (island_base_path.empty())
    {
        std::cerr << "Cannot extract Moana Island Scene base path from " << filename << '\n';
        return;
    }

    boost::property_tree::ptree pt;
    boost::property_tree::read_json(stream, pt);

    auto root = std::make_shared<sg::node>();

    // matFile
    std::string mat_file = pt.get<std::string>("matFile");
    std::map<std::string, std::shared_ptr<sg::disney_material>> materials;
    load_material_file((island_base_path / mat_file).string(), materials);

    auto base_transform = std::make_shared<sg::transform>();
    root->add_child(base_transform);

    // transformMatrix
    int i = 0;
    for (auto& item : pt.get_child("transformMatrix"))
    {
        base_transform->matrix().data()[i++] = item.second.get_value<float>();
        assert(i <= 16);
    }

    // geomObjFile
    try
    {
        std::string geom_obj_file = pt.get<std::string>("geomObjFile");
        std::vector<std::shared_ptr<sg::node>> objs;
        load_obj((island_base_path / geom_obj_file).string(), materials, objs);
        for (auto obj : objs)
        {
            base_transform->add_child(obj);
        }
    }
    catch (...)
    {
        // No geomObjFile
    }

    // instancedPrimitiveJsonFiles
    try
    {
        for (auto& v : pt.get_child("instancedPrimitiveJsonFiles"))
        {
            std::string type = v.second.get<std::string>("type");

            if (type == "archive")
            {
                std::string inst_json_file = v.second.get<std::string>("jsonFile");
                load_instanced_primitive_json_file(island_base_path, inst_json_file, base_transform, materials);
            }
        }
    }
    catch (...)
    {
        // No instancedPrimitiveJsonFiles
    }

    // instandedCopies (TODO: support copies with other geometry!)
    try
    {
        for (auto& v : pt.get_child("instancedCopies"))
        {
            auto transform = std::make_shared<sg::transform>();
            root->add_child(transform);

            int i = 0;
            for (auto& item : v.second.get_child("transformMatrix"))
            {
                transform->matrix().data()[i++] = item.second.get_value<float>();
                assert(i <= 16);
            }

            // Check if the copy has its own geomObjFile
            // (i.e. only texture and material are copied)
            try
            {
                std::string geom_obj_file = v.second.get<std::string>("geomObjFile");
                std::vector<std::shared_ptr<sg::node>> objs;
                load_obj((island_base_path / geom_obj_file).string(), materials, objs);
                for (auto obj : objs)
                {
                    transform->add_child(obj);
                }
            }
            catch (...)
            {
                // No. Rather the default case, add the top level
                // json file's geomObjFile content as an instance
                for (auto c : base_transform->children())
                {
                    transform->add_child(c);
                }
            }

            // Copies may also have their own instancedPrimitiveJsonFiles
            try
            {
                for (auto& item : v.second.get_child("instancedPrimitiveJsonFiles"))
                {
                    std::string type = item.second.get<std::string>("type");

                    if (type == "archive")
                    {
                        std::string inst_json_file = item.second.get<std::string>("jsonFile");
                        load_instanced_primitive_json_file(island_base_path, inst_json_file, transform, materials);
                    }
                }
            }
            catch (...)
            {
                // But they don't have to
            }
        }
    }
    catch (...)
    {
        // No instancedCopies
    }

#if 0
    flatten(mod, *root);std::cout << mod.primitives.size() << '\n';
#else
    if (mod.scene_graph == nullptr)
    {
        mod.scene_graph = std::make_shared<sg::node>();
    }

    mod.scene_graph->add_child(root);
#endif
}

} // visionaray
<commit_msg>Use rapidjson to parse Moana json files<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <cassert>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <numeric>
#include <ostream>
#include <vector>

#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/filesystem.hpp>

#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>

#include "moana_loader.h"
#include "model.h"
#include "obj_grammar.h"
#include "sg.h"

namespace visionaray
{

//-------------------------------------------------------------------------------------------------
// Extract base path from json file
// As a heuristic, traverse the path from left to right until we find a folder json/
// The parent folder of the latter is the base path
//

inline boost::filesystem::path get_base_path(std::string const& filename)
{
    boost::filesystem::path p(filename);

    while (p.stem().string() != "json" || p.empty())
    {
        p = p.parent_path();
    }

    return p.parent_path();
}

//-------------------------------------------------------------------------------------------------
// Map obj indices to unsigned base-0 indices
//

template <typename Int>
inline Int remap_index(Int idx, Int size)
{
    return idx < 0 ? size + idx : idx - 1;
}

static void store_faces(
        std::shared_ptr<sg::triangle_mesh>& tm,
        vertex_vector const&                vertices,
        normal_vector const&                normals,
        face_vector const&                  faces
        )
{

    auto vertices_size = static_cast<int>(vertices.size());
    size_t last = 2;
    auto i1 = remap_index(faces[0].vertex_index, vertices_size);

    // simply construct new vertices for each obj face we encounter
    // ..too hard to keep track of v/vn combinations..
    while (last != faces.size())
    {
        // triangle indices
        auto i2 = remap_index(faces[last - 1].vertex_index, vertices_size);
        auto i3 = remap_index(faces[last].vertex_index, vertices_size);

        // no texture coordinates
        // ...

        // normal indices
        auto normals_size = static_cast<int>(normals.size());
        auto ni1 = remap_index(*faces[0].normal_index, normals_size);
        auto ni2 = remap_index(*faces[last - 1].normal_index, normals_size);
        auto ni3 = remap_index(*faces[last].normal_index, normals_size);

        tm->vertices.push_back({
            vertices[i1],
            normals[ni1],
            vec2(0.0f), // no tex coords
            vec4(0.0f)  // base color undefined
            });

        tm->vertices.push_back({
            vertices[i2],
            normals[ni2],
            vec2(0.0f), // no tex coords
            vec4(0.0f)  // base color undefined
            });

        tm->vertices.push_back({
            vertices[i3],
            normals[ni3],
            vec2(0.0f), // no tex coords
            vec4(0.0f)  // base color undefined
            });

        ++last;
    }
}

static void load_obj(
        std::string const& filename,
        std::map<std::string, std::shared_ptr<sg::disney_material>> const& materials,
        std::vector<std::shared_ptr<sg::node>>& objs
        )
{
    namespace qi = boost::spirit::qi;

    using boost::string_ref;

    boost::iostreams::mapped_file_source file(filename);

    obj_grammar grammar;

    string_ref text(file.data(), file.size());
    auto it = text.cbegin();

    // containers for parsing

    vertex_vector    vertices;
    tex_coord_vector tex_coords;
    normal_vector    normals;
    face_vector      faces;

    string_ref comment;
    string_ref mtl_file;
    string_ref mtl_name;

    std::shared_ptr<sg::surface_properties> surf = nullptr;
    std::shared_ptr<sg::triangle_mesh> tm = nullptr;

    while (it != text.cend())
    {
        faces.clear();
        if ( qi::phrase_parse(it, text.cend(), grammar.r_usemtl, qi::blank, mtl_name) )
        {
            objs.push_back(std::make_shared<sg::surface_properties>());
            objs.back()->add_child(std::make_shared<sg::triangle_mesh>());

            surf = std::dynamic_pointer_cast<sg::surface_properties>(objs.back());
            tm = std::dynamic_pointer_cast<sg::triangle_mesh>(surf->children().back());

            std::string usemtl = mtl_name.to_string();
            auto it = materials.find(usemtl);
            if (it != materials.end())
            {
                surf->material() = it->second;
            }
        }
        else if ( qi::phrase_parse(it, text.cend(), grammar.r_vertices, qi::blank, vertices) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), grammar.r_normals, qi::blank, normals) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), grammar.r_face, qi::blank, faces) )
        {
            store_faces(tm, vertices, normals, faces);
        }
        else if ( qi::phrase_parse(it, text.cend(), grammar.r_unhandled, qi::blank) )
        {
        }
        else
        {
            ++it;
        }
    }

    for (auto obj : objs)
    {
        auto surf = std::dynamic_pointer_cast<sg::surface_properties>(obj);

        for (auto c : surf->children())
        {
            auto tm = std::dynamic_pointer_cast<sg::triangle_mesh>(c);

            // Fill with 0,1,2,3,4,..
            tm->indices.resize(tm->vertices.size());
            std::iota(tm->indices.begin(), tm->indices.end(), 0);
        }
    }
}

static void load_instanced_primitive_json_file(
        boost::filesystem::path const& island_base_path,
        std::string const& filename,
        std::shared_ptr<sg::node> root,
        std::map<std::string, std::shared_ptr<sg::disney_material>>& materials
        )
{
    std::cout << "Load instanced primitive json file: " << (island_base_path / filename).string() << '\n';
    std::ifstream stream((island_base_path / filename).string());
    if (stream.fail())
    {
        std::cerr << "Cannot open " << filename << '\n';
        return;
    }

    rapidjson::IStreamWrapper isw(stream);
    rapidjson::Document doc;
    doc.ParseStream(isw);

    for (auto it = doc.MemberBegin(); it != doc.MemberEnd(); ++it)
    {
        auto entry = it->value.GetObject();

        // Instance geometry
        std::string obj_file = it->name.GetString();
        std::vector<std::shared_ptr<sg::node>> objs;
        load_obj((island_base_path / obj_file).string(), materials, objs);

        // Instance transforms
        auto entries = it->value.GetObject();
        for (auto it = entries.MemberBegin(); it != entries.MemberEnd(); ++it)
        {
            auto transform = std::make_shared<sg::transform>();
            int i = 0;
            rapidjson::Value const& tm = it->value;
            for (auto& item : tm.GetArray())
            {
                transform->matrix().data()[i++] = item.GetFloat();
                assert(i <= 16);
            }

            for (auto obj : objs)
            {
                transform->add_child(obj);
            }
            root->add_child(transform);
        }
    }
}

void load_material_file(
        std::string const& filename,
        std::map<std::string, std::shared_ptr<sg::disney_material>>& materials
        )
{
    std::cout << "Load material file: " << filename << '\n';
    std::ifstream stream(filename);
    if (stream.fail())
    {
        std::cerr << "Cannot open " << filename << '\n';
        return;
    }

    rapidjson::IStreamWrapper isw(stream);
    rapidjson::Document doc;
    doc.ParseStream(isw);

    for (auto it = doc.MemberBegin(); it != doc.MemberEnd(); ++it)
    {
        auto entry = it->value.GetObject();

        std::string material_name = it->name.GetString();

        std::shared_ptr<sg::disney_material> mat = std::make_shared<sg::disney_material>();

        int i = 0;
        rapidjson::Value const& bc = entry["baseColor"];
        for (auto& item : bc.GetArray())
        {
            mat->base_color[i++] = item.GetFloat();
            assert(i <= 4);
        }

        materials.insert({ material_name, mat });
    }
}

void load_moana(std::string const& filename, model& mod)
{
    std::cout << "Load moana file: " << filename << '\n';
    std::ifstream stream(filename);
    if (stream.fail())
    {
        std::cerr << "Cannot open " << filename << '\n';
        return;
    }

    boost::filesystem::path island_base_path = get_base_path(filename);

    if (island_base_path.empty())
    {
        std::cerr << "Cannot extract Moana Island Scene base path from " << filename << '\n';
        return;
    }

    rapidjson::IStreamWrapper isw(stream);
    rapidjson::Document doc;
    doc.ParseStream(isw);

    auto root = std::make_shared<sg::node>();

    // matFile
    std::string mat_file = doc["matFile"].GetString();
    std::map<std::string, std::shared_ptr<sg::disney_material>> materials;
    load_material_file((island_base_path / mat_file).string(), materials);

    auto base_transform = std::make_shared<sg::transform>();
    root->add_child(base_transform);

    // transformMatrix
    int i = 0;
    rapidjson::Value const& tm = doc["transformMatrix"];
    for (auto& item : tm.GetArray())
    {
        base_transform->matrix().data()[i++] = item.GetFloat();
        assert(i <= 16);
    }

    // geomObjFile
    if (doc.HasMember("geomObjFile"))
    {
        std::string geom_obj_file = doc["geomObjFile"].GetString();
        std::vector<std::shared_ptr<sg::node>> objs;
        load_obj((island_base_path / geom_obj_file).string(), materials, objs);
        for (auto obj : objs)
        {
            base_transform->add_child(obj);
        }
    }

    // instancedPrimitiveJsonFiles
    if (doc.HasMember("instancedPrimitiveJsonFiles"))
    {
        rapidjson::Value const& entries = doc["instancedPrimitiveJsonFiles"];
        for (auto it = entries.MemberBegin(); it != entries.MemberEnd(); ++it)
        {
            auto entry = it->value.GetObject();
            std::string type = entry["type"].GetString();

            if (type == "archive")
            {
                std::string inst_json_file = entry["jsonFile"].GetString();
                load_instanced_primitive_json_file(island_base_path, inst_json_file, base_transform, materials);
            }
        }
    }

    // instandedCopies (TODO: support copies with other geometry!)
    if (doc.HasMember("instancedCopies"))
    {
        rapidjson::Value const& entries = doc["instancedCopies"];
        for (auto it = entries.MemberBegin(); it != entries.MemberEnd(); ++it)
        {
            auto transform = std::make_shared<sg::transform>();
            root->add_child(transform);

            auto entry = it->value.GetObject();

            int i = 0;
            rapidjson::Value const& tm = entry["transformMatrix"];
            for (auto& item : tm.GetArray())
            {
                transform->matrix().data()[i++] = item.GetFloat();
                assert(i <= 16);
            }

            // Check if the copy has its own geomObjFile
            // (i.e. only texture and material are copied)
            if (entry.HasMember("geomObjFile"))
            {
                std::string geom_obj_file = entry["geomObjFile"].GetString();
                std::vector<std::shared_ptr<sg::node>> objs;
                load_obj((island_base_path / geom_obj_file).string(), materials, objs);
                for (auto obj : objs)
                {
                    transform->add_child(obj);
                }
            }
            else
            {
                // No. Rather the default case, add the top level
                // json file's geomObjFile content as an instance
                for (auto c : base_transform->children())
                {
                    transform->add_child(c);
                }
            }

            // Copies may also have their own instancedPrimitiveJsonFiles
            if (entry.HasMember("instancedPrimitiveJsonFiles"))
            {
                rapidjson::Value const& entries = entry["instancedPrimitiveJsonFiles"];
                for (auto it = entries.MemberBegin(); it != entries.MemberEnd(); ++it)
                {
                    auto entry = it->value.GetObject();

                    std::string type = entry["type"].GetString();

                    if (type == "archive")
                    {
                        std::string inst_json_file = entry["jsonFile"].GetString();
                        load_instanced_primitive_json_file(island_base_path, inst_json_file, transform, materials);
                    }
                }
            }
        }
    }

#if 0
    flatten(mod, *root);std::cout << mod.primitives.size() << '\n';
#else
    if (mod.scene_graph == nullptr)
    {
        mod.scene_graph = std::make_shared<sg::node>();
    }

    mod.scene_graph->add_child(root);
#endif
}

} // visionaray
<|endoftext|>


<commit_before>// Copyright (c) 2010 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.

#include "base/nss_util.h"
#include "base/nss_util_internal.h"

#include <nss.h>
#include <plarena.h>
#include <prerror.h>
#include <prinit.h>
#include <prtime.h>
#include <pk11pub.h>
#include <secmod.h>

#if defined(OS_LINUX)
#include <linux/magic.h>
#include <sys/vfs.h>
#endif

#include "base/file_util.h"
#include "base/logging.h"
#include "base/singleton.h"
#include "base/stringprintf.h"
#include "base/thread_restrictions.h"

// USE_NSS means we use NSS for everything crypto-related.  If USE_NSS is not
// defined, such as on Mac and Windows, we use NSS for SSL only -- we don't
// use NSS for crypto or certificate verification, and we don't use the NSS
// certificate and key databases.
#if defined(USE_NSS)
#include "base/environment.h"
#include "base/lock.h"
#include "base/scoped_ptr.h"
#endif  // defined(USE_NSS)

namespace base {

namespace {

#if defined(USE_NSS)
FilePath GetDefaultConfigDirectory() {
  FilePath dir = file_util::GetHomeDir();
  if (dir.empty()) {
    LOG(ERROR) << "Failed to get home directory.";
    return dir;
  }
  dir = dir.AppendASCII(".pki").AppendASCII("nssdb");
  if (!file_util::CreateDirectory(dir)) {
    LOG(ERROR) << "Failed to create ~/.pki/nssdb directory.";
    dir.clear();
  }
  return dir;
}

// On non-chromeos platforms, return the default config directory.
// On chromeos, return a read-only directory with fake root CA certs for testing
// (which will not exist on non-testing images).  These root CA certs are used
// by the local Google Accounts server mock we use when testing our login code.
// If this directory is not present, NSS_Init() will fail.  It is up to the
// caller to failover to NSS_NoDB_Init() at that point.
FilePath GetInitialConfigDirectory() {
#if defined(OS_CHROMEOS)
  static const FilePath::CharType kReadOnlyCertDB[] =
      FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
  return FilePath(kReadOnlyCertDB);
#else
  return GetDefaultConfigDirectory();
#endif  // defined(OS_CHROMEOS)
}

// NSS creates a local cache of the sqlite database if it detects that the
// filesystem the database is on is much slower than the local disk.  The
// detection doesn't work with the latest versions of sqlite, such as 3.6.22
// (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561).  So we set
// the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's
// detection when database_dir is on NFS.  See http://crbug.com/48585.
//
// TODO(wtc): port this function to other USE_NSS platforms.  It is defined
// only for OS_LINUX simply because the statfs structure is OS-specific.
void UseLocalCacheOfNSSDatabaseIfNFS(const FilePath& database_dir) {
#if defined(OS_LINUX)
  struct statfs buf;
  if (statfs(database_dir.value().c_str(), &buf) == 0) {
    if (buf.f_type == NFS_SUPER_MAGIC) {
      scoped_ptr<base::Environment> env(base::Environment::Create());
      const char* use_cache_env_var = "NSS_SDB_USE_CACHE";
      if (!env->HasVar(use_cache_env_var))
        env->SetVar(use_cache_env_var, "yes");
    }
  }
#endif  // defined(OS_LINUX)
}

// Load nss's built-in root certs.
SECMODModule *InitDefaultRootCerts() {
  const char* kModulePath = "libnssckbi.so";
  char modparams[1024];
  snprintf(modparams, sizeof(modparams),
           "name=\"Root Certs\" library=\"%s\"", kModulePath);
  SECMODModule *root = SECMOD_LoadUserModule(modparams, NULL, PR_FALSE);
  if (root)
    return root;

  // Aw, snap.  Can't find/load root cert shared library.
  // This will make it hard to talk to anybody via https.
  NOTREACHED();
  return NULL;
}
#endif  // defined(USE_NSS)

// A singleton to initialize/deinitialize NSPR.
// Separate from the NSS singleton because we initialize NSPR on the UI thread.
class NSPRInitSingleton {
 public:
  NSPRInitSingleton() {
    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
  }

  ~NSPRInitSingleton() {
    PL_ArenaFinish();
    PRStatus prstatus = PR_Cleanup();
    if (prstatus != PR_SUCCESS) {
      LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?";
    }
  }
};

class NSSInitSingleton {
 public:
  NSSInitSingleton()
      : real_db_slot_(NULL),
        test_db_slot_(NULL),
        root_(NULL),
        chromeos_user_logged_in_(false) {
    base::EnsureNSPRInit();

    // We *must* have NSS >= 3.12.3.  See bug 26448.
    COMPILE_ASSERT(
        (NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH >= 3) ||
        (NSS_VMAJOR == 3 && NSS_VMINOR > 12) ||
        (NSS_VMAJOR > 3),
        nss_version_check_failed);
    // Also check the run-time NSS version.
    // NSS_VersionCheck is a >= check, not strict equality.
    if (!NSS_VersionCheck("3.12.3")) {
      // It turns out many people have misconfigured NSS setups, where
      // their run-time NSPR doesn't match the one their NSS was compiled
      // against.  So rather than aborting, complain loudly.
      LOG(ERROR) << "NSS_VersionCheck(\"3.12.3\") failed.  "
                    "We depend on NSS >= 3.12.3, and this error is not fatal "
                    "only because many people have busted NSS setups (for "
                    "example, using the wrong version of NSPR). "
                    "Please upgrade to the latest NSS and NSPR, and if you "
                    "still get this error, contact your distribution "
                    "maintainer.";
    }

    SECStatus status = SECFailure;
#if !defined(USE_NSS)
    // Use the system certificate store, so initialize NSS without database.
    status = NSS_NoDB_Init(NULL);
    if (status != SECSuccess) {
      LOG(ERROR) << "Error initializing NSS without a persistent "
                    "database: NSS error code " << PR_GetError();
    }
#else
    FilePath database_dir = GetInitialConfigDirectory();
    if (!database_dir.empty()) {
      UseLocalCacheOfNSSDatabaseIfNFS(database_dir);

      // Initialize with a persistent database (likely, ~/.pki/nssdb).
      // Use "sql:" which can be shared by multiple processes safely.
      std::string nss_config_dir =
          StringPrintf("sql:%s", database_dir.value().c_str());
#if defined(OS_CHROMEOS)
      status = NSS_Init(nss_config_dir.c_str());
#else
      status = NSS_InitReadWrite(nss_config_dir.c_str());
#endif
      if (status != SECSuccess) {
        LOG(ERROR) << "Error initializing NSS with a persistent "
                      "database (" << nss_config_dir
                   << "): NSS error code " << PR_GetError();
      }
    }
    if (status != SECSuccess) {
      LOG(WARNING) << "Initialize NSS without a persistent database "
                      "(~/.pki/nssdb).";
      status = NSS_NoDB_Init(NULL);
      if (status != SECSuccess) {
        LOG(ERROR) << "Error initializing NSS without a persistent "
                      "database: NSS error code " << PR_GetError();
        return;
      }
    }

    // If we haven't initialized the password for the NSS databases,
    // initialize an empty-string password so that we don't need to
    // log in.
    PK11SlotInfo* slot = PK11_GetInternalKeySlot();
    if (slot) {
      // PK11_InitPin may write to the keyDB, but no other thread can use NSS
      // yet, so we don't need to lock.
      if (PK11_NeedUserInit(slot))
        PK11_InitPin(slot, NULL, NULL);
      PK11_FreeSlot(slot);
    }

    // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011
    // is fixed, we will no longer need the lock. We should detect this and not
    // initialize a Lock here.
    write_lock_.reset(new Lock());

    root_ = InitDefaultRootCerts();
#endif  // !defined(USE_NSS)
  }

  ~NSSInitSingleton() {
    if (real_db_slot_) {
      SECMOD_CloseUserDB(real_db_slot_);
      PK11_FreeSlot(real_db_slot_);
      real_db_slot_ = NULL;
    }
    CloseTestNSSDB();
    if (root_) {
      SECMOD_UnloadUserModule(root_);
      SECMOD_DestroyModule(root_);
      root_ = NULL;
    }

    SECStatus status = NSS_Shutdown();
    if (status != SECSuccess) {
      // We VLOG(1) because this failure is relatively harmless (leaking, but
      // we're shutting down anyway).
      VLOG(1) << "NSS_Shutdown failed; see "
                 "http://code.google.com/p/chromium/issues/detail?id=4609";
    }
  }

#if defined(OS_CHROMEOS)
  void OpenPersistentNSSDB() {
    if (!chromeos_user_logged_in_) {
      chromeos_user_logged_in_ = true;
      real_db_slot_ = OpenUserDB(GetDefaultConfigDirectory(),
                                 "Real NSS database");
    }
  }
#endif  // defined(OS_CHROMEOS)

  bool OpenTestNSSDB(const FilePath& path, const char* description) {
    test_db_slot_ = OpenUserDB(path, description);
    return !!test_db_slot_;
  }

  void CloseTestNSSDB() {
    if (test_db_slot_) {
      SECStatus status = SECMOD_CloseUserDB(test_db_slot_);
      if (status != SECSuccess)
        LOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
      PK11_FreeSlot(test_db_slot_);
      test_db_slot_ = NULL;
    }
  }

  PK11SlotInfo* GetDefaultKeySlot() {
    if (test_db_slot_)
      return PK11_ReferenceSlot(test_db_slot_);
    if (real_db_slot_)
      return PK11_ReferenceSlot(real_db_slot_);
    return PK11_GetInternalKeySlot();
  }

#if defined(USE_NSS)
  Lock* write_lock() {
    return write_lock_.get();
  }
#endif  // defined(USE_NSS)

 private:
  static PK11SlotInfo* OpenUserDB(const FilePath& path,
                                  const char* description) {
    const std::string modspec =
        StringPrintf("configDir='sql:%s' tokenDescription='%s'",
                     path.value().c_str(), description);
    PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str());
    if (db_slot) {
      if (PK11_NeedUserInit(db_slot))
        PK11_InitPin(db_slot, NULL, NULL);
    }
    else {
      LOG(ERROR) << "Error opening persistent database (" << modspec
                 << "): NSS error code " << PR_GetError();
    }
    return db_slot;
  }

  PK11SlotInfo* real_db_slot_;  // Overrides internal key slot if non-NULL.
  PK11SlotInfo* test_db_slot_;  // Overrides internal key slot and real_db_slot_
  SECMODModule *root_;
  bool chromeos_user_logged_in_;
#if defined(USE_NSS)
  scoped_ptr<Lock> write_lock_;
#endif  // defined(USE_NSS)
};

}  // namespace

void EnsureNSPRInit() {
  Singleton<NSPRInitSingleton>::get();
}

void EnsureNSSInit() {
  // Initializing SSL causes us to do blocking IO.
  // Temporarily allow it until we fix
  //   http://code.google.com/p/chromium/issues/detail?id=59847
  base::ThreadRestrictions::ScopedAllowIO allow_io;
  Singleton<NSSInitSingleton>::get();
}

#if defined(USE_NSS)
bool OpenTestNSSDB(const FilePath& path, const char* description) {
  return Singleton<NSSInitSingleton>::get()->OpenTestNSSDB(path, description);
}

void CloseTestNSSDB() {
  Singleton<NSSInitSingleton>::get()->CloseTestNSSDB();
}

Lock* GetNSSWriteLock() {
  return Singleton<NSSInitSingleton>::get()->write_lock();
}

AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) {
  // May be NULL if the lock is not needed in our version of NSS.
  if (lock_)
    lock_->Acquire();
}

AutoNSSWriteLock::~AutoNSSWriteLock() {
  if (lock_) {
    lock_->AssertAcquired();
    lock_->Release();
  }
}
#endif  // defined(USE_NSS)

#if defined(OS_CHROMEOS)
void OpenPersistentNSSDB() {
  Singleton<NSSInitSingleton>::get()->OpenPersistentNSSDB();
}
#endif

// TODO(port): Implement this more simply.  We can convert by subtracting an
// offset (the difference between NSPR's and base::Time's epochs).
Time PRTimeToBaseTime(PRTime prtime) {
  PRExplodedTime prxtime;
  PR_ExplodeTime(prtime, PR_GMTParameters, &prxtime);

  base::Time::Exploded exploded;
  exploded.year         = prxtime.tm_year;
  exploded.month        = prxtime.tm_month + 1;
  exploded.day_of_week  = prxtime.tm_wday;
  exploded.day_of_month = prxtime.tm_mday;
  exploded.hour         = prxtime.tm_hour;
  exploded.minute       = prxtime.tm_min;
  exploded.second       = prxtime.tm_sec;
  exploded.millisecond  = prxtime.tm_usec / 1000;

  return Time::FromUTCExploded(exploded);
}

PK11SlotInfo* GetDefaultNSSKeySlot() {
  return Singleton<NSSInitSingleton>::get()->GetDefaultKeySlot();
}

}  // namespace base
<commit_msg>linux: include linux/nfs_fs.h for NFS superblock magic<commit_after>// Copyright (c) 2010 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.

#include "base/nss_util.h"
#include "base/nss_util_internal.h"

#include <nss.h>
#include <plarena.h>
#include <prerror.h>
#include <prinit.h>
#include <prtime.h>
#include <pk11pub.h>
#include <secmod.h>

#if defined(OS_LINUX)
#include <linux/nfs_fs.h>
#include <sys/vfs.h>
#endif

#include "base/file_util.h"
#include "base/logging.h"
#include "base/singleton.h"
#include "base/stringprintf.h"
#include "base/thread_restrictions.h"

// USE_NSS means we use NSS for everything crypto-related.  If USE_NSS is not
// defined, such as on Mac and Windows, we use NSS for SSL only -- we don't
// use NSS for crypto or certificate verification, and we don't use the NSS
// certificate and key databases.
#if defined(USE_NSS)
#include "base/environment.h"
#include "base/lock.h"
#include "base/scoped_ptr.h"
#endif  // defined(USE_NSS)

namespace base {

namespace {

#if defined(USE_NSS)
FilePath GetDefaultConfigDirectory() {
  FilePath dir = file_util::GetHomeDir();
  if (dir.empty()) {
    LOG(ERROR) << "Failed to get home directory.";
    return dir;
  }
  dir = dir.AppendASCII(".pki").AppendASCII("nssdb");
  if (!file_util::CreateDirectory(dir)) {
    LOG(ERROR) << "Failed to create ~/.pki/nssdb directory.";
    dir.clear();
  }
  return dir;
}

// On non-chromeos platforms, return the default config directory.
// On chromeos, return a read-only directory with fake root CA certs for testing
// (which will not exist on non-testing images).  These root CA certs are used
// by the local Google Accounts server mock we use when testing our login code.
// If this directory is not present, NSS_Init() will fail.  It is up to the
// caller to failover to NSS_NoDB_Init() at that point.
FilePath GetInitialConfigDirectory() {
#if defined(OS_CHROMEOS)
  static const FilePath::CharType kReadOnlyCertDB[] =
      FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
  return FilePath(kReadOnlyCertDB);
#else
  return GetDefaultConfigDirectory();
#endif  // defined(OS_CHROMEOS)
}

// NSS creates a local cache of the sqlite database if it detects that the
// filesystem the database is on is much slower than the local disk.  The
// detection doesn't work with the latest versions of sqlite, such as 3.6.22
// (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561).  So we set
// the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's
// detection when database_dir is on NFS.  See http://crbug.com/48585.
//
// TODO(wtc): port this function to other USE_NSS platforms.  It is defined
// only for OS_LINUX simply because the statfs structure is OS-specific.
void UseLocalCacheOfNSSDatabaseIfNFS(const FilePath& database_dir) {
#if defined(OS_LINUX)
  struct statfs buf;
  if (statfs(database_dir.value().c_str(), &buf) == 0) {
    if (buf.f_type == NFS_SUPER_MAGIC) {
      scoped_ptr<base::Environment> env(base::Environment::Create());
      const char* use_cache_env_var = "NSS_SDB_USE_CACHE";
      if (!env->HasVar(use_cache_env_var))
        env->SetVar(use_cache_env_var, "yes");
    }
  }
#endif  // defined(OS_LINUX)
}

// Load nss's built-in root certs.
SECMODModule *InitDefaultRootCerts() {
  const char* kModulePath = "libnssckbi.so";
  char modparams[1024];
  snprintf(modparams, sizeof(modparams),
           "name=\"Root Certs\" library=\"%s\"", kModulePath);
  SECMODModule *root = SECMOD_LoadUserModule(modparams, NULL, PR_FALSE);
  if (root)
    return root;

  // Aw, snap.  Can't find/load root cert shared library.
  // This will make it hard to talk to anybody via https.
  NOTREACHED();
  return NULL;
}
#endif  // defined(USE_NSS)

// A singleton to initialize/deinitialize NSPR.
// Separate from the NSS singleton because we initialize NSPR on the UI thread.
class NSPRInitSingleton {
 public:
  NSPRInitSingleton() {
    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
  }

  ~NSPRInitSingleton() {
    PL_ArenaFinish();
    PRStatus prstatus = PR_Cleanup();
    if (prstatus != PR_SUCCESS) {
      LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?";
    }
  }
};

class NSSInitSingleton {
 public:
  NSSInitSingleton()
      : real_db_slot_(NULL),
        test_db_slot_(NULL),
        root_(NULL),
        chromeos_user_logged_in_(false) {
    base::EnsureNSPRInit();

    // We *must* have NSS >= 3.12.3.  See bug 26448.
    COMPILE_ASSERT(
        (NSS_VMAJOR == 3 && NSS_VMINOR == 12 && NSS_VPATCH >= 3) ||
        (NSS_VMAJOR == 3 && NSS_VMINOR > 12) ||
        (NSS_VMAJOR > 3),
        nss_version_check_failed);
    // Also check the run-time NSS version.
    // NSS_VersionCheck is a >= check, not strict equality.
    if (!NSS_VersionCheck("3.12.3")) {
      // It turns out many people have misconfigured NSS setups, where
      // their run-time NSPR doesn't match the one their NSS was compiled
      // against.  So rather than aborting, complain loudly.
      LOG(ERROR) << "NSS_VersionCheck(\"3.12.3\") failed.  "
                    "We depend on NSS >= 3.12.3, and this error is not fatal "
                    "only because many people have busted NSS setups (for "
                    "example, using the wrong version of NSPR). "
                    "Please upgrade to the latest NSS and NSPR, and if you "
                    "still get this error, contact your distribution "
                    "maintainer.";
    }

    SECStatus status = SECFailure;
#if !defined(USE_NSS)
    // Use the system certificate store, so initialize NSS without database.
    status = NSS_NoDB_Init(NULL);
    if (status != SECSuccess) {
      LOG(ERROR) << "Error initializing NSS without a persistent "
                    "database: NSS error code " << PR_GetError();
    }
#else
    FilePath database_dir = GetInitialConfigDirectory();
    if (!database_dir.empty()) {
      UseLocalCacheOfNSSDatabaseIfNFS(database_dir);

      // Initialize with a persistent database (likely, ~/.pki/nssdb).
      // Use "sql:" which can be shared by multiple processes safely.
      std::string nss_config_dir =
          StringPrintf("sql:%s", database_dir.value().c_str());
#if defined(OS_CHROMEOS)
      status = NSS_Init(nss_config_dir.c_str());
#else
      status = NSS_InitReadWrite(nss_config_dir.c_str());
#endif
      if (status != SECSuccess) {
        LOG(ERROR) << "Error initializing NSS with a persistent "
                      "database (" << nss_config_dir
                   << "): NSS error code " << PR_GetError();
      }
    }
    if (status != SECSuccess) {
      LOG(WARNING) << "Initialize NSS without a persistent database "
                      "(~/.pki/nssdb).";
      status = NSS_NoDB_Init(NULL);
      if (status != SECSuccess) {
        LOG(ERROR) << "Error initializing NSS without a persistent "
                      "database: NSS error code " << PR_GetError();
        return;
      }
    }

    // If we haven't initialized the password for the NSS databases,
    // initialize an empty-string password so that we don't need to
    // log in.
    PK11SlotInfo* slot = PK11_GetInternalKeySlot();
    if (slot) {
      // PK11_InitPin may write to the keyDB, but no other thread can use NSS
      // yet, so we don't need to lock.
      if (PK11_NeedUserInit(slot))
        PK11_InitPin(slot, NULL, NULL);
      PK11_FreeSlot(slot);
    }

    // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011
    // is fixed, we will no longer need the lock. We should detect this and not
    // initialize a Lock here.
    write_lock_.reset(new Lock());

    root_ = InitDefaultRootCerts();
#endif  // !defined(USE_NSS)
  }

  ~NSSInitSingleton() {
    if (real_db_slot_) {
      SECMOD_CloseUserDB(real_db_slot_);
      PK11_FreeSlot(real_db_slot_);
      real_db_slot_ = NULL;
    }
    CloseTestNSSDB();
    if (root_) {
      SECMOD_UnloadUserModule(root_);
      SECMOD_DestroyModule(root_);
      root_ = NULL;
    }

    SECStatus status = NSS_Shutdown();
    if (status != SECSuccess) {
      // We VLOG(1) because this failure is relatively harmless (leaking, but
      // we're shutting down anyway).
      VLOG(1) << "NSS_Shutdown failed; see "
                 "http://code.google.com/p/chromium/issues/detail?id=4609";
    }
  }

#if defined(OS_CHROMEOS)
  void OpenPersistentNSSDB() {
    if (!chromeos_user_logged_in_) {
      chromeos_user_logged_in_ = true;
      real_db_slot_ = OpenUserDB(GetDefaultConfigDirectory(),
                                 "Real NSS database");
    }
  }
#endif  // defined(OS_CHROMEOS)

  bool OpenTestNSSDB(const FilePath& path, const char* description) {
    test_db_slot_ = OpenUserDB(path, description);
    return !!test_db_slot_;
  }

  void CloseTestNSSDB() {
    if (test_db_slot_) {
      SECStatus status = SECMOD_CloseUserDB(test_db_slot_);
      if (status != SECSuccess)
        LOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError();
      PK11_FreeSlot(test_db_slot_);
      test_db_slot_ = NULL;
    }
  }

  PK11SlotInfo* GetDefaultKeySlot() {
    if (test_db_slot_)
      return PK11_ReferenceSlot(test_db_slot_);
    if (real_db_slot_)
      return PK11_ReferenceSlot(real_db_slot_);
    return PK11_GetInternalKeySlot();
  }

#if defined(USE_NSS)
  Lock* write_lock() {
    return write_lock_.get();
  }
#endif  // defined(USE_NSS)

 private:
  static PK11SlotInfo* OpenUserDB(const FilePath& path,
                                  const char* description) {
    const std::string modspec =
        StringPrintf("configDir='sql:%s' tokenDescription='%s'",
                     path.value().c_str(), description);
    PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str());
    if (db_slot) {
      if (PK11_NeedUserInit(db_slot))
        PK11_InitPin(db_slot, NULL, NULL);
    }
    else {
      LOG(ERROR) << "Error opening persistent database (" << modspec
                 << "): NSS error code " << PR_GetError();
    }
    return db_slot;
  }

  PK11SlotInfo* real_db_slot_;  // Overrides internal key slot if non-NULL.
  PK11SlotInfo* test_db_slot_;  // Overrides internal key slot and real_db_slot_
  SECMODModule *root_;
  bool chromeos_user_logged_in_;
#if defined(USE_NSS)
  scoped_ptr<Lock> write_lock_;
#endif  // defined(USE_NSS)
};

}  // namespace

void EnsureNSPRInit() {
  Singleton<NSPRInitSingleton>::get();
}

void EnsureNSSInit() {
  // Initializing SSL causes us to do blocking IO.
  // Temporarily allow it until we fix
  //   http://code.google.com/p/chromium/issues/detail?id=59847
  base::ThreadRestrictions::ScopedAllowIO allow_io;
  Singleton<NSSInitSingleton>::get();
}

#if defined(USE_NSS)
bool OpenTestNSSDB(const FilePath& path, const char* description) {
  return Singleton<NSSInitSingleton>::get()->OpenTestNSSDB(path, description);
}

void CloseTestNSSDB() {
  Singleton<NSSInitSingleton>::get()->CloseTestNSSDB();
}

Lock* GetNSSWriteLock() {
  return Singleton<NSSInitSingleton>::get()->write_lock();
}

AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) {
  // May be NULL if the lock is not needed in our version of NSS.
  if (lock_)
    lock_->Acquire();
}

AutoNSSWriteLock::~AutoNSSWriteLock() {
  if (lock_) {
    lock_->AssertAcquired();
    lock_->Release();
  }
}
#endif  // defined(USE_NSS)

#if defined(OS_CHROMEOS)
void OpenPersistentNSSDB() {
  Singleton<NSSInitSingleton>::get()->OpenPersistentNSSDB();
}
#endif

// TODO(port): Implement this more simply.  We can convert by subtracting an
// offset (the difference between NSPR's and base::Time's epochs).
Time PRTimeToBaseTime(PRTime prtime) {
  PRExplodedTime prxtime;
  PR_ExplodeTime(prtime, PR_GMTParameters, &prxtime);

  base::Time::Exploded exploded;
  exploded.year         = prxtime.tm_year;
  exploded.month        = prxtime.tm_month + 1;
  exploded.day_of_week  = prxtime.tm_wday;
  exploded.day_of_month = prxtime.tm_mday;
  exploded.hour         = prxtime.tm_hour;
  exploded.minute       = prxtime.tm_min;
  exploded.second       = prxtime.tm_sec;
  exploded.millisecond  = prxtime.tm_usec / 1000;

  return Time::FromUTCExploded(exploded);
}

PK11SlotInfo* GetDefaultNSSKeySlot() {
  return Singleton<NSSInitSingleton>::get()->GetDefaultKeySlot();
}

}  // namespace base
<|endoftext|>