<commit_before>/*
 * Copyright (C) 2012 by Glenn Hickey (hickey@soe.ucsc.edu)
 *
 * Released under the MIT license, see LICENSE.txt
 */
#include <sstream>
#include <deque>
#include <iostream>
#include "halValidate.h"
#include "hal.h"

using namespace std;
using namespace hal;

// current implementation is poor and hacky.  should fix up to
// use iterators to properly scan the segments.

void hal::validateBottomSegment(const BottomSegment* bottomSegment)
{
  const Genome* genome = bottomSegment->getGenome();
  hal_index_t index = bottomSegment->getArrayIndex();
  if (index < 0 || index >= (hal_index_t)genome->getSequenceLength())
  {
    stringstream ss;
    ss << "Bottom segment out of range " << index << " in genome "
       << genome->getName();
    throw hal_exception(ss.str());
  }

  if (bottomSegment->getLength() < 1)
  {
    stringstream ss;
    ss << "Bottom segment " << index  << " in genome " << genome->getName()
       << " has length 0 which is not currently supported";
    throw hal_exception(ss.str());
  }

  hal_size_t numChildren = bottomSegment->getNumChildren();
  for (hal_size_t child = 0; child < numChildren; ++child)
  {
    const Genome* childGenome = genome->getChild(child);
    const hal_index_t childIndex = bottomSegment->getChildIndex(child);
    if (childGenome != NULL && childIndex != NULL_INDEX)
    {
      if (childIndex >= (hal_index_t)childGenome->getNumTopSegments())
      {
        stringstream ss;
        ss << "Child " << child << " index " <<childIndex << " of segment "
           << bottomSegment->getArrayIndex() << " out of range in genome "
           << childGenome->getName();
        throw hal_exception(ss.str());
      }
      TopSegmentIteratorConstPtr topSegmentIteratr =
         childGenome->getTopSegmentIterator(childIndex);
      const TopSegment* childSegment = topSegmentIteratr->getTopSegment();
      if (childSegment->getLength() != bottomSegment->getLength())
      {
        stringstream ss;
        ss << "Child " << child << " length of segment "
           << bottomSegment->getArrayIndex()
           << " in genome " << genome->getName() << " has length "
           << childSegment->getLength() << " which does not match "
           << bottomSegment->getLength();
        throw hal_exception(ss.str());
      }
      if (childSegment->getNextParalogyIndex() == NULL_INDEX &&
          childSegment->getParentIndex() != bottomSegment->getArrayIndex())
      {
        throw hal_exception("parent / child index mismatch (parent=" +
                            genome->getName() + " child=" +
                            childGenome->getName());
      }
    }
  }

  const hal_index_t parseIndex = bottomSegment->getTopParseIndex();
  if (parseIndex == NULL_INDEX)
  {
    if (genome->getParent() != NULL)
    {
      stringstream ss;
      ss << "Bottom segment " << bottomSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has null parse index";
      throw hal_exception(ss.str());
    }
  }
  else
  {
    if (parseIndex >= (hal_index_t)genome->getNumTopSegments())
    {
      stringstream ss;
      ss << "BottomSegment " << bottomSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has parse index " << parseIndex
         << " greater than the number of top segments, "
         << (hal_index_t)genome->getNumTopSegments();
      throw hal_exception(ss.str());
    }
    TopSegmentIteratorConstPtr parseIterator =
       genome->getTopSegmentIterator(parseIndex);
    const TopSegment* parseSegment = parseIterator->getTopSegment();
    hal_offset_t parseOffset = bottomSegment->getTopParseOffset();
    if (parseOffset >= parseSegment->getLength())
    {
      stringstream ss;
      ss << "BottomSegment " << bottomSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has parse offset, " << parseOffset
         << ", greater than the length of the segment, "
         << parseSegment->getLength();
      throw hal_exception(ss.str());
    }
    if ((hal_index_t)parseOffset + parseSegment->getStartPosition() !=
        bottomSegment->getStartPosition())
    {
      throw hal_exception("parse index broken in bottom segment in genome " +
                          genome->getName());

    }
  }
}

void hal::validateTopSegment(const TopSegment* topSegment)
{
  const Genome* genome = topSegment->getGenome();
  hal_index_t index = topSegment->getArrayIndex();
  if (index < 0 || index >= (hal_index_t)genome->getSequenceLength())
  {
    stringstream ss;
    ss << "Segment out of range " << index << " in genome "
       << genome->getName();
    throw hal_exception(ss.str());
  }

  if (topSegment->getLength() < 1)
  {
    stringstream ss;
    ss << "Top segment " << index  << " in genome " << genome->getName()
       << " has length 0 which is not currently supported";
    throw hal_exception(ss.str());
  }

  const Genome* parentGenome = genome->getParent();
  const hal_index_t parentIndex = topSegment->getParentIndex();
  if (parentGenome != NULL && parentIndex != NULL_INDEX)
  {
    if (parentIndex >= (hal_index_t)parentGenome->getNumBottomSegments())
    {
      stringstream ss;
      ss << "Parent index " << parentIndex << " of segment "
         << topSegment->getArrayIndex() << " out of range in genome "
         << parentGenome->getName();
      throw hal_exception(ss.str());
    }
    BottomSegmentIteratorConstPtr bottomSegmentIterator =
       parentGenome->getBottomSegmentIterator(parentIndex);
    const BottomSegment* parentSegment =
       bottomSegmentIterator->getBottomSegment();
    if (topSegment->getLength() != parentSegment->getLength())
    {
      stringstream ss;
      ss << "Parent length of segment " << topSegment->getArrayIndex()
         << " in genome " << genome->getName() << " has length "
         << parentSegment->getLength() << " which does not match "
         << topSegment->getLength();
      throw hal_exception(ss.str());
    }
  }

  const hal_index_t parseIndex = topSegment->getBottomParseIndex();
  if (parseIndex == NULL_INDEX)
  {
    if (genome->getNumChildren() != 0)
    {
      stringstream ss;
      ss << "Segment " << topSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has null parse index";
      throw hal_exception(ss.str());
    }
  }
  else
  {
    if (parseIndex >= (hal_index_t)genome->getNumBottomSegments())
    {
      stringstream ss;
      ss << "Top Segment " << topSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has parse index out of range";
      throw hal_exception(ss.str());
    }
    hal_offset_t parseOffset = topSegment->getBottomParseOffset();
    BottomSegmentIteratorConstPtr bottomSegmentIterator =
       genome->getBottomSegmentIterator(parseIndex);
    const BottomSegment* parseSegment =
       bottomSegmentIterator->getBottomSegment();
    if (parseOffset >= parseSegment->getLength())
    {
      stringstream ss;
      ss << "Top Segment " << topSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has parse offset out of range";
      throw hal_exception(ss.str());
    }
    if ((hal_index_t)parseOffset + parseSegment->getStartPosition() !=
        topSegment->getStartPosition())
    {
      throw hal_exception("parse index broken in top segment in genome " +
                          genome->getName());

    }
  }

  const hal_index_t paralogyIndex = topSegment->getNextParalogyIndex();
  if (paralogyIndex != NULL_INDEX)
  {
    TopSegmentIteratorConstPtr pti =
       genome->getTopSegmentIterator(paralogyIndex);
    if (pti->getTopSegment()->getParentIndex() != topSegment->getParentIndex())
    {
      stringstream ss;
      ss << "Top segment " << topSegment->getArrayIndex() << " has parent index "
         << topSegment->getParentIndex() << ", but next paraglog "
         << topSegment->getNextParalogyIndex() << " has parent Index "
         << pti->getTopSegment()->getParentIndex()
         << ". Paralogous top segments must share same parent.";
      throw hal_exception(ss.str());
    }
  }
}

void hal::validateSequence(const Sequence* sequence)
{
  // Verify that the DNA sequence doesn't contain funny characters
  DNAIteratorConstPtr dnaIt = sequence->getDNAIterator();
  hal_size_t length = sequence->getSequenceLength();
  for (hal_size_t i = 0; i < length; ++i)
  {
    hal_dna_t c = dnaIt->getChar();
    if (isNucleotide(c) == false)
    {
      stringstream ss;
      ss << "Non-nucleotide character discoverd at position "
         << i << " of sequence " << sequence->getName() << ": " << c;
      throw hal_exception(ss.str());
    }
  }

  // Check the top segments
  if (sequence->getGenome()->getParent() != NULL)
  {
    hal_size_t totalTopLength = 0;
    TopSegmentIteratorConstPtr topIt = sequence->getTopSegmentIterator();
    hal_size_t numTopSegments = sequence->getNumTopSegments();
    for (hal_size_t i = 0; i < numTopSegments; ++i)
    {
      const TopSegment* topSegment = topIt->getTopSegment();
      validateTopSegment(topSegment);
      totalTopLength += topSegment->getLength();
      topIt->toRight();
    }
    if (totalTopLength != length)
    {
      stringstream ss;
      ss << "Sequence " << sequence->getName() << " has length " << length
         << " but its top segments add up to " << totalTopLength;
      throw hal_exception(ss.str());
    }
  }

  // Check the bottom segments
  if (sequence->getGenome()->getNumChildren() > 0)
  {
    hal_size_t totalBottomLength = 0;
    BottomSegmentIteratorConstPtr bottomIt =
       sequence->getBottomSegmentIterator();
    hal_size_t numBottomSegments = sequence->getNumBottomSegments();
    for (hal_size_t i = 0; i < numBottomSegments; ++i)
    {
      const BottomSegment* bottomSegment = bottomIt->getBottomSegment();
      validateBottomSegment(bottomSegment);
      totalBottomLength += bottomSegment->getLength();
      bottomIt->toRight();
    }
    if (totalBottomLength != length)
    {
      stringstream ss;
      ss << "Sequence " << sequence->getName() << " has length " << length
         << " but its bottom segments add up to " << totalBottomLength;
      throw hal_exception(ss.str());
    }
  }
}

void hal::validateGenome(const Genome* genome)
{
  // first we check the sequence coverage
  hal_size_t totalTop = 0;
  hal_size_t totalBottom = 0;
  hal_size_t totalLength = 0;

  SequenceIteratorConstPtr seqIt = genome->getSequenceIterator();
  hal_size_t numSequences = genome->getNumSequences();
  for (hal_size_t i = 0; i < numSequences; ++i)
  {
    const Sequence* sequence = seqIt->getSequence();
    validateSequence(sequence);

    totalTop += sequence->getNumTopSegments();
    totalBottom += sequence->getNumBottomSegments();
    totalLength += sequence->getSequenceLength();

    // make sure it doesn't overlap any other sequences;
    const Sequence* s1 =
       genome->getSequenceBySite(sequence->getStartPosition());
    if (s1 == NULL || s1->getName() != sequence->getName())
    {
      stringstream ss;
      ss << "Sequence " << sequence->getName() << " has a bad overlap in "
         << genome->getName();
      throw hal_exception(ss.str());
    }
    const Sequence* s2 =
       genome->getSequenceBySite(sequence->getStartPosition() +
                                 sequence->getSequenceLength() - 1);
    if (s2 == NULL || s2->getName() != sequence->getName())
    {
      stringstream ss;
      ss << "Sequence " << sequence->getName() << " has a bad overlap in "
         << genome->getName();
      throw hal_exception(ss.str());
    }
  }

  hal_size_t genomeLength = genome->getSequenceLength();
  hal_size_t genomeTop = genome->getNumTopSegments();
  hal_size_t genomeBottom = genome->getNumBottomSegments();

  if (genomeLength != totalLength)
  {
    stringstream ss;
    ss << "Problem: genome has length " << genomeLength
       << "But sequences total " << totalLength;
    throw hal_exception(ss.str());
  }
  if (genomeTop != totalTop)
  {
    stringstream ss;
    ss << "Problem: genome has " << genomeTop << " top segments but "
       << "sequences have " << totalTop << " top segments";
    throw ss.str();
  }
  if (genomeBottom != totalBottom)
  {
    stringstream ss;
    ss << "Problem: genome has " << genomeBottom << " bottom segments but "
       << "sequences have " << totalBottom << " bottom segments";
    throw hal_exception(ss.str());
  }
}

void hal::validateAlignment(AlignmentConstPtr alignment)
{
  deque<string> bfQueue;
  bfQueue.push_back(alignment->getRootName());
  while (bfQueue.empty() == false)
  {
    string name = bfQueue.back();
    bfQueue.pop_back();
    if (name.empty() == false)
    {
      const Genome* genome = alignment->openGenome(name);
      if (genome == NULL)
      {
        throw hal_exception("Failure to open genome " + name);
      }
      validateGenome(genome);
      vector<string> childNames = alignment->getChildNames(name);
      for (size_t i = 0; i < childNames.size(); ++i)
      {
        bfQueue.push_front(childNames[i]);
      }
    }
  }
}
<commit_msg>fix error message<commit_after>/*
 * Copyright (C) 2012 by Glenn Hickey (hickey@soe.ucsc.edu)
 *
 * Released under the MIT license, see LICENSE.txt
 */
#include <sstream>
#include <deque>
#include <iostream>
#include "halValidate.h"
#include "hal.h"

using namespace std;
using namespace hal;

// current implementation is poor and hacky.  should fix up to
// use iterators to properly scan the segments.

void hal::validateBottomSegment(const BottomSegment* bottomSegment)
{
  const Genome* genome = bottomSegment->getGenome();
  hal_index_t index = bottomSegment->getArrayIndex();
  if (index < 0 || index >= (hal_index_t)genome->getSequenceLength())
  {
    stringstream ss;
    ss << "Bottom segment out of range " << index << " in genome "
       << genome->getName();
    throw hal_exception(ss.str());
  }

  if (bottomSegment->getLength() < 1)
  {
    stringstream ss;
    ss << "Bottom segment " << index  << " in genome " << genome->getName()
       << " has length 0 which is not currently supported";
    throw hal_exception(ss.str());
  }

  hal_size_t numChildren = bottomSegment->getNumChildren();
  for (hal_size_t child = 0; child < numChildren; ++child)
  {
    const Genome* childGenome = genome->getChild(child);
    const hal_index_t childIndex = bottomSegment->getChildIndex(child);
    if (childGenome != NULL && childIndex != NULL_INDEX)
    {
      if (childIndex >= (hal_index_t)childGenome->getNumTopSegments())
      {
        stringstream ss;
        ss << "Child " << child << " index " <<childIndex << " of segment "
           << bottomSegment->getArrayIndex() << " out of range in genome "
           << childGenome->getName();
        throw hal_exception(ss.str());
      }
      TopSegmentIteratorConstPtr topSegmentIteratr =
         childGenome->getTopSegmentIterator(childIndex);
      const TopSegment* childSegment = topSegmentIteratr->getTopSegment();
      if (childSegment->getLength() != bottomSegment->getLength())
      {
        stringstream ss;
        ss << "Child " << child << " length of segment "
           << bottomSegment->getArrayIndex()
           << " in genome " << genome->getName() << " has length "
           << childSegment->getLength() << " which does not match "
           << bottomSegment->getLength();
        throw hal_exception(ss.str());
      }
      if (childSegment->getNextParalogyIndex() == NULL_INDEX &&
          childSegment->getParentIndex() != bottomSegment->getArrayIndex())
      {
        throw hal_exception("parent / child index mismatch (parent=" +
                            genome->getName() + " child=" +
                            childGenome->getName());
      }
    }
  }

  const hal_index_t parseIndex = bottomSegment->getTopParseIndex();
  if (parseIndex == NULL_INDEX)
  {
    if (genome->getParent() != NULL)
    {
      stringstream ss;
      ss << "Bottom segment " << bottomSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has null parse index";
      throw hal_exception(ss.str());
    }
  }
  else
  {
    if (parseIndex >= (hal_index_t)genome->getNumTopSegments())
    {
      stringstream ss;
      ss << "BottomSegment " << bottomSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has parse index " << parseIndex
         << " greater than the number of top segments, "
         << (hal_index_t)genome->getNumTopSegments();
      throw hal_exception(ss.str());
    }
    TopSegmentIteratorConstPtr parseIterator =
       genome->getTopSegmentIterator(parseIndex);
    const TopSegment* parseSegment = parseIterator->getTopSegment();
    hal_offset_t parseOffset = bottomSegment->getTopParseOffset();
    if (parseOffset >= parseSegment->getLength())
    {
      stringstream ss;
      ss << "BottomSegment " << bottomSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has parse offset, " << parseOffset
         << ", greater than the length of the segment, "
         << parseSegment->getLength();
      throw hal_exception(ss.str());
    }
    if ((hal_index_t)parseOffset + parseSegment->getStartPosition() !=
        bottomSegment->getStartPosition())
    {
      throw hal_exception("parse index broken in bottom segment in genome " +
                          genome->getName());

    }
  }
}

void hal::validateTopSegment(const TopSegment* topSegment)
{
  const Genome* genome = topSegment->getGenome();
  hal_index_t index = topSegment->getArrayIndex();
  if (index < 0 || index >= (hal_index_t)genome->getSequenceLength())
  {
    stringstream ss;
    ss << "Segment out of range " << index << " in genome "
       << genome->getName();
    throw hal_exception(ss.str());
  }

  if (topSegment->getLength() < 1)
  {
    stringstream ss;
    ss << "Top segment " << index  << " in genome " << genome->getName()
       << " has length 0 which is not currently supported";
    throw hal_exception(ss.str());
  }

  const Genome* parentGenome = genome->getParent();
  const hal_index_t parentIndex = topSegment->getParentIndex();
  if (parentGenome != NULL && parentIndex != NULL_INDEX)
  {
    if (parentIndex >= (hal_index_t)parentGenome->getNumBottomSegments())
    {
      stringstream ss;
      ss << "Parent index " << parentIndex << " of segment "
         << topSegment->getArrayIndex() << " out of range in genome "
         << parentGenome->getName();
      throw hal_exception(ss.str());
    }
    BottomSegmentIteratorConstPtr bottomSegmentIterator =
       parentGenome->getBottomSegmentIterator(parentIndex);
    const BottomSegment* parentSegment =
       bottomSegmentIterator->getBottomSegment();
    if (topSegment->getLength() != parentSegment->getLength())
    {
      stringstream ss;
      ss << "Parent length of segment " << topSegment->getArrayIndex()
         << " in genome " << genome->getName() << " has length "
         << parentSegment->getLength() << " which does not match "
         << topSegment->getLength();
      throw hal_exception(ss.str());
    }
  }

  const hal_index_t parseIndex = topSegment->getBottomParseIndex();
  if (parseIndex == NULL_INDEX)
  {
    if (genome->getNumChildren() != 0)
    {
      stringstream ss;
      ss << "Top Segment " << topSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has null parse index";
      throw hal_exception(ss.str());
    }
  }
  else
  {
    if (parseIndex >= (hal_index_t)genome->getNumBottomSegments())
    {
      stringstream ss;
      ss << "Top Segment " << topSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has parse index out of range";
      throw hal_exception(ss.str());
    }
    hal_offset_t parseOffset = topSegment->getBottomParseOffset();
    BottomSegmentIteratorConstPtr bottomSegmentIterator =
       genome->getBottomSegmentIterator(parseIndex);
    const BottomSegment* parseSegment =
       bottomSegmentIterator->getBottomSegment();
    if (parseOffset >= parseSegment->getLength())
    {
      stringstream ss;
      ss << "Top Segment " << topSegment->getArrayIndex() << " in genome "
         << genome->getName() << " has parse offset out of range";
      throw hal_exception(ss.str());
    }
    if ((hal_index_t)parseOffset + parseSegment->getStartPosition() !=
        topSegment->getStartPosition())
    {
      throw hal_exception("parse index broken in top segment in genome " +
                          genome->getName());

    }
  }

  const hal_index_t paralogyIndex = topSegment->getNextParalogyIndex();
  if (paralogyIndex != NULL_INDEX)
  {
    TopSegmentIteratorConstPtr pti =
       genome->getTopSegmentIterator(paralogyIndex);
    if (pti->getTopSegment()->getParentIndex() != topSegment->getParentIndex())
    {
      stringstream ss;
      ss << "Top segment " << topSegment->getArrayIndex() << " has parent index "
         << topSegment->getParentIndex() << ", but next paraglog "
         << topSegment->getNextParalogyIndex() << " has parent Index "
         << pti->getTopSegment()->getParentIndex()
         << ". Paralogous top segments must share same parent.";
      throw hal_exception(ss.str());
    }
  }
}

void hal::validateSequence(const Sequence* sequence)
{
  // Verify that the DNA sequence doesn't contain funny characters
  DNAIteratorConstPtr dnaIt = sequence->getDNAIterator();
  hal_size_t length = sequence->getSequenceLength();
  for (hal_size_t i = 0; i < length; ++i)
  {
    hal_dna_t c = dnaIt->getChar();
    if (isNucleotide(c) == false)
    {
      stringstream ss;
      ss << "Non-nucleotide character discoverd at position "
         << i << " of sequence " << sequence->getName() << ": " << c;
      throw hal_exception(ss.str());
    }
  }

  // Check the top segments
  if (sequence->getGenome()->getParent() != NULL)
  {
    hal_size_t totalTopLength = 0;
    TopSegmentIteratorConstPtr topIt = sequence->getTopSegmentIterator();
    hal_size_t numTopSegments = sequence->getNumTopSegments();
    for (hal_size_t i = 0; i < numTopSegments; ++i)
    {
      const TopSegment* topSegment = topIt->getTopSegment();
      validateTopSegment(topSegment);
      totalTopLength += topSegment->getLength();
      topIt->toRight();
    }
    if (totalTopLength != length)
    {
      stringstream ss;
      ss << "Sequence " << sequence->getName() << " has length " << length
         << " but its top segments add up to " << totalTopLength;
      throw hal_exception(ss.str());
    }
  }

  // Check the bottom segments
  if (sequence->getGenome()->getNumChildren() > 0)
  {
    hal_size_t totalBottomLength = 0;
    BottomSegmentIteratorConstPtr bottomIt =
       sequence->getBottomSegmentIterator();
    hal_size_t numBottomSegments = sequence->getNumBottomSegments();
    for (hal_size_t i = 0; i < numBottomSegments; ++i)
    {
      const BottomSegment* bottomSegment = bottomIt->getBottomSegment();
      validateBottomSegment(bottomSegment);
      totalBottomLength += bottomSegment->getLength();
      bottomIt->toRight();
    }
    if (totalBottomLength != length)
    {
      stringstream ss;
      ss << "Sequence " << sequence->getName() << " has length " << length
         << " but its bottom segments add up to " << totalBottomLength;
      throw hal_exception(ss.str());
    }
  }
}

void hal::validateGenome(const Genome* genome)
{
  // first we check the sequence coverage
  hal_size_t totalTop = 0;
  hal_size_t totalBottom = 0;
  hal_size_t totalLength = 0;

  SequenceIteratorConstPtr seqIt = genome->getSequenceIterator();
  hal_size_t numSequences = genome->getNumSequences();
  for (hal_size_t i = 0; i < numSequences; ++i)
  {
    const Sequence* sequence = seqIt->getSequence();
    validateSequence(sequence);

    totalTop += sequence->getNumTopSegments();
    totalBottom += sequence->getNumBottomSegments();
    totalLength += sequence->getSequenceLength();

    // make sure it doesn't overlap any other sequences;
    const Sequence* s1 =
       genome->getSequenceBySite(sequence->getStartPosition());
    if (s1 == NULL || s1->getName() != sequence->getName())
    {
      stringstream ss;
      ss << "Sequence " << sequence->getName() << " has a bad overlap in "
         << genome->getName();
      throw hal_exception(ss.str());
    }
    const Sequence* s2 =
       genome->getSequenceBySite(sequence->getStartPosition() +
                                 sequence->getSequenceLength() - 1);
    if (s2 == NULL || s2->getName() != sequence->getName())
    {
      stringstream ss;
      ss << "Sequence " << sequence->getName() << " has a bad overlap in "
         << genome->getName();
      throw hal_exception(ss.str());
    }
  }

  hal_size_t genomeLength = genome->getSequenceLength();
  hal_size_t genomeTop = genome->getNumTopSegments();
  hal_size_t genomeBottom = genome->getNumBottomSegments();

  if (genomeLength != totalLength)
  {
    stringstream ss;
    ss << "Problem: genome has length " << genomeLength
       << "But sequences total " << totalLength;
    throw hal_exception(ss.str());
  }
  if (genomeTop != totalTop)
  {
    stringstream ss;
    ss << "Problem: genome has " << genomeTop << " top segments but "
       << "sequences have " << totalTop << " top segments";
    throw ss.str();
  }
  if (genomeBottom != totalBottom)
  {
    stringstream ss;
    ss << "Problem: genome has " << genomeBottom << " bottom segments but "
       << "sequences have " << totalBottom << " bottom segments";
    throw hal_exception(ss.str());
  }
}

void hal::validateAlignment(AlignmentConstPtr alignment)
{
  deque<string> bfQueue;
  bfQueue.push_back(alignment->getRootName());
  while (bfQueue.empty() == false)
  {
    string name = bfQueue.back();
    bfQueue.pop_back();
    if (name.empty() == false)
    {
      const Genome* genome = alignment->openGenome(name);
      if (genome == NULL)
      {
        throw hal_exception("Failure to open genome " + name);
      }
      validateGenome(genome);
      vector<string> childNames = alignment->getChildNames(name);
      for (size_t i = 0; i < childNames.size(); ++i)
      {
        bfQueue.push_front(childNames[i]);
      }
    }
  }
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright (C) 2016 ScyllaDB
 *
 * Modified by ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "auth/default_authorizer.hh"

extern "C" {
#include <crypt.h>
#include <unistd.h>
}

#include <chrono>
#include <random>

#include <boost/algorithm/string/join.hpp>
#include <boost/range.hpp>
#include <seastar/core/reactor.hh>

#include "auth/authenticated_user.hh"
#include "auth/common.hh"
#include "auth/permission.hh"
#include "cql3/query_processor.hh"
#include "cql3/untyped_result_set.hh"
#include "exceptions/exceptions.hh"
#include "log.hh"

namespace auth {

const sstring& default_authorizer_name() {
    static const sstring name = meta::AUTH_PACKAGE_NAME + "CassandraAuthorizer";
    return name;
}

static const sstring ROLE_NAME = "role";
static const sstring RESOURCE_NAME = "resource";
static const sstring PERMISSIONS_NAME = "permissions";
static const sstring PERMISSIONS_CF = "role_permissions";

static logging::logger alogger("default_authorizer");

// To ensure correct initialization order, we unfortunately need to use a string literal.
static const class_registrator<
        authorizer,
        default_authorizer,
        cql3::query_processor&,
        ::service::migration_manager&> password_auth_reg("org.apache.cassandra.auth.CassandraAuthorizer");

default_authorizer::default_authorizer(cql3::query_processor& qp, ::service::migration_manager& mm)
        : _qp(qp)
        , _migration_manager(mm) {
}

default_authorizer::~default_authorizer() {
}

future<> default_authorizer::start() {
    static const sstring create_table = sprint(
            "CREATE TABLE %s.%s ("
            "%s text,"
            "%s text,"
            "%s set<text>,"
            "PRIMARY KEY(%s, %s)"
            ") WITH gc_grace_seconds=%d",
            meta::AUTH_KS,
            PERMISSIONS_CF,
            ROLE_NAME,
            RESOURCE_NAME,
            PERMISSIONS_NAME,
            ROLE_NAME,
            RESOURCE_NAME,
            90 * 24 * 60 * 60); // 3 months.

    return once_among_shards([this] {
        return create_metadata_table_if_missing(
                PERMISSIONS_CF,
                _qp,
                create_table,
                _migration_manager);
    });
}

future<> default_authorizer::stop() {
    return make_ready_future<>();
}

future<permission_set> default_authorizer::authorize_role_directly(
        stdx::string_view role_name,
        const resource& r,
        const service& ser) const {
    return ser.has_superuser(role_name).then([this, role_name, &r](bool has_superuser) {
        if (has_superuser) {
            return make_ready_future<permission_set>(r.applicable_permissions());
        }

        static const sstring query = sprint(
                "SELECT %s FROM %s.%s WHERE %s = ? AND %s = ?",
                PERMISSIONS_NAME,
                meta::AUTH_KS,
                PERMISSIONS_CF,
                ROLE_NAME,
                RESOURCE_NAME);

        return _qp.process(
                query,
                db::consistency_level::LOCAL_ONE,
                {sstring(role_name), r.name()}).then([](::shared_ptr<cql3::untyped_result_set> results) {
            if (results->empty()) {
                return permissions::NONE;
            }

            return permissions::from_strings(results->one().get_set<sstring>(PERMISSIONS_NAME));
        });
    });
}

future<permission_set> default_authorizer::authorize(
        stdx::string_view role_name,
        const resource& r,
        service& ser) const {
    return do_with(permission_set(), [this, &ser, role_name, &r](auto& ps) {
        return ser.get_roles(role_name).then([this, &ser, &ps, &r](std::unordered_set<sstring> all_roles) {
            return do_with(std::move(all_roles), [this, &ser, &ps, &r](const auto& all_roles) {
                return parallel_for_each(all_roles, [this, &ser, &ps, &r](stdx::string_view role_name) {
                    return this->authorize_role_directly(role_name, r, ser).then([&ps](permission_set rp) {
                        ps = permission_set::from_mask(ps.mask() | rp.mask());
                    });
                });
            });
        }).then([&ps] {
            return ps;
        });
    });
}

future<>
default_authorizer::modify(
        stdx::string_view role_name,
        permission_set set,
        const resource& resource,
        stdx::string_view op) {
    // TODO: why does this not check super user?
    auto query = sprint(
            "UPDATE %s.%s SET %s = %s %s ? WHERE %s = ? AND %s = ?",
            meta::AUTH_KS,
            PERMISSIONS_CF,
            PERMISSIONS_NAME,
            PERMISSIONS_NAME,
            op,
            ROLE_NAME,
            RESOURCE_NAME);

    return _qp.process(
            query,
            db::consistency_level::ONE,
            {permissions::to_strings(set), sstring(role_name), resource.name()}).discard_result();
}

future<> default_authorizer::grant(stdx::string_view role_name, permission_set set, const resource& resource) {
    return modify(role_name, std::move(set), resource, "+");
}

future<> default_authorizer::revoke(stdx::string_view role_name, permission_set set, const resource& resource) {
    return modify(role_name, std::move(set), resource, "-");
}

future<std::vector<permission_details>> default_authorizer::list(
        permission_set set,
        const std::optional<resource>& resource,
        const std::optional<stdx::string_view>& role_name,
        service& ser) const {
    sstring query = sprint(
            "SELECT %s, %s, %s FROM %s.%s",
            ROLE_NAME,
            RESOURCE_NAME,
            PERMISSIONS_NAME,
            meta::AUTH_KS,
            PERMISSIONS_CF);

    // Oh, look, it is a case where it does not pay off to have
    // parameters to process in an initializer list.
    future<::shared_ptr<cql3::untyped_result_set>> f = make_ready_future<::shared_ptr<cql3::untyped_result_set>>();

    if (role_name) {
        f = ser.get_roles(*role_name).then([this, &resource, query, &f](
                std::unordered_set<sstring> all_roles) mutable {
            if (resource) {
                query += sprint(" WHERE %s IN ? AND %s = ?", ROLE_NAME, RESOURCE_NAME);
                return _qp.process(query, db::consistency_level::ONE, {all_roles, resource->name()});
            }

            query += sprint(" WHERE %s IN ?", ROLE_NAME);
            return _qp.process(query, db::consistency_level::ONE, {all_roles});
        });
    } else if (resource) {
        query += sprint(" WHERE %s = ? ALLOW FILTERING", RESOURCE_NAME);
        f = _qp.process(query, db::consistency_level::ONE, {resource->name()});
    } else {
        f = _qp.process(query, db::consistency_level::ONE, {});
    }

    return f.then([set](::shared_ptr<cql3::untyped_result_set> res) {
        std::vector<permission_details> result;

        for (auto& row : *res) {
            if (row.has(PERMISSIONS_NAME)) {
                auto username = row.get_as<sstring>(ROLE_NAME);
                auto resource = parse_resource(row.get_as<sstring>(RESOURCE_NAME));
                auto ps = permissions::from_strings(row.get_set<sstring>(PERMISSIONS_NAME));
                ps = permission_set::from_mask(ps.mask() & set.mask());

                result.emplace_back(permission_details {username, resource, ps});
            }
        }
        return make_ready_future<std::vector<permission_details>>(std::move(result));
    });
}

future<> default_authorizer::revoke_all(stdx::string_view role_name) {
    auto query = sprint(
            "DELETE FROM %s.%s WHERE %s = ?",
            meta::AUTH_KS,
            PERMISSIONS_CF,
            ROLE_NAME);

    return _qp.process(
            query,
            db::consistency_level::ONE,
            {sstring(role_name)}).discard_result().handle_exception([role_name](auto ep) {
        try {
            std::rethrow_exception(ep);
        } catch (exceptions::request_execution_exception& e) {
            alogger.warn("CassandraAuthorizer failed to revoke all permissions of {}: {}", role_name, e);
        }
    });
}

future<> default_authorizer::revoke_all(const resource& resource) {
    auto query = sprint(
            "SELECT %s FROM %s.%s WHERE %s = ? ALLOW FILTERING",
            ROLE_NAME,
            meta::AUTH_KS,
            PERMISSIONS_CF,
            RESOURCE_NAME);

    return _qp.process(
            query,
            db::consistency_level::LOCAL_ONE,
            {resource.name()}).then_wrapped([this, resource](future<::shared_ptr<cql3::untyped_result_set>> f) {
        try {
            auto res = f.get0();
            return parallel_for_each(
                    res->begin(),
                    res->end(),
                    [this, res, resource](const cql3::untyped_result_set::row& r) {
                auto query = sprint(
                        "DELETE FROM %s.%s WHERE %s = ? AND %s = ?",
                        meta::AUTH_KS,
                        PERMISSIONS_CF,
                        ROLE_NAME,
                        RESOURCE_NAME);

                return _qp.process(
                        query,
                        db::consistency_level::LOCAL_ONE,
                        {r.get_as<sstring>(ROLE_NAME), resource.name()}).discard_result().handle_exception(
                                [resource](auto ep) {
                    try {
                        std::rethrow_exception(ep);
                    } catch (exceptions::request_execution_exception& e) {
                        alogger.warn("CassandraAuthorizer failed to revoke all permissions on {}: {}", resource, e);
                    }

                });
            });
        } catch (exceptions::request_execution_exception& e) {
            alogger.warn("CassandraAuthorizer failed to revoke all permissions on {}: {}", resource, e);
            return make_ready_future();
        }
    });
}

const resource_set& default_authorizer::protected_resources() const {
    static const resource_set resources({ make_data_resource(meta::AUTH_KS, PERMISSIONS_CF) });
    return resources;
}

}
<commit_msg>auth: Remove outdated "TODO"<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Copyright (C) 2016 ScyllaDB
 *
 * Modified by ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "auth/default_authorizer.hh"

extern "C" {
#include <crypt.h>
#include <unistd.h>
}

#include <chrono>
#include <random>

#include <boost/algorithm/string/join.hpp>
#include <boost/range.hpp>
#include <seastar/core/reactor.hh>

#include "auth/authenticated_user.hh"
#include "auth/common.hh"
#include "auth/permission.hh"
#include "cql3/query_processor.hh"
#include "cql3/untyped_result_set.hh"
#include "exceptions/exceptions.hh"
#include "log.hh"

namespace auth {

const sstring& default_authorizer_name() {
    static const sstring name = meta::AUTH_PACKAGE_NAME + "CassandraAuthorizer";
    return name;
}

static const sstring ROLE_NAME = "role";
static const sstring RESOURCE_NAME = "resource";
static const sstring PERMISSIONS_NAME = "permissions";
static const sstring PERMISSIONS_CF = "role_permissions";

static logging::logger alogger("default_authorizer");

// To ensure correct initialization order, we unfortunately need to use a string literal.
static const class_registrator<
        authorizer,
        default_authorizer,
        cql3::query_processor&,
        ::service::migration_manager&> password_auth_reg("org.apache.cassandra.auth.CassandraAuthorizer");

default_authorizer::default_authorizer(cql3::query_processor& qp, ::service::migration_manager& mm)
        : _qp(qp)
        , _migration_manager(mm) {
}

default_authorizer::~default_authorizer() {
}

future<> default_authorizer::start() {
    static const sstring create_table = sprint(
            "CREATE TABLE %s.%s ("
            "%s text,"
            "%s text,"
            "%s set<text>,"
            "PRIMARY KEY(%s, %s)"
            ") WITH gc_grace_seconds=%d",
            meta::AUTH_KS,
            PERMISSIONS_CF,
            ROLE_NAME,
            RESOURCE_NAME,
            PERMISSIONS_NAME,
            ROLE_NAME,
            RESOURCE_NAME,
            90 * 24 * 60 * 60); // 3 months.

    return once_among_shards([this] {
        return create_metadata_table_if_missing(
                PERMISSIONS_CF,
                _qp,
                create_table,
                _migration_manager);
    });
}

future<> default_authorizer::stop() {
    return make_ready_future<>();
}

future<permission_set> default_authorizer::authorize_role_directly(
        stdx::string_view role_name,
        const resource& r,
        const service& ser) const {
    return ser.has_superuser(role_name).then([this, role_name, &r](bool has_superuser) {
        if (has_superuser) {
            return make_ready_future<permission_set>(r.applicable_permissions());
        }

        static const sstring query = sprint(
                "SELECT %s FROM %s.%s WHERE %s = ? AND %s = ?",
                PERMISSIONS_NAME,
                meta::AUTH_KS,
                PERMISSIONS_CF,
                ROLE_NAME,
                RESOURCE_NAME);

        return _qp.process(
                query,
                db::consistency_level::LOCAL_ONE,
                {sstring(role_name), r.name()}).then([](::shared_ptr<cql3::untyped_result_set> results) {
            if (results->empty()) {
                return permissions::NONE;
            }

            return permissions::from_strings(results->one().get_set<sstring>(PERMISSIONS_NAME));
        });
    });
}

future<permission_set> default_authorizer::authorize(
        stdx::string_view role_name,
        const resource& r,
        service& ser) const {
    return do_with(permission_set(), [this, &ser, role_name, &r](auto& ps) {
        return ser.get_roles(role_name).then([this, &ser, &ps, &r](std::unordered_set<sstring> all_roles) {
            return do_with(std::move(all_roles), [this, &ser, &ps, &r](const auto& all_roles) {
                return parallel_for_each(all_roles, [this, &ser, &ps, &r](stdx::string_view role_name) {
                    return this->authorize_role_directly(role_name, r, ser).then([&ps](permission_set rp) {
                        ps = permission_set::from_mask(ps.mask() | rp.mask());
                    });
                });
            });
        }).then([&ps] {
            return ps;
        });
    });
}

future<>
default_authorizer::modify(
        stdx::string_view role_name,
        permission_set set,
        const resource& resource,
        stdx::string_view op) {
    auto query = sprint(
            "UPDATE %s.%s SET %s = %s %s ? WHERE %s = ? AND %s = ?",
            meta::AUTH_KS,
            PERMISSIONS_CF,
            PERMISSIONS_NAME,
            PERMISSIONS_NAME,
            op,
            ROLE_NAME,
            RESOURCE_NAME);

    return _qp.process(
            query,
            db::consistency_level::ONE,
            {permissions::to_strings(set), sstring(role_name), resource.name()}).discard_result();
}

future<> default_authorizer::grant(stdx::string_view role_name, permission_set set, const resource& resource) {
    return modify(role_name, std::move(set), resource, "+");
}

future<> default_authorizer::revoke(stdx::string_view role_name, permission_set set, const resource& resource) {
    return modify(role_name, std::move(set), resource, "-");
}

future<std::vector<permission_details>> default_authorizer::list(
        permission_set set,
        const std::optional<resource>& resource,
        const std::optional<stdx::string_view>& role_name,
        service& ser) const {
    sstring query = sprint(
            "SELECT %s, %s, %s FROM %s.%s",
            ROLE_NAME,
            RESOURCE_NAME,
            PERMISSIONS_NAME,
            meta::AUTH_KS,
            PERMISSIONS_CF);

    // Oh, look, it is a case where it does not pay off to have
    // parameters to process in an initializer list.
    future<::shared_ptr<cql3::untyped_result_set>> f = make_ready_future<::shared_ptr<cql3::untyped_result_set>>();

    if (role_name) {
        f = ser.get_roles(*role_name).then([this, &resource, query, &f](
                std::unordered_set<sstring> all_roles) mutable {
            if (resource) {
                query += sprint(" WHERE %s IN ? AND %s = ?", ROLE_NAME, RESOURCE_NAME);
                return _qp.process(query, db::consistency_level::ONE, {all_roles, resource->name()});
            }

            query += sprint(" WHERE %s IN ?", ROLE_NAME);
            return _qp.process(query, db::consistency_level::ONE, {all_roles});
        });
    } else if (resource) {
        query += sprint(" WHERE %s = ? ALLOW FILTERING", RESOURCE_NAME);
        f = _qp.process(query, db::consistency_level::ONE, {resource->name()});
    } else {
        f = _qp.process(query, db::consistency_level::ONE, {});
    }

    return f.then([set](::shared_ptr<cql3::untyped_result_set> res) {
        std::vector<permission_details> result;

        for (auto& row : *res) {
            if (row.has(PERMISSIONS_NAME)) {
                auto username = row.get_as<sstring>(ROLE_NAME);
                auto resource = parse_resource(row.get_as<sstring>(RESOURCE_NAME));
                auto ps = permissions::from_strings(row.get_set<sstring>(PERMISSIONS_NAME));
                ps = permission_set::from_mask(ps.mask() & set.mask());

                result.emplace_back(permission_details {username, resource, ps});
            }
        }
        return make_ready_future<std::vector<permission_details>>(std::move(result));
    });
}

future<> default_authorizer::revoke_all(stdx::string_view role_name) {
    auto query = sprint(
            "DELETE FROM %s.%s WHERE %s = ?",
            meta::AUTH_KS,
            PERMISSIONS_CF,
            ROLE_NAME);

    return _qp.process(
            query,
            db::consistency_level::ONE,
            {sstring(role_name)}).discard_result().handle_exception([role_name](auto ep) {
        try {
            std::rethrow_exception(ep);
        } catch (exceptions::request_execution_exception& e) {
            alogger.warn("CassandraAuthorizer failed to revoke all permissions of {}: {}", role_name, e);
        }
    });
}

future<> default_authorizer::revoke_all(const resource& resource) {
    auto query = sprint(
            "SELECT %s FROM %s.%s WHERE %s = ? ALLOW FILTERING",
            ROLE_NAME,
            meta::AUTH_KS,
            PERMISSIONS_CF,
            RESOURCE_NAME);

    return _qp.process(
            query,
            db::consistency_level::LOCAL_ONE,
            {resource.name()}).then_wrapped([this, resource](future<::shared_ptr<cql3::untyped_result_set>> f) {
        try {
            auto res = f.get0();
            return parallel_for_each(
                    res->begin(),
                    res->end(),
                    [this, res, resource](const cql3::untyped_result_set::row& r) {
                auto query = sprint(
                        "DELETE FROM %s.%s WHERE %s = ? AND %s = ?",
                        meta::AUTH_KS,
                        PERMISSIONS_CF,
                        ROLE_NAME,
                        RESOURCE_NAME);

                return _qp.process(
                        query,
                        db::consistency_level::LOCAL_ONE,
                        {r.get_as<sstring>(ROLE_NAME), resource.name()}).discard_result().handle_exception(
                                [resource](auto ep) {
                    try {
                        std::rethrow_exception(ep);
                    } catch (exceptions::request_execution_exception& e) {
                        alogger.warn("CassandraAuthorizer failed to revoke all permissions on {}: {}", resource, e);
                    }

                });
            });
        } catch (exceptions::request_execution_exception& e) {
            alogger.warn("CassandraAuthorizer failed to revoke all permissions on {}: {}", resource, e);
            return make_ready_future();
        }
    });
}

const resource_set& default_authorizer::protected_resources() const {
    static const resource_set resources({ make_data_resource(meta::AUTH_KS, PERMISSIONS_CF) });
    return resources;
}

}
<|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 "net/base/keygen_handler.h"

#include <windows.h>
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")
#include <rpc.h>
#pragma comment(lib, "rpcrt4.lib")

#include <list>
#include <string>
#include <vector>

#include "base/base64.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/string_piece.h"
#include "base/utf_string_conversions.h"

namespace net {

// TODO(rsleevi): The following encoding functions are adapted from
// base/crypto/rsa_private_key.h and can/should probably be refactored.
static const uint8 kSequenceTag = 0x30;

void PrependLength(size_t size, std::list<BYTE>* data) {
  // The high bit is used to indicate whether additional octets are needed to
  // represent the length.
  if (size < 0x80) {
    data->push_front(static_cast<BYTE>(size));
  } else {
    uint8 num_bytes = 0;
    while (size > 0) {
      data->push_front(static_cast<BYTE>(size & 0xFF));
      size >>= 8;
      num_bytes++;
    }
    CHECK_LE(num_bytes, 4);
    data->push_front(0x80 | num_bytes);
  }
}

void PrependTypeHeaderAndLength(uint8 type, uint32 length,
                                std::vector<BYTE>* output) {
  std::list<BYTE> type_and_length;

  PrependLength(length, &type_and_length);
  type_and_length.push_front(type);

  output->insert(output->begin(), type_and_length.begin(),
                 type_and_length.end());
}

bool EncodeAndAppendType(LPCSTR type, const void* to_encode,
                         std::vector<BYTE>* output) {
  BOOL ok;
  DWORD size = 0;
  ok = CryptEncodeObject(X509_ASN_ENCODING, type, to_encode, NULL, &size);
  DCHECK(ok);
  if (!ok)
    return false;

  std::vector<BYTE>::size_type old_size = output->size();
  output->resize(old_size + size);

  ok = CryptEncodeObject(X509_ASN_ENCODING, type, to_encode,
                         &(*output)[old_size], &size);
  DCHECK(ok);
  if (!ok)
    return false;

  // Sometimes the initial call to CryptEncodeObject gave a generous estimate
  // of the size, so shrink back to what was actually used.
  output->resize(old_size + size);

  return true;
}

// Appends a DER IA5String containing |challenge| to |output|.
// Returns true if encoding was successful.
bool EncodeChallenge(const std::string& challenge, std::vector<BYTE>* output) {
  CERT_NAME_VALUE challenge_nv;
  challenge_nv.dwValueType = CERT_RDN_IA5_STRING;
  challenge_nv.Value.pbData = const_cast<BYTE*>(
      reinterpret_cast<const BYTE*>(challenge.data()));
  challenge_nv.Value.cbData = challenge.size();

  return EncodeAndAppendType(X509_ANY_STRING, &challenge_nv, output);
}

// Appends a DER SubjectPublicKeyInfo structure for the signing key in |prov|
// to |output|.
// Returns true if encoding was successful.
bool EncodeSubjectPublicKeyInfo(HCRYPTPROV prov, std::vector<BYTE>* output) {
  BOOL ok;
  DWORD size = 0;

  // From the private key stored in HCRYPTPROV, obtain the public key, stored
  // as a CERT_PUBLIC_KEY_INFO structure. Currently, only RSA public keys are
  // supported.
  ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
                                  szOID_RSA_RSA, 0, NULL, NULL, &size);
  DCHECK(ok);
  if (!ok)
    return false;

  std::vector<BYTE> public_key_info(size);
  PCERT_PUBLIC_KEY_INFO public_key_casted =
      reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&public_key_info[0]);
  ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
                                  szOID_RSA_RSA, 0, NULL, public_key_casted,
                                  &size);
  DCHECK(ok);
  if (!ok)
    return false;

  public_key_info.resize(size);

  return EncodeAndAppendType(X509_PUBLIC_KEY_INFO, &public_key_info[0],
                             output);
}

// Generates an ASN.1 DER representation of the PublicKeyAndChallenge structure
// from the signing key of |prov| and the specified |challenge| and appends it
// to |output|.
// True if the the encoding was successfully generated.
bool GetPublicKeyAndChallenge(HCRYPTPROV prov, const std::string& challenge,
                              std::vector<BYTE>* output) {
  if (!EncodeSubjectPublicKeyInfo(prov, output) ||
      !EncodeChallenge(challenge, output)) {
    return false;
  }

  PrependTypeHeaderAndLength(kSequenceTag, output->size(), output);
  return true;
}

// Generates a DER encoded SignedPublicKeyAndChallenge structure from the
// signing key of |prov| and the specified |challenge| string and appends it
// to |output|.
// True if the encoding was successfully generated.
bool GetSignedPublicKeyAndChallenge(HCRYPTPROV prov,
                                    const std::string& challenge,
                                    std::string* output) {
  std::vector<BYTE> pkac;
  if (!GetPublicKeyAndChallenge(prov, challenge, &pkac))
    return false;

  std::vector<BYTE> signature;
  std::vector<BYTE> signed_pkac;
  DWORD size = 0;
  BOOL ok;

  // While the MSDN documentation states that CERT_SIGNED_CONTENT_INFO should
  // be an X.509 certificate type, for encoding this is not necessary. The
  // result of encoding this structure will be a DER-encoded structure with
  // the ASN.1 equivalent of
  //  ::= SEQUENCE {
  //    ToBeSigned IMPLICIT OCTET STRING,
  //    SignatureAlgorithm AlgorithmIdentifier,
  //    Signature BIT STRING
  //  }
  //
  // This happens to be the same naive type as an SPKAC, so this works.
  CERT_SIGNED_CONTENT_INFO info;
  info.ToBeSigned.cbData = pkac.size();
  info.ToBeSigned.pbData = &pkac[0];
  info.SignatureAlgorithm.pszObjId = szOID_RSA_MD5RSA;
  info.SignatureAlgorithm.Parameters.cbData = 0;
  info.SignatureAlgorithm.Parameters.pbData = NULL;

  ok = CryptSignCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
                            info.ToBeSigned.pbData, info.ToBeSigned.cbData,
                            &info.SignatureAlgorithm, NULL, NULL, &size);
  DCHECK(ok);
  if (!ok)
    return false;

  signature.resize(size);
  info.Signature.cbData = signature.size();
  info.Signature.pbData = &signature[0];
  info.Signature.cUnusedBits = 0;

  ok = CryptSignCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
                            info.ToBeSigned.pbData, info.ToBeSigned.cbData,
                            &info.SignatureAlgorithm, NULL,
                            info.Signature.pbData, &info.Signature.cbData);
  DCHECK(ok);
  if (!ok || !EncodeAndAppendType(X509_CERT, &info, &signed_pkac))
    return false;

  output->assign(reinterpret_cast<char*>(&signed_pkac[0]),
                 signed_pkac.size());

  return true;
}

// Generates a unique name for the container which will store the key that is
// generated. The traditional Windows approach is to use a GUID here.
std::wstring GetNewKeyContainerId() {
  RPC_STATUS status = RPC_S_OK;
  std::wstring result;

  UUID id = { 0 };
  status = UuidCreateSequential(&id);
  if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY)
    return result;

  RPC_WSTR rpc_string = NULL;
  status = UuidToString(&id, &rpc_string);
  if (status != RPC_S_OK)
    return result;

  // RPC_WSTR is unsigned short*.  wchar_t is a built-in type of Visual C++,
  // so the type cast is necessary.
  result.assign(reinterpret_cast<wchar_t*>(rpc_string));
  RpcStringFree(&rpc_string);

  return result;
}

void StoreKeyLocationInCache(HCRYPTPROV prov) {
  BOOL ok;
  DWORD size = 0;

  // Though it is known the container and provider name, as they are supplied
  // during GenKeyAndSignChallenge, explicitly resolving them via
  // CryptGetProvParam ensures that any defaults (such as provider name being
  // NULL) or any CSP modifications to the container name are properly
  // reflected.

  // Find the container name. Though the MSDN documentation states it will
  // return the exact same value as supplied when the provider was aquired, it
  // also notes the return type will be CHAR, /not/ WCHAR.
  ok = CryptGetProvParam(prov, PP_CONTAINER, NULL, &size, 0);
  if (!ok)
    return;

  std::vector<BYTE> buffer(size);
  ok = CryptGetProvParam(prov, PP_CONTAINER, &buffer[0], &size, 0);
  if (!ok)
    return;

  KeygenHandler::KeyLocation key_location;
  UTF8ToWide(reinterpret_cast<char*>(&buffer[0]), size,
             &key_location.container_name);

  // Get the provider name. This will always resolve, even if NULL (indicating
  // the default provider) was supplied to the CryptAcquireContext.
  size = 0;
  ok = CryptGetProvParam(prov, PP_NAME, NULL, &size, 0);
  if (!ok)
    return;

  buffer.resize(size);
  ok = CryptGetProvParam(prov, PP_NAME, &buffer[0], &size, 0);
  if (!ok)
    return;

  UTF8ToWide(reinterpret_cast<char*>(&buffer[0]), size,
             &key_location.provider_name);

  std::vector<BYTE> public_key_info;
  if (!EncodeSubjectPublicKeyInfo(prov, &public_key_info))
    return;

  KeygenHandler::Cache* cache = KeygenHandler::Cache::GetInstance();
  cache->Insert(std::string(public_key_info.begin(), public_key_info.end()),
                key_location);
}

bool KeygenHandler::KeyLocation::Equals(
    const KeygenHandler::KeyLocation& location) const {
  return container_name == location.container_name &&
         provider_name == location.provider_name;
}

std::string KeygenHandler::GenKeyAndSignChallenge() {
  std::string result;

  bool is_success = true;
  HCRYPTPROV prov = NULL;
  HCRYPTKEY key = NULL;
  DWORD flags = (key_size_in_bits_ << 16) | CRYPT_EXPORTABLE;
  std::string spkac;

  std::wstring new_key_id;

  // TODO(rsleevi): Have the user choose which provider they should use, which
  // needs to be filtered by those providers which can provide the key type
  // requested or the key size requested. This is especially important for
  // generating certificates that will be stored on smart cards.
  const int kMaxAttempts = 5;
  BOOL ok = FALSE;
  for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
    // Per MSDN documentation for CryptAcquireContext, if applications will be
    // creating their own keys, they should ensure unique naming schemes to
    // prevent overlap with any other applications or consumers of CSPs, and
    // *should not* store new keys within the default, NULL key container.
    new_key_id = GetNewKeyContainerId();
    if (new_key_id.empty())
      return result;

    // Only create new key containers, so that existing key containers are not
    // overwritten.
    ok = CryptAcquireContext(&prov, new_key_id.c_str(), NULL, PROV_RSA_FULL,
                             CRYPT_SILENT | CRYPT_NEWKEYSET);

    if (ok || GetLastError() != NTE_BAD_KEYSET)
      break;
  }
  if (!ok) {
    LOG(ERROR) << "Couldn't acquire a CryptoAPI provider context: "
               << GetLastError();
    is_success = false;
    goto failure;
  }

  if (!CryptGenKey(prov, CALG_RSA_KEYX, flags, &key)) {
    LOG(ERROR) << "Couldn't generate an RSA key";
    is_success = false;
    goto failure;
  }

  if (!GetSignedPublicKeyAndChallenge(prov, challenge_, &spkac)) {
    LOG(ERROR) << "Couldn't generate the signed public key and challenge";
    is_success = false;
    goto failure;
  }

  if (!base::Base64Encode(spkac, &result)) {
    LOG(ERROR) << "Couldn't convert signed key into base64";
    is_success = false;
    goto failure;
  }

  StoreKeyLocationInCache(prov);

 failure:
  if (!is_success) {
    LOG(ERROR) << "SSL Keygen failed";
  } else {
    LOG(INFO) << "SSL Key succeeded";
  }
  if (key) {
    // Securely destroys the handle, but leaves the underlying key alone. The
    // key can be obtained again by resolving the key location. If
    // |stores_key_| is false, the underlying key will be destroyed below.
    CryptDestroyKey(key);
  }

  if (prov) {
    CryptReleaseContext(prov, 0);
    prov = NULL;
    if (!stores_key_) {
      // Fully destroys any of the keys that were created and releases prov.
      CryptAcquireContext(&prov, new_key_id.c_str(), NULL, PROV_RSA_FULL,
                          CRYPT_SILENT | CRYPT_DELETEKEYSET);
    }
  }

  return result;
}

}  // namespace net
<commit_msg>Simplify the Windows <keygen> implementation by making better use of CryptoAPI.<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 "net/base/keygen_handler.h"

#include <windows.h>
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")
#include <rpc.h>
#pragma comment(lib, "rpcrt4.lib")

#include <list>
#include <string>
#include <vector>

#include "base/base64.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"

namespace net {

bool EncodeAndAppendType(LPCSTR type, const void* to_encode,
                         std::vector<BYTE>* output) {
  BOOL ok;
  DWORD size = 0;
  ok = CryptEncodeObject(X509_ASN_ENCODING, type, to_encode, NULL, &size);
  DCHECK(ok);
  if (!ok)
    return false;

  std::vector<BYTE>::size_type old_size = output->size();
  output->resize(old_size + size);

  ok = CryptEncodeObject(X509_ASN_ENCODING, type, to_encode,
                         &(*output)[old_size], &size);
  DCHECK(ok);
  if (!ok)
    return false;

  // Sometimes the initial call to CryptEncodeObject gave a generous estimate
  // of the size, so shrink back to what was actually used.
  output->resize(old_size + size);

  return true;
}

// Assigns the contents of a CERT_PUBLIC_KEY_INFO structure for the signing
// key in |prov| to |output|. Returns true if encoding was successful.
bool GetSubjectPublicKeyInfo(HCRYPTPROV prov, std::vector<BYTE>* output) {
  BOOL ok;
  DWORD size = 0;

  // From the private key stored in HCRYPTPROV, obtain the public key, stored
  // as a CERT_PUBLIC_KEY_INFO structure. Currently, only RSA public keys are
  // supported.
  ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
                                  szOID_RSA_RSA, 0, NULL, NULL, &size);
  DCHECK(ok);
  if (!ok)
    return false;

  output->resize(size);

  PCERT_PUBLIC_KEY_INFO public_key_casted =
      reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&(*output)[0]);
  ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
                                  szOID_RSA_RSA, 0, NULL, public_key_casted,
                                  &size);
  DCHECK(ok);
  if (!ok)
    return false;

  output->resize(size);

  return true;
}

// Appends a DER SubjectPublicKeyInfo structure for the signing key in |prov|
// to |output|.
// Returns true if encoding was successful.
bool EncodeSubjectPublicKeyInfo(HCRYPTPROV prov, std::vector<BYTE>* output) {
  std::vector<BYTE> public_key_info;
  if (!GetSubjectPublicKeyInfo(prov, &public_key_info))
    return false;

  return EncodeAndAppendType(X509_PUBLIC_KEY_INFO, &public_key_info[0],
                             output);
}

// Generates a DER encoded SignedPublicKeyAndChallenge structure from the
// signing key of |prov| and the specified ASCII |challenge| string and
// appends it to |output|.
// True if the encoding was successfully generated.
bool GetSignedPublicKeyAndChallenge(HCRYPTPROV prov,
                                    const std::string& challenge,
                                    std::string* output) {
  std::wstring wide_challenge = ASCIIToWide(challenge);
  std::vector<BYTE> spki;

  if (!GetSubjectPublicKeyInfo(prov, &spki))
    return false;

  // PublicKeyAndChallenge ::= SEQUENCE {
  //     spki SubjectPublicKeyInfo,
  //     challenge IA5STRING
  // }
  CERT_KEYGEN_REQUEST_INFO pkac;
  pkac.dwVersion = CERT_KEYGEN_REQUEST_V1;
  pkac.SubjectPublicKeyInfo =
      *reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&spki[0]);
  pkac.pwszChallengeString = const_cast<wchar_t*>(wide_challenge.c_str());

  CRYPT_ALGORITHM_IDENTIFIER sig_alg;
  memset(&sig_alg, 0, sizeof(sig_alg));
  sig_alg.pszObjId = szOID_RSA_MD5RSA;

  BOOL ok;
  DWORD size = 0;
  std::vector<BYTE> signed_pkac;
  ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
                                     X509_KEYGEN_REQUEST_TO_BE_SIGNED,
                                     &pkac, &sig_alg, NULL,
                                     NULL, &size);
  DCHECK(ok);
  if (!ok)
    return false;

  signed_pkac.resize(size);
  ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
                                     X509_KEYGEN_REQUEST_TO_BE_SIGNED,
                                     &pkac, &sig_alg, NULL,
                                     &signed_pkac[0], &size);
  DCHECK(ok);
  if (!ok)
    return false;

  output->assign(reinterpret_cast<char*>(&signed_pkac[0]), size);
  return true;
}

// Generates a unique name for the container which will store the key that is
// generated. The traditional Windows approach is to use a GUID here.
std::wstring GetNewKeyContainerId() {
  RPC_STATUS status = RPC_S_OK;
  std::wstring result;

  UUID id = { 0 };
  status = UuidCreateSequential(&id);
  if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY)
    return result;

  RPC_WSTR rpc_string = NULL;
  status = UuidToString(&id, &rpc_string);
  if (status != RPC_S_OK)
    return result;

  // RPC_WSTR is unsigned short*.  wchar_t is a built-in type of Visual C++,
  // so the type cast is necessary.
  result.assign(reinterpret_cast<wchar_t*>(rpc_string));
  RpcStringFree(&rpc_string);

  return result;
}

void StoreKeyLocationInCache(HCRYPTPROV prov) {
  BOOL ok;
  DWORD size = 0;

  // Though it is known the container and provider name, as they are supplied
  // during GenKeyAndSignChallenge, explicitly resolving them via
  // CryptGetProvParam ensures that any defaults (such as provider name being
  // NULL) or any CSP modifications to the container name are properly
  // reflected.

  // Find the container name. Though the MSDN documentation states it will
  // return the exact same value as supplied when the provider was aquired, it
  // also notes the return type will be CHAR, /not/ WCHAR.
  ok = CryptGetProvParam(prov, PP_CONTAINER, NULL, &size, 0);
  if (!ok)
    return;

  std::vector<BYTE> buffer(size);
  ok = CryptGetProvParam(prov, PP_CONTAINER, &buffer[0], &size, 0);
  if (!ok)
    return;

  KeygenHandler::KeyLocation key_location;
  UTF8ToWide(reinterpret_cast<char*>(&buffer[0]), size,
             &key_location.container_name);

  // Get the provider name. This will always resolve, even if NULL (indicating
  // the default provider) was supplied to the CryptAcquireContext.
  size = 0;
  ok = CryptGetProvParam(prov, PP_NAME, NULL, &size, 0);
  if (!ok)
    return;

  buffer.resize(size);
  ok = CryptGetProvParam(prov, PP_NAME, &buffer[0], &size, 0);
  if (!ok)
    return;

  UTF8ToWide(reinterpret_cast<char*>(&buffer[0]), size,
             &key_location.provider_name);

  std::vector<BYTE> public_key_info;
  if (!EncodeSubjectPublicKeyInfo(prov, &public_key_info))
    return;

  KeygenHandler::Cache* cache = KeygenHandler::Cache::GetInstance();
  cache->Insert(std::string(public_key_info.begin(), public_key_info.end()),
                key_location);
}

bool KeygenHandler::KeyLocation::Equals(
    const KeygenHandler::KeyLocation& location) const {
  return container_name == location.container_name &&
         provider_name == location.provider_name;
}

std::string KeygenHandler::GenKeyAndSignChallenge() {
  std::string result;

  bool is_success = true;
  HCRYPTPROV prov = NULL;
  HCRYPTKEY key = NULL;
  DWORD flags = (key_size_in_bits_ << 16) | CRYPT_EXPORTABLE;
  std::string spkac;

  std::wstring new_key_id;

  // TODO(rsleevi): Have the user choose which provider they should use, which
  // needs to be filtered by those providers which can provide the key type
  // requested or the key size requested. This is especially important for
  // generating certificates that will be stored on smart cards.
  const int kMaxAttempts = 5;
  BOOL ok = FALSE;
  for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
    // Per MSDN documentation for CryptAcquireContext, if applications will be
    // creating their own keys, they should ensure unique naming schemes to
    // prevent overlap with any other applications or consumers of CSPs, and
    // *should not* store new keys within the default, NULL key container.
    new_key_id = GetNewKeyContainerId();
    if (new_key_id.empty())
      return result;

    // Only create new key containers, so that existing key containers are not
    // overwritten.
    ok = CryptAcquireContext(&prov, new_key_id.c_str(), NULL, PROV_RSA_FULL,
                             CRYPT_SILENT | CRYPT_NEWKEYSET);

    if (ok || GetLastError() != NTE_BAD_KEYSET)
      break;
  }
  if (!ok) {
    LOG(ERROR) << "Couldn't acquire a CryptoAPI provider context: "
               << GetLastError();
    is_success = false;
    goto failure;
  }

  if (!CryptGenKey(prov, CALG_RSA_KEYX, flags, &key)) {
    LOG(ERROR) << "Couldn't generate an RSA key";
    is_success = false;
    goto failure;
  }

  if (!GetSignedPublicKeyAndChallenge(prov, challenge_, &spkac)) {
    LOG(ERROR) << "Couldn't generate the signed public key and challenge";
    is_success = false;
    goto failure;
  }

  if (!base::Base64Encode(spkac, &result)) {
    LOG(ERROR) << "Couldn't convert signed key into base64";
    is_success = false;
    goto failure;
  }

  StoreKeyLocationInCache(prov);

 failure:
  if (!is_success) {
    LOG(ERROR) << "SSL Keygen failed";
  } else {
    LOG(INFO) << "SSL Key succeeded";
  }
  if (key) {
    // Securely destroys the handle, but leaves the underlying key alone. The
    // key can be obtained again by resolving the key location. If
    // |stores_key_| is false, the underlying key will be destroyed below.
    CryptDestroyKey(key);
  }

  if (prov) {
    CryptReleaseContext(prov, 0);
    prov = NULL;
    if (!stores_key_) {
      // Fully destroys any of the keys that were created and releases prov.
      CryptAcquireContext(&prov, new_key_id.c_str(), NULL, PROV_RSA_FULL,
                          CRYPT_SILENT | CRYPT_DELETEKEYSET);
    }
  }

  return result;
}

}  // namespace net
<|endoftext|>


<commit_before>/*
 * NuoDB Adapter
 */

#include "nuodb/sqlapi/SqlEnvironment.h"
#include "nuodb/sqlapi/SqlConnection.h"
#include "nuodb/sqlapi/SqlDatabaseMetaData.h"
#include "nuodb/sqlapi/SqlStatement.h"
#include "nuodb/sqlapi/SqlExceptions.h"

using nuodb::sqlapi::ErrorCodeException;
using nuodb::sqlapi::SqlConnection;
using nuodb::sqlapi::SqlDatabaseMetaData;
using nuodb::sqlapi::SqlEnvironment;
using nuodb::sqlapi::SqlOption;
using nuodb::sqlapi::SqlOptionArray;
using nuodb::sqlapi::SqlStatement;

#include <ruby.h>

#include <iostream> // TODO temporary

using std::cout; // TODO temporary

//-------------------------------------------------------------------------
// Type definitions

class ClassType
{
public:
  VALUE type;
};

#define DECLARE_CLASS_TYPE(classname) \
  class classname : public ClassType \
  { public: void init(VALUE module); }

DECLARE_CLASS_TYPE(SqlEnvironmentType);
DECLARE_CLASS_TYPE(SqlConnectionType);
DECLARE_CLASS_TYPE(SqlDatabaseMetaDataType);
DECLARE_CLASS_TYPE(SqlStatementType);
DECLARE_CLASS_TYPE(SqlPreparedStatementType);
DECLARE_CLASS_TYPE(SqlResultSetType);
DECLARE_CLASS_TYPE(SqlColumnMetaDataType);

class AllTypes
{
  VALUE module;
public:
  SqlEnvironmentType env;
  SqlConnectionType con;
  SqlDatabaseMetaDataType dbmeta;
  SqlStatementType stmt;
  SqlPreparedStatementType pstmt;
  SqlResultSetType rset;
  SqlColumnMetaDataType colmeta;

  void init();
};

static AllTypes types;

//-------------------------------------------------------------------------

#define WRAPPER_CTOR(WT, RT)			\
  WT(RT& arg) : ref(arg) {}

#define WRAPPER_RELEASE(WT)						\
  static void release(WT* self) {					\
    self->ref.release();						\
    delete self;							\
  }

#define WRAPPER_AS_REF(WT, RT)			\
  static RT& asRef(VALUE value) {		\
    Check_Type(value, T_DATA);			\
    return ((WT*) DATA_PTR(value))->ref;	\
  }

#define WRAPPER_METHODS(WT, RT)			\
  WRAPPER_CTOR(WT, RT)				\
  WRAPPER_RELEASE(WT)				\
  WRAPPER_AS_REF(WT, RT)

class SqlDatabaseMetaDataWrapper
{
  SqlDatabaseMetaData& ref;

public:
  WRAPPER_METHODS(SqlDatabaseMetaDataWrapper, SqlDatabaseMetaData)

  static VALUE getDatabaseVersion(VALUE self)
  {
    return rb_str_new2(asRef(self).getDatabaseVersion());
  }
};

class SqlStatementWrapper
{
  SqlStatement& ref;
public:
  WRAPPER_METHODS(SqlStatementWrapper, SqlStatement)
};

class SqlConnectionWrapper
{
  SqlConnection& ref;

public:
  WRAPPER_METHODS(SqlConnectionWrapper, SqlConnection)

  static VALUE createStatement(VALUE self)
  {
    SqlStatementWrapper* w = new SqlStatementWrapper(asRef(self).createStatement());
    VALUE obj = Data_Wrap_Struct(types.stmt.type, 0, SqlStatementWrapper::release, w);
    rb_obj_call_init(obj, 0, 0);
    return obj;
  }

  static VALUE getMetaData(VALUE self)
  {
    SqlDatabaseMetaDataWrapper* w = new SqlDatabaseMetaDataWrapper(asRef(self).getMetaData());
    VALUE obj = Data_Wrap_Struct(types.dbmeta.type, 0, SqlDatabaseMetaDataWrapper::release, w);
    rb_obj_call_init(obj, 0, 0);
    return obj;
  }
};

class SqlEnvironmentWrapper
{
  SqlEnvironment& ref;

public:
  WRAPPER_METHODS(SqlEnvironmentWrapper, SqlEnvironment)

  static VALUE create(VALUE klass)
  {
    try {
      SqlOptionArray optsArray;
      optsArray.count = 0;

      SqlEnvironmentWrapper* w = new SqlEnvironmentWrapper(SqlEnvironment::createSqlEnvironment(&optsArray));
      VALUE obj = Data_Wrap_Struct(types.env.type, 0, SqlEnvironmentWrapper::release, w);
      rb_obj_call_init(obj, 0, 0);
      return obj;
    } catch (ErrorCodeException & e) {
      rb_raise(rb_eRuntimeError, "failed to create SqlEnvironment: %s", e.what().c_str());
    }
  }

  static VALUE createSqlConnection(VALUE self, VALUE database, VALUE username, VALUE password)
  {
    try {
      SqlOption options[3];

      options[0].option = "database";
      options[0].extra = (void*) StringValuePtr(database);

      options[1].option = "user";
      options[1].extra = (void*) StringValuePtr(username);

      options[2].option = "password";
      options[2].extra = (void*) StringValuePtr(password);

      SqlOptionArray optsArray;
      optsArray.count = 3;
      optsArray.array = options;

      SqlConnectionWrapper* con = new SqlConnectionWrapper(asRef(self).createSqlConnection(&optsArray));
      VALUE obj = Data_Wrap_Struct(types.con.type, 0, SqlConnectionWrapper::release, con);
      rb_obj_call_init(obj, 0, 0);
      return obj;
    } catch (ErrorCodeException & e) {
      rb_raise(rb_eRuntimeError, "failed to create SqlConnection: %s", e.what().c_str());
    }
  }
};

//-------------------------------------------------------------------------
// Initialization

#define DEF_CLASS(name) \
  type = rb_define_class_under(module, name, rb_cObject)

#define DEF_SINGLETON(name, func, count) \
  rb_define_singleton_method(type, name, RUBY_METHOD_FUNC(func), count)

#define DEF_METHOD(name, func, count) \
  rb_define_method(type, name, RUBY_METHOD_FUNC(func), count)

void SqlEnvironmentType::init(VALUE module)
{
  DEF_CLASS("SqlEnvironment");
  DEF_SINGLETON("createSqlEnvironment", SqlEnvironmentWrapper::create, 0);
  DEF_METHOD("createSqlConnection", SqlEnvironmentWrapper::createSqlConnection, 3);
}

void SqlConnectionType::init(VALUE module)
{
  DEF_CLASS("SqlConnection");
  DEF_METHOD("createStatement", SqlConnectionWrapper::createStatement, 0);
  // TODO SqlStatement & createStatement();
  // TODO SqlPreparedStatement & createPreparedStatement(char const * sql);
  // TODO void setAutoCommit(bool autoCommit = true);
  // TODO bool hasAutoCommit() const;
  // TODO void commit();
  // TODO void rollback();
  DEF_METHOD("getMetaData", SqlConnectionWrapper::getMetaData, 0);
}

void SqlDatabaseMetaDataType::init(VALUE module)
{
  DEF_CLASS("SqlDatabaseMetaData");
  DEF_METHOD("getDatabaseVersion", SqlDatabaseMetaDataWrapper::getDatabaseVersion, 0);
}

void SqlStatementType::init(VALUE module)
{
  DEF_CLASS("SqlStatement");
  // TODO void execute(char const * sql);
  // TODO SqlResultSet & executeQuery(char const * sql);
  // TODO void release();
}

void SqlPreparedStatementType::init(VALUE module)
{
  DEF_CLASS("SqlPreparedStatement");
  // TODO void setInteger(size_t index, int32_t value);
  // TODO void setDouble(size_t index, double value);
  // TODO void setString(size_t index, char const * value);
  // TODO void execute();
  // TODO SqlResultSet & executeQuery();
  // TODO void release();
}

void SqlResultSetType::init(VALUE module)
{
  DEF_CLASS("SqlResultSet");
  // TODO bool next();
  // TODO size_t getColumnCount() const;
  // TODO SqlColumnMetaData & getMetaData(size_t column) const;
  // TODO int32_t getInteger(size_t column) const;
  // TODO double getDouble(size_t column) const;
  // TODO char const * getString(size_t column) const;
  // TODO SqlDate const * getDate(size_t column) const;
  // TODO void release();
}

void SqlColumnMetaDataType::init(VALUE module)
{
  DEF_CLASS("SqlColumnMetaData");
  // TODO char const * getColumnName() const;
  // TODO SqlType getType() const;
  // TODO void release();
}

void AllTypes::init()
{
  module = rb_define_module("Nuodb");
  env.init(module);
  con.init(module);
  dbmeta.init(module);
  stmt.init(module);
  pstmt.init(module);
  rset.init(module);
  colmeta.init(module);
}

extern "C"
void Init_nuodb(void)
{
  types.init();
}

//-------------------------------------------------------------------------
<commit_msg>Update to latest NuoSqlApi. Disable release because of ordering bug.<commit_after>/*
 * NuoDB Adapter
 */

#include "nuodb/sqlapi/SqlEnvironment.h"
#include "nuodb/sqlapi/SqlConnection.h"
#include "nuodb/sqlapi/SqlDatabaseMetaData.h"
#include "nuodb/sqlapi/SqlStatement.h"
#include "nuodb/sqlapi/SqlExceptions.h"

using nuodb::sqlapi::ErrorCodeException;
using nuodb::sqlapi::SqlConnection;
using nuodb::sqlapi::SqlDatabaseMetaData;
using nuodb::sqlapi::SqlEnvironment;
using nuodb::sqlapi::SqlOption;
using nuodb::sqlapi::SqlOptionArray;
using nuodb::sqlapi::SqlStatement;

#include <ruby.h>

#include <iostream> // TODO temporary

using std::cout; // TODO temporary

//-------------------------------------------------------------------------
// Type definitions

class ClassType
{
public:
  VALUE type;
};

#define DECLARE_CLASS_TYPE(classname) \
  class classname : public ClassType \
  { public: void init(VALUE module); }

DECLARE_CLASS_TYPE(SqlEnvironmentType);
DECLARE_CLASS_TYPE(SqlConnectionType);
DECLARE_CLASS_TYPE(SqlDatabaseMetaDataType);
DECLARE_CLASS_TYPE(SqlStatementType);
DECLARE_CLASS_TYPE(SqlPreparedStatementType);
DECLARE_CLASS_TYPE(SqlResultSetType);
DECLARE_CLASS_TYPE(SqlColumnMetaDataType);

class AllTypes
{
  VALUE module;
public:
  SqlEnvironmentType env;
  SqlConnectionType con;
  SqlDatabaseMetaDataType dbmeta;
  SqlStatementType stmt;
  SqlPreparedStatementType pstmt;
  SqlResultSetType rset;
  SqlColumnMetaDataType colmeta;

  void init();
};

static AllTypes types;

//-------------------------------------------------------------------------

#define WRAPPER_CTOR(WT, RT)			\
  WT(RT& arg) : ref(arg) {}

#define WRAPPER_RELEASE(WT)					\
  static void release(WT* self) {				\
    cout << "DISABLED release " << #WT << " " << self << "\n";	\
    /* self->ref.release(); */					\
    delete self;						\
  }

#define WRAPPER_AS_REF(WT, RT)			\
  static RT& asRef(VALUE value) {		\
    Check_Type(value, T_DATA);			\
    return ((WT*) DATA_PTR(value))->ref;	\
  }

#define WRAPPER_METHODS(WT, RT)			\
  WRAPPER_CTOR(WT, RT)				\
  WRAPPER_RELEASE(WT)				\
  WRAPPER_AS_REF(WT, RT)

class SqlDatabaseMetaDataWrapper
{
  SqlDatabaseMetaData& ref;

public:
  WRAPPER_METHODS(SqlDatabaseMetaDataWrapper, SqlDatabaseMetaData)

  static VALUE getDatabaseVersion(VALUE self)
  {
    return rb_str_new2(asRef(self).getDatabaseVersion());
  }
};

class SqlStatementWrapper
{
  SqlStatement& ref;
public:
  WRAPPER_METHODS(SqlStatementWrapper, SqlStatement)
};

class SqlConnectionWrapper
{
  SqlConnection& ref;

public:
  WRAPPER_METHODS(SqlConnectionWrapper, SqlConnection)

  static VALUE createStatement(VALUE self)
  {
    SqlStatementWrapper* w = new SqlStatementWrapper(asRef(self).createStatement());
    VALUE obj = Data_Wrap_Struct(types.stmt.type, 0, SqlStatementWrapper::release, w);
    rb_obj_call_init(obj, 0, 0);
    return obj;
  }

  static VALUE getMetaData(VALUE self)
  {
    SqlDatabaseMetaDataWrapper* w = new SqlDatabaseMetaDataWrapper(asRef(self).getMetaData());
    VALUE obj = Data_Wrap_Struct(types.dbmeta.type, 0, SqlDatabaseMetaDataWrapper::release, w);
    rb_obj_call_init(obj, 0, 0);
    return obj;
  }
};

class SqlEnvironmentWrapper
{
  SqlEnvironment& ref;

public:
  WRAPPER_METHODS(SqlEnvironmentWrapper, SqlEnvironment)

  static VALUE create(VALUE klass)
  {
    try {
      SqlOptionArray optsArray;
      optsArray.count = 0;

      SqlEnvironmentWrapper* w = new SqlEnvironmentWrapper(SqlEnvironment::createSqlEnvironment(&optsArray));
      VALUE obj = Data_Wrap_Struct(types.env.type, 0, SqlEnvironmentWrapper::release, w);
      rb_obj_call_init(obj, 0, 0);
      return obj;
    } catch (ErrorCodeException & e) {
      rb_raise(rb_eRuntimeError, "failed to create SqlEnvironment: %s", e.what());
    }
  }

  static VALUE createSqlConnection(VALUE self, VALUE database, VALUE username, VALUE password)
  {
    try {
      SqlOption options[3];

      options[0].option = "database";
      options[0].extra = (void*) StringValuePtr(database);

      options[1].option = "user";
      options[1].extra = (void*) StringValuePtr(username);

      options[2].option = "password";
      options[2].extra = (void*) StringValuePtr(password);

      SqlOptionArray optsArray;
      optsArray.count = 3;
      optsArray.array = options;

      SqlConnectionWrapper* con = new SqlConnectionWrapper(asRef(self).createSqlConnection(&optsArray));
      VALUE obj = Data_Wrap_Struct(types.con.type, 0, SqlConnectionWrapper::release, con);
      rb_obj_call_init(obj, 0, 0);
      return obj;
    } catch (ErrorCodeException & e) {
      rb_raise(rb_eRuntimeError, "failed to create SqlConnection: %s", e.what());
    }
  }
};

//-------------------------------------------------------------------------
// Initialization

#define DEF_CLASS(name) \
  type = rb_define_class_under(module, name, rb_cObject)

#define DEF_SINGLETON(name, func, count) \
  rb_define_singleton_method(type, name, RUBY_METHOD_FUNC(func), count)

#define DEF_METHOD(name, func, count) \
  rb_define_method(type, name, RUBY_METHOD_FUNC(func), count)

void SqlEnvironmentType::init(VALUE module)
{
  DEF_CLASS("SqlEnvironment");
  DEF_SINGLETON("createSqlEnvironment", SqlEnvironmentWrapper::create, 0);
  DEF_METHOD("createSqlConnection", SqlEnvironmentWrapper::createSqlConnection, 3);
}

void SqlConnectionType::init(VALUE module)
{
  DEF_CLASS("SqlConnection");
  DEF_METHOD("createStatement", SqlConnectionWrapper::createStatement, 0);
  // TODO SqlStatement & createStatement();
  // TODO SqlPreparedStatement & createPreparedStatement(char const * sql);
  // TODO void setAutoCommit(bool autoCommit = true);
  // TODO bool hasAutoCommit() const;
  // TODO void commit();
  // TODO void rollback();
  DEF_METHOD("getMetaData", SqlConnectionWrapper::getMetaData, 0);
}

void SqlDatabaseMetaDataType::init(VALUE module)
{
  DEF_CLASS("SqlDatabaseMetaData");
  DEF_METHOD("getDatabaseVersion", SqlDatabaseMetaDataWrapper::getDatabaseVersion, 0);
}

void SqlStatementType::init(VALUE module)
{
  DEF_CLASS("SqlStatement");
  // TODO void execute(char const * sql);
  // TODO SqlResultSet & executeQuery(char const * sql);
  // TODO void release();
}

void SqlPreparedStatementType::init(VALUE module)
{
  DEF_CLASS("SqlPreparedStatement");
  // TODO void setInteger(size_t index, int32_t value);
  // TODO void setDouble(size_t index, double value);
  // TODO void setString(size_t index, char const * value);
  // TODO void execute();
  // TODO SqlResultSet & executeQuery();
  // TODO void release();
}

void SqlResultSetType::init(VALUE module)
{
  DEF_CLASS("SqlResultSet");
  // TODO bool next();
  // TODO size_t getColumnCount() const;
  // TODO SqlColumnMetaData & getMetaData(size_t column) const;
  // TODO int32_t getInteger(size_t column) const;
  // TODO double getDouble(size_t column) const;
  // TODO char const * getString(size_t column) const;
  // TODO SqlDate const * getDate(size_t column) const;
  // TODO void release();
}

void SqlColumnMetaDataType::init(VALUE module)
{
  DEF_CLASS("SqlColumnMetaData");
  // TODO char const * getColumnName() const;
  // TODO SqlType getType() const;
  // TODO void release();
}

void AllTypes::init()
{
  module = rb_define_module("Nuodb");
  env.init(module);
  con.init(module);
  dbmeta.init(module);
  stmt.init(module);
  pstmt.init(module);
  rset.init(module);
  colmeta.init(module);
}

extern "C"
void Init_nuodb(void)
{
  types.init();
}

//-------------------------------------------------------------------------
<|endoftext|>